|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Добрый день, столкнулся со следующей задачей. Есть таблица tPart в которой хранятся партии изготавливаемых изделий, для изготовления партии изделий используются другие партии изделий, какая партия в какую входит описано с помощью таблицы tLink. Мне нужно построить дерево вхождений партий так что бы в корневую партию с минимальным номером входили листовые партии так же с минимальным номером (id). Пример данных: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35.
Желаемый результат: idRoot idParent idChild fQty1 1 4 11 4 5 12 3 4 12 4 6 12 6 9 13 3 4 13 4 6 13 6 10 1 Вопрос можно ли данную задачу решить на SQL (на PL/SQL у меня решение есть, но мне оно не совсем нравится) без использования model? ... |
|||
:
Нравится:
Не нравится:
|
|||
06.09.2019, 19:01 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123, Результат выглядит несколько странно. Почему (4,5) относится к корню 1, а (4,6) к корню 2? Почему (4,6) фигурирует в результате дважды? Почему у (4,6) fQty в результате стало 1 а не 2 и нужно ли оно вообще или это мусор не имеющий отношения к постановке? Нужна ли таблица tPart для решения или это просто список айдишников? И т. д. Код: plsql 1. 2. 3. 4. 5. 6.
Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
... |
|||
:
Нравится:
Не нравится:
|
|||
06.09.2019, 19:21 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Кобанчегln123, Результат выглядит несколько странно. Почему (4,5) относится к корню 1, а (4,6) к корню 2? Потому что по условию задачи "в корневую партию с минимальным номером входили листовые партии так же с минимальным номером" таким образом получается что 1<2 поэтому его нужно обеспечивать в первую очередь 5<9<10 (5,9,10 это в данном случае листья) поэтому 5-тая партия входит в 1-ю а 9-я во вторую (через 6-ю) Почему (4,6) фигурирует в результате дважды? Потому что в одном случае это часть пути от 2 до 9, а в другом от 3 до 10. Почему у (4,6) fQty в результате стало 1 а не 2 и нужно ли оно вообще или это мусор не имеющий отношения к постановке? Нужна ли таблица tPart для решения или это просто список айдишников? fQty - это количество, в tPart - это количество в партии, а в tLink количество в связи. Таким образом, данные нужно понимать так, партия 4 состоит из 3-х изделий каждое из изделий может быть изготовлено или из 1 шт партии 5 или из 1 шт партии 6. (3 (партия 4)= 1 ( партия 5) + 2 (партия 6). Исходя из вышеизложенного для изготовления партии 2 нужна 1 шт партии 4, а для изготовления 1 шт партии 4 нужно либо 1 шт партии 5, но она уже занята под партию 1 либо 1 шт партии 6, в результате получаем искомую запись idRoot = 2, idParent=4, idChild = 6, fQty=1. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.09.2019, 10:30 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123Вопрос можно ли данную задачу решить на SQL (на PL/SQL у меня решение есть, но мне оно не совсем нравится) без использования model?Можно решить с использованием временной таблицы и весьма симпатично. ... |
|||
:
Нравится:
Не нравится:
|
|||
10.09.2019, 08:34 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Кобанчег, Ну вы прям заинтриговали :), я сейчас делаю то же с помощью временной таблицы, но свое решение я бы симпатичным не назвал. Выглядит приблизительно так: 1. В таблицу закидываем все возможные пути от корня до листьев (получаются простым иерархическим запросом при спуске вниз по дереву) 2. Идем циклом (PL/SQL) по записям отсортированным по idRoot, id листа Если у данной записи есть в пути развилки/альтернативы то пересчитываем количество в альтернативных ветках, если количество получилось равным 0, то удаляем такую запись. Помечаем текущую запись как обработанную, переходим на следующую по порядку. Когда пройдем все записи то в таблице останется искомый результат. А у вас какой вариант был? ... |
|||
:
Нравится:
Не нравится:
|
|||
10.09.2019, 21:05 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123, У тебя чудесным образом строятся не деревья, а просто списки от каждого корня до некоторого листа. При этом на каждом шаге выбирается ребро с минимальным ребенком. Если fQty определяет сколько раз может быть использовано ребро, то можно как-то так: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72.
Предполагается, что в выделенной строке в твоём результате опечатка. PS. Case sensitive Java style identifiers в Оракле это мерзко. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.09.2019, 00:03 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Кобанчегln123, У тебя чудесным образом строятся не деревья, а просто списки от каждого корня до некоторого листа. Пример несколько упрощен, но да же в этой упрощенной структуре при других данных легко можно получить дерево. Например: Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29.
Для первой партии даст результат idRoot idParent idChild fQty1 1 4 21 4 5 11 4 6 11 6 9 1 При этом на каждом шаге выбирается ребро с минимальным ребенком. Нет гарантии что выбор минимального ребенка на промежуточном шаге приведет в конечном счете к минимальному листу. Хотя наверное данную проблему можно решить предварительной подготовкой данных, пройдясь от листьев вверх и выполнив перенумерацию промежуточных узлов. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.09.2019, 21:12 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123Нет гарантии что выбор минимального ребенка на промежуточном шаге приведет в конечном счете к минимальному листу.Иными словами можно стоить дерево из каждого корня и потом отбирать листья (и все промежуточные ребра) покуда qty в каждом узле пути не исчерпано? Глубина не важна? То есть, если в дереве ниже везде qty=1, то отбирается только ребро 5-0, потому что 0 - минимальный лист? Код: plaintext 1. 2. 3.
... |
|||
:
Нравится:
Не нравится:
|
|||
12.09.2019, 13:34 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Кобанчег, Да, все верно. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.09.2019, 20:54 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Кстати мое решение данной задачи выглядит вот так Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118.
Но во первых это не красиво :), а во вторых не очень быстро. На тестовом примере с количеством связей порядка 43 тыс., мой код работает 15-20 минут (с учетом наличия правильных индексов на промежуточной таблице). Конечно его можно оптимизировать обработав то что все эти сложности нужны только если есть ветвления, убрать delete из цикла и т.п., но когда писал на форму я очень надеялся что кто нибудь предложит какой-нибудь альтернативный подход. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.09.2019, 17:54 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123я очень надеялся что кто нибудь предложит какой-нибудь альтернативный подход.Т.е. премию надеялся пропить самолично, наглая сволочь? ... |
|||
:
Нравится:
Не нравится:
|
|||
13.09.2019, 19:37 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123, Какое число корней? По сути необходимо для каждого корня делать следующее: 1) строим дерево 2) отсекаем не подходящие пути 3) отмечаем использованные ребра Все три операции быстрые и в итоге должно выполняться за секунды. У тебя операторов поболее и если честно мне в тот код вникать лень. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.09.2019, 19:43 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Elic, Не стоит всех равнять по себе, премию я уже две недели назад как пропил :) ... |
|||
:
Нравится:
Не нравится:
|
|||
14.09.2019, 10:34 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
Кобанчегln123, Какое число корней? На ограниченной выборке было 5-8, на полной меньше 100. По сути необходимо для каждого корня делать следующее: 1) строим дерево Ок, это я сделал. 2) отсекаем не подходящие пути 3) отмечаем использованные ребра Не вижу смысла разбивать на два шага, т.к. мы сначала выбираем подходящий путь и соответственно можем пометить использованные ребра, а затем уже можем отсекать не подходящие пути. Но тут то и кроется проблема, что бы просто отметить ребра как использованные нужно подняться вверх от каждого еще не отсеченного листа, что бы отсечь не подходящие пути нужно спуститься вниз от каждой развилки пересчитав доступное количество. Как это сделать одним оператором я не придумал :( Все три операции быстрые и в итоге должно выполняться за секунды. Построение деревьев со всеми возможными путями это действительно секунды, все основное время уходит на пересчеты по мере подбора правильных путей. У тебя операторов поболее и если честно мне в тот код вникать лень. Код плохой (это просто упрощещенная иллюстрация возможного решения) и особого смысла в него вникать нет. Мне было бы интересно увидеть как такую задачу можно решить только на SQL, либо узнать что для ее решения есть какой нибудь известный алгоритм (по идеи задача довольно типовая и общая). В любом случае спасибо за диалог. ... |
|||
:
Нравится:
Не нравится:
|
|||
14.09.2019, 11:14 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
ln123Пример несколько упрощенНадо было, кстати, дальше доупрощать и выкинуть таблицу tPart. Её необходимость для решения так и осталась неясна. ln123Не вижу смысла разбивать на два шага, т.к. мы сначала выбираем подходящий путь и соответственно можем пометить использованные ребра, а затем уже можем отсекать не подходящие пути.Под "3) отмечаем использованные ребра" подразумевался merge statement в моём примере, то есть обновляется исходный граф чтоб при построении дерева из следующего корня в нём было учтено что уже использовано. ln123Но тут то и кроется проблема, что бы просто отметить ребра как использованные нужно подняться вверх от каждого еще не отсеченного листа, что бы отсечь не подходящие пути нужно спуститься вниз от каждой развилки пересчитав доступное количество. Как это сделать одним оператором я не придумал :(Если б ты изначально отбросил ненужное и спросил как решить это одним оператором, возможно, отвечающих было бы больше. Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.
Итого (если есть острое желание можно всю логику засунуть в merge, но понадобится дополнительное теложвижение, чтоб построить итоговый результат): Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44.
Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
... |
|||
:
Нравится:
Не нравится:
|
|||
18.09.2019, 15:01 |
|
Разбить направленный граф на несколько деревьев
|
|||
---|---|---|---|
#18+
С аналитикой, конечно, оно в общем случае не вернет корректный результат... Отсечение можно сделать с еще одной временной таблицей Код: plsql 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74.
... |
|||
:
Нравится:
Не нравится:
|
|||
18.09.2019, 19:43 |
|
|
start [/forum/topic.php?fid=52&msg=39861733&tid=1882064]: |
0ms |
get settings: |
11ms |
get forum list: |
14ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
40ms |
get topic data: |
12ms |
get forum data: |
2ms |
get page messages: |
58ms |
get tp. blocked users: |
2ms |
others: | 263ms |
total: | 410ms |
0 / 0 |