|
Задачка про рюкзак и рекурсию | ☑ | ||
---|---|---|---|---|
0
web_profiler
10.06.15
✎
11:57
|
Понимаю, что тема избита много раз.
Но очень нуждаюсь в помощи при решении задачки про рюкзак. Постановка задачи: Есть таблица значений с строками и колонками Индекс Кол-во Коэф Метраж 0 4 50 200 1 1 50 50 2 2 50 100 3 3 45 135 4 1 50 50 5 3 60 180 6 4 40 160 7 3 35 105 Требуется: при вводе определенной суммы метров правильно выбрать из ТЗ суммы метражей. Разрешается разбивать строки на подстроки. К примеру пользователю необходимо получить 125 метров, система должна проанализировать ТЗ и понять, что 125 метров мы можем получить путем разбивки строки с Индексом 3 на две подстроки и строки 7 также на две построки след.вида: Исходные нужные для разбивки строки: 3 3 45 135 7 3 35 105 Должно получиться: 3 2 45 90 7 2 35 70 8 1 45 45 (добавленная строка разбивкой строки 3) 9 1 35 35 (добавленная строка разбивкой строки 7) Так вот задача: как понять что мне для разбивки необходимы 2 строки (3,7) и их требуется разбить по подстрокам. Пробывал использовать рекурсию из задачки про рюкзаки из которой получается найти необходимую сумму элементов и к тому же нашел решение только с одномерным массивом. Перем спРяд, спРез, спЛучшийРез; Перем мин_ост; Процедура рекурсия(нач, ост) Если ост < мин_ост Тогда спЛучшийРез = спРез.ВыгрузитьЗначения(); мин_ост = ост; КонецЕсли; для н = нач по спРяд.Количество()-1 Цикл дли = спРяд[н].Значение; Если дли > ост Тогда продолжить; КонецЕсли; спРез.Добавить(дли); рекурсия(н+1, ост-дли); спРез.Удалить(спРез.Количество()-1); КонецЦикла; КонецПроцедуры Функция Поиск(спЗначения, ИскомаяДлина) спРез = новый СписокЗначений; спЛучшийРез = Новый СписокЗначений; спРяд = спЗначения; мин_ост = ИскомаяДлина; рекурсия(0, ИскомаяДлина); возврат спЛучшийРез; КонецФункции Процедура ОсновныеДействияФормыОсновныеДействияФормыВыполнить(Кнопка) СпЗнач = Новый СписокЗначений; СпЗнач.Добавить(200); СпЗнач.Добавить(50); СпЗнач.Добавить(100); СпЗнач.Добавить(135); СпЗнач.Добавить(50); СпЗнач.Добавить(180); СпЗнач.Добавить(160); СпЗнач.Добавить(105); СпЗнач.СортироватьПоЗначению(НаправлениеСортировки.Убыв); Рез = Поиск(СпЗнач, ПолеВвода1); Для Каждого Стр Из Рез Цикл Сообщить(Стр); КонецЦикла; КонецПроцедуры Читал википедию про методы решения задачки с рюкзаком, но как эти все формулы воплотить в 1С – ума не приложу. Совсем запутался, помогите, пожалуйста. |
|||
42
Злопчинский
10.06.15
✎
12:56
|
фиг его знает как там с оптимальностью, но туеву хучу артикулов (более 100) с разными объемами напихивала рекурсией на стандартные палеты секунды за 3-5
|
|||
43
Злопчинский
10.06.15
✎
12:58
|
(41) реальные задачи всегда интересно
а если еще карту нарисовать и показывать выбранные разбиения.. ааа... экстаззз... |
|||
44
web_profiler
10.06.15
✎
14:16
|
(32) сообразил такое движение, и получил на выходе картинку, которая не очень устраивает:
Получается, что произошла выборка из 3-х разбитых строк, хотя эту задачку можно решить разбивая две строки http://iscr.ru/1433934951/ |
|||
45
web_profiler
10.06.15
✎
14:18
|
можно наверно попробывать бить не по полному колву строк, а отнимать по одной, к примеру
Колво Коэф Метры 3 35 105 На первой итерации получить 2 строки Колво Коэф Метры 1 35 35 2 35 70 И так со всеми строками |
|||
46
web_profiler
10.06.15
✎
14:20
|
хотя нет строка с метражем 40 тоже разобьется и получу точно такой же результат, едрит мадрид, что же это такое, а?
|
|||
47
web_profiler
10.06.15
✎
14:22
|
по каким условиям мне определить, что мне необходимо 2 строки? третья и седьмая
|
|||
48
Михаил Козлов
10.06.15
✎
14:31
|
1. Задача может не иметь решения: например, требуется нечетное количество метров, а все возможные "куски" четные.
2. На "классическом" рюкзаке (а эта задача несколько "сложнее") много топтались. Мне не известно полиномиальных методов решения рюкзака на равенство (не максимизация веса, а именно равенство. Формально задача состоит в ответе на вопрос, имеет ли решение уравнение СУММА(Ai*Xi)=B в булевых переменных. Т.е. в общем случае ответить на вопрос: "Возможно ли подобрать нужное число метров" не полным перебором не представляется возможным. 3. Для практического решения нужно искать какие-то разумные подходы. Например, попробовать жадным алгоритмом решить задачу, разбив вес "рулоны" на соответствующие куски, а потом попытаться минимизировать число кусков. Но к вечеру врядли получится найти (и проверить) такой подход. |
|||
49
Михаил Козлов
10.06.15
✎
14:33
|
(48)+. Извините, описался (ударение на 3-ем слоге): не "вес "рулоны", а все "рулоны".
|
|||
50
web_profiler
10.06.15
✎
14:34
|
(48) не кратно коэфициентам - досвидос - сообщение пользователю "Ввести сумму кратную..." и вывод всех кратностей
|
|||
51
Михаил Козлов
10.06.15
✎
14:37
|
(50) Например, нужно 8 м. Возможны куски в 3 и 5 м. 8 не кратно ни 3, ни 5. Какой досвидос?
|
|||
52
web_profiler
10.06.15
✎
14:38
|
(51), согласен, но в таком случае наше условие то выполнилось 3+5 = 8
|
|||
53
web_profiler
10.06.15
✎
14:39
|
как бы сейчас рабочий код, но корявый. Разбивается 3 строки, хотя могло бы разбиться 2
|
|||
54
web_profiler
10.06.15
✎
14:39
|
Перем спРяд, спРез, спЛучшийРез;
Перем мин_ост; Процедура рекурсия(нач, ост) Если ост < мин_ост Тогда спЛучшийРез = спРез.Скопировать(); мин_ост = ост; КонецЕсли; для н = нач по спРяд.Количество()-1 Цикл дли = спРяд[н].Метры; Если дли > ост Тогда продолжить; КонецЕсли; ЗаполнитьЗначенияСвойств(спРез.добавить(), спРяд[н]); //спРез.Добавить(дли); рекурсия(н+1, ост-дли); спРез.Удалить(спРез.Количество()-1); КонецЦикла; КонецПроцедуры Функция Поиск(спЗначения, ИскомаяДлина) спРез = новый ТаблицаЗначений; спРез = спЗначения.СкопироватьКолонки(); спЛучшийРез = Новый ТаблицаЗначений; спЛучшийРез = спЗначения.СкопироватьКолонки(); спРяд = спЗначения.скопировать(); мин_ост = ИскомаяДлина; рекурсия(0, ИскомаяДлина); возврат спЛучшийРез; КонецФункции Процедура ОсновныеДействияФормыОсновныеДействияФормыВыполнить(Кнопка) СпЗнач = Новый ТаблицаЗначений; СпЗнач.Колонки.Добавить("НомСтроки",,"НомСтроки"); СпЗнач.Колонки.Добавить("Колво",,"Колво"); СпЗнач.Колонки.Добавить("Коэф",,"Коэф"); СпЗнач.Колонки.Добавить("Метры",,"Метры"); СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 0; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 200; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 1; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 2; СтрСпЗнач.Колво = 2; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 100; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 3; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 45; СтрСпЗнач.Метры = 135; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 4; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 5; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 60; СтрСпЗнач.Метры = 180; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 6; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 40; СтрСпЗнач.Метры = 160; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 7; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 35; СтрСпЗнач.Метры = 105; // СпЗнач.СортироватьПоЗначению(НаправлениеСортировки.Убыв); Рез = Поиск(СпЗнач, ПолеВвода1); ОбщСумма = 0; Для Каждого Стр Из Рез Цикл ОбщСумма = ОбщСумма + Стр.Метры; //Сообщить(Стр.Метры); КонецЦикла; Если Стр.Метры <> ПолеВвода1 Тогда //будем разбивать все строки и все по новой в рекурсии спРяд.очистить(); спРез.Очистить(); спЛучшийРез.Очистить(); КопияСтрСпЗнач = СпЗнач.СкопироватьКолонки(); Для Каждого Стр Из СпЗнач Цикл Если Стр.колво > 1 Тогда Для щ = 1 По Стр.колво цикл Строчечка = КопияСтрСпЗнач.Добавить(); Строчечка.колво = 1; Строчечка.НомСтроки = Стр.НомСтроки; Строчечка.Коэф = Стр.Коэф; Строчечка.Метры = Строчечка.Коэф; КонецЦикла; Иначе ЗаполнитьЗначенияСвойств(КопияСтрСпЗнач.Добавить(), Стр); КонецЕсли; КонецЦикла; Рез = Поиск(КопияСтрСпЗнач, ПолеВвода1); КонецЕсли; КонецПроцедуры |
|||
55
Ildarovich
10.06.15
✎
14:40
|
Вот решение:
У обработки реквизит "Требуется", табличная часть "Метраж" с реквизитами "Имеется" (ваше количество), Коэффициент (коэф), НужноВзять (собственно туда ответ помещается). Метраж мне не понадобился, Индекс заменен номером строки, из него нужно один вычесть. Вот код модуля формы: Процедура КнопкаСформироватьНажатие(Кнопка) РабочаяТаблица = Метраж.Выгрузить(); РабочаяТаблица.ЗаполнитьЗначения(Метраж.Итог("Имеется"), "НужноВзять"); Метраж.ЗагрузитьКолонку(РабочаяТаблица.ВыгрузитьКолонку("НужноВзять"), "НужноВзять"); РабочаяТаблица.ЗаполнитьЗначения(0, "НужноВзять"); РабочаяТаблица.Сортировать("Коэффициент Убыв"); ПодборКусков(РабочаяТаблица, РабочаяТаблица.Количество() - 1, Требуется) КонецПроцедуры Вот код модуля обработки Процедура ПодборКусков(РабочаяТаблица, ВГраница, ЗаданнаяСумма, УжеВзяли = 0) Экспорт Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ УжеВзяли >= Метраж.Итог("НужноВзять") Тогда //промах Возврат ИначеЕсли ЗаданнаяСумма = 0 Тогда //фиксируем решение Ответ = РабочаяТаблица.Скопировать( , "НомерСтроки, НужноВзять"); Ответ.Сортировать("НомерСтроки"); Метраж.ЗагрузитьКолонку(Ответ.ВыгрузитьКолонку("НужноВзять"), "НужноВзять") КонецЕсли; Курсор = РабочаяТаблица[ВГраница]; Для НужноВзять = 0 По Курсор.Имеется Цикл Курсор.НужноВзять = НужноВзять; ПодборКусков(РабочаяТаблица, ВГраница - 1, ЗаданнаяСумма - НужноВзять * Курсор.Коэффициент, УжеВзяли + НужноВзять); Курсор.НужноВзять = 0 КонецЦикла КонецПроцедуры // ПодборКусков() Могу прислать саму обработку, сообщите куда. |
|||
56
web_profiler
10.06.15
✎
14:40
|
не ругайте за переменные, надо отладить рабочий алгоритм
|
|||
57
web_profiler
10.06.15
✎
14:41
|
(55) [email protected]
|
|||
58
Михаил Козлов
10.06.15
✎
14:48
|
(54), (55) Чудаки вы: в общем случае без перебора нельзя ответить на вопрос, можно ли обеспечить нужную длину.
|
|||
59
web_profiler
10.06.15
✎
14:51
|
(58) подскажите как правильно
|
|||
60
web_profiler
10.06.15
✎
14:55
|
(58) а что вы на это скажете?
http://iscr.ru/1433937290/ |
|||
61
web_profiler
10.06.15
✎
15:03
|
(55) В идеале, конечно получить
http://iscr.ru/1433937748/ зачем бить строку, если можно набрать 150 из 195 посредством суммирования 3-ей и 5-ой строки |
|||
62
Михаил Козлов
10.06.15
✎
15:04
|
(60) На это скажу, что в частном случае (а, возможно, и в подавляющем числе случаев) алгоритм может давать решение.
И имеет смысл его использовать, только отдавая себе отчет, что он сработает не всегда. Например, жадный алгоритм в рюкзаке статистически дает оптимальное решение. Из-за того, что задача коммивояжера не имеет эффективного алгоритма, вовсе не означает, что практические задачи (например, при трассировке плат) не решают. (59) Правильно (и эффективно) нет, поэтому не скажу. |
|||
63
web_profiler
10.06.15
✎
15:17
|
(55) это по сути тоже, что и у меня в (44), только по двум строкам. Проходит разбиение той строки, которую можно не разбивать а взять из строк 3 и 5
|
|||
64
Ildarovich
10.06.15
✎
15:26
|
(62) Это конкретная задача целочисленного программирования. Она может и не иметь решения. Алгоритм это покажет.
Если не поленитесь внимательно посмотреть решение (55), то увидите, что решение выполняется способом сокращенного перебора (то есть методом ветвей и границ). Для НужноВзять = 0 По Курсор.Имеется Цикл //это ветвление Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ УжеВзяли >= Метраж.Итог("НужноВзять") Тогда //это проверка границ А то, что вы говорите, относится к задачам более высокой размерности, когда переборные методы уже не работают. В общем-то я призываю не повторять заученные когда-то фразы, а всеръез решать каждую задачу и рассуждать конкретно по делу. |
|||
65
web_profiler
10.06.15
✎
15:29
|
(64) ок по делу, как решить то что описал в (63)?
|
|||
66
Ildarovich
10.06.15
✎
15:32
|
(63) вы сказали, что критерий - минимальное число кусков, а сейчас добавляете критерий минимизации числа строк, из которых берутся куски. Потребуется, кроме "УжеВзяли", которое число кусков отражает, ввести параметр "ИспользовалиСтрок" и наращивать его при ненулевом "НужноВзять". То есть отчет можно еще доработать. Это не сложно, но я на сегодня, наверное, все.
А вообще, конечно, задаче сильно не хватает "фактуры". Написали бы, если не военная тайна. |
|||
67
web_profiler
10.06.15
✎
15:35
|
(66) помогите сейчас, плиз, отблагодарю
"фактура" не нужна пока - надо отработать именно этот механизм |
|||
68
web_profiler
10.06.15
✎
15:38
|
а мож просто пробегая по имеющейся таблице - сравнивать на равность колво и нужно взять. и если не равно, то пробывать найти где равно с соответствующим коэффициентом
|
|||
69
Михаил Козлов
10.06.15
✎
15:44
|
(64) Текст в (55) не смотрел. Я хотел обратить внимание ТС на тот момент, что решение задачи без перебора вряд ли возможно.
По поводу условия сокращения перебора в (55): оно не слишком эффективно, т.к. отсекает очевидно недопустимые решения. Для эффективной работы алгоритмов, основанных на идее ветвей и границ обычно нужен еще и способ выбора "перспективного" ветвления, который в (55) не наблюдается. Т.е., я бы сказал, что (55) - это перебор с отсечением заведомо "неперспективных" ветвей. |
|||
70
web_profiler
10.06.15
✎
15:46
|
(69) вы все умными словами говорите, а по коду показать можете?
|
|||
71
Михаил Козлов
10.06.15
✎
15:57
|
(70) По коду я уже, вроде как, высказался: в общем случае без перебора не обойтись, а это чревато временем поиска решения.
Судорожные попытки к вечеру найти решение модификации известной задачи вряд ли будут успешными. Может быть, Вам имеет смысл посмотреть в сторону такого подхода: 1. Ограничить возможные длины, например, кратно 5 м. 2. Предварительно насчитать различные варианты (оптимальные) компоновки разных длин. 3. Для конкретного заказа поискать приемлимые варианты компоновки из уже насчитанных вариантов. |
|||
72
web_profiler
10.06.15
✎
16:02
|
(71) буду делать (68). на мой взгляд - самый оптимальный вариант
|
|||
73
ХардHard
10.06.15
✎
16:15
|
(72) Вот накатал по быстрому. Не пинать за костыли и прочее %)
Функция СоздатьТз() СпЗнач = Новый ТаблицаЗначений; СпЗнач.Колонки.Добавить("НомСтроки",,"НомСтроки"); СпЗнач.Колонки.Добавить("Колво",,"Колво"); СпЗнач.Колонки.Добавить("Коэф",,"Коэф"); СпЗнач.Колонки.Добавить("Метры",,"Метры"); СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 0; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 200; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 1; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 2; СтрСпЗнач.Колво = 2; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 100; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 3; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 45; СтрСпЗнач.Метры = 135; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 4; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 5; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 60; СтрСпЗнач.Метры = 180; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 6; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 40; СтрСпЗнач.Метры = 160; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 7; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 35; СтрСпЗнач.Метры = 105; Возврат СпЗнач; КонецФункции Процедура ВыполнитьЗадачу() Экспорт НужноеКоличество = 125; Тз1 = СоздатьТз(); // По сути ТЗ остатков ТзРезультат = Тз1.СкопироватьКолонки(); ТзРезультат.Колонки.Добавить("ИспользуемыеНомераСтрок",,"ИспользуемыеНомераСтрок"); Погнали(Тз1,ТзРезультат,НужноеКоличество); //В норм случае ответ будет в последней строке таблицы // иначе сортируй по коэф. КонецПроцедуры Процедура Погнали(Тз1,ТзРезультат,НужноеКоличество) ВариантовБольшеНет = Истина; ВсеКомбинацииУжеБольшеНужногоКоличества = Истина; КоличСтрокТзРез = ТзРезультат.Количество(); ПервыйРаз = Истина; Если КоличСтрокТзРез = 0 Тогда //первоначальное заполнение Для Каждого СтрокаТз1 из Тз1 Цикл НовСтрРез = ТзРезультат.Добавить(); ЗаполнитьЗначенияСвойств(НовСтрРез,СтрокаТз1); НовСтрРез.ИспользуемыеНомераСтрок = Строка(СтрокаТз1.НомСтроки); СтрокаТз1.Колво = СтрокаТз1.Колво - 1; КонецЦикла; ВариантовБольшеНет = Ложь; ВсеКомбинацииУжеБольшеНужногоКоличества = Ложь; Иначе Для Каждого СтрокаТз1 из Тз1 Цикл Если СтрокаТз1.Колво = 0 Тогда Продолжить; Иначе ВариантовБольшеНет = Ложь; КонецЕсли; Для Сч = 0 по КоличСтрокТзРез - 1 Цикл //Для Каждого СтрРез из ТзРезультат Цикл СтрРез = ТзРезультат[Сч]; Если СтрРез.Коэф > НужноеКоличество Тогда Продолжить; Иначе ВсеКомбинацииУжеБольшеНужногоКоличества = Ложь; КонецЕсли; Если Не ПервыйРаз Тогда НовСтрРез = ТзРезультат.Добавить(); ЗаполнитьЗначенияСвойств(НовСтрРез,СтрРез); Иначе НовСтрРез = СтрРез; КонецЕсли; НовСтрРез.Коэф = НовСтрРез.Коэф + СтрокаТз1.Коэф; НовСтрРез.ИспользуемыеНомераСтрок = НовСтрРез.ИспользуемыеНомераСтрок + ";" + Строка(СтрокаТз1.НомСтроки); Если НовСтрРез.Коэф = НужноеКоличество Тогда Возврат; КонецЕсли; КонецЦикла; Если ПервыйРаз Тогда ПервыйРаз = Ложь; КонецЕсли; СтрокаТз1.Колво = СтрокаТз1.Колво - 1; КонецЦикла; КонецЕсли; Если ВариантовБольшеНет или ВсеКомбинацииУжеБольшеНужногоКоличества Тогда Возврат; КонецЕсли; Погнали(Тз1,ТзРезультат,НужноеКоличество); КонецПроцедуры |
|||
74
web_profiler
10.06.15
✎
16:46
|
(73) не правильно работает ни без сортировки ни с сортировкой
|
|||
75
web_profiler
10.06.15
✎
16:47
|
(73) на число 125 ответ 7;0;6 - а это строки с коэф 40 и 60
ну никак не выйдет 125 |
|||
76
ХардHard
10.06.15
✎
16:48
|
(75) Да есть там косяк, пытаюсь исправить%). А 7 0 6 не подходит по условию ?
|
|||
77
ХардHard
10.06.15
✎
16:52
|
(76) Основной там косяк в этой строке СтрокаТз1.Колво = СтрокаТз1.Колво - 1;
Получается что у комбинации "1 0 0" и у " 0 0 0 " остаток 0вых строк одинаковый а это неверно. Хранить для каждой комбинкции остатки в соответствии что-то не хочется. Пытаюсь придумать что делать %) |
|||
78
web_profiler
10.06.15
✎
17:04
|
Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ УжеВзяли >= Метраж.Итог("НужноВзять") Тогда //промах
Возврат ИначеЕсли ЗаданнаяСумма = 0 Тогда //фиксируем решение Ответ = РабочаяТаблица.Скопировать( , "НомерСтроки, НужноВзять"); Ответ.Сортировать("НомерСтроки"); Метраж.ЗагрузитьКолонку(Ответ.ВыгрузитьКолонку("НужноВзять"), "НужноВзять") КонецЕсли; Курсор = РабочаяТаблица[ВГраница]; Для НужноВзять = 0 По Курсор.Имеется Цикл Курсор.НужноВзять = НужноВзять; ПодборКусков(РабочаяТаблица, ВГраница - 1, ЗаданнаяСумма - НужноВзять * Курсор.Коэффициент, УжеВзяли + НужноВзять); Курсор.НужноВзять = 0 КонецЦикла; Надо этот код доработать идеей разработчика: Потребуется, кроме "УжеВзяли", которое число кусков отражает, ввести параметр "ИспользовалиСтрок" и наращивать его при ненулевом "НужноВзять" Ничего не пойму. Где наращивать? И где ввести параметр "ИспользовалиСтрок" |
|||
79
web_profiler
10.06.15
✎
17:04
|
Вот с опредением процедуры и ее окончанием
Процедура ПодборКусков(РабочаяТаблица, ВГраница, ЗаданнаяСумма, УжеВзяли = 0) Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ УжеВзяли >= Метраж.Итог("НужноВзять") Тогда //промах Возврат ИначеЕсли ЗаданнаяСумма = 0 Тогда //фиксируем решение Ответ = РабочаяТаблица.Скопировать( , "НомерСтроки, НужноВзять"); Ответ.Сортировать("НомерСтроки"); Метраж.ЗагрузитьКолонку(Ответ.ВыгрузитьКолонку("НужноВзять"), "НужноВзять") КонецЕсли; Курсор = РабочаяТаблица[ВГраница]; Для НужноВзять = 0 По Курсор.Имеется Цикл Курсор.НужноВзять = НужноВзять; ПодборКусков(РабочаяТаблица, ВГраница - 1, ЗаданнаяСумма - НужноВзять * Курсор.Коэффициент, УжеВзяли + НужноВзять); Курсор.НужноВзять = 0 КонецЦикла; КонецПроцедуры // ПодборКусков() |
|||
80
web_profiler
10.06.15
✎
17:05
|
тут и надо ввести параметр и как-то нарастить
|
|||
81
ХардHard
10.06.15
✎
17:21
|
(80) Почти довел до ума, но домой пора. Завтра может скину, если актуально еще будет.
|
|||
82
web_profiler
10.06.15
✎
17:29
|
(81) я думаю вопрос будет и завтра открытым
|
|||
83
Garykom
гуру
10.06.15
✎
17:42
|
(71) да не, все проще, благодаря кратности
т.е. просто ТС задачу прямую пытается обратным способом решить )) и да выходит конкретный такой рюкзак кстати по факту )) ЗЫ раздели сначала все свои большие площади на мелкие с указанием еще одной колонки к какой крупной относится ЗЗЫ потом простейшая задача набора из мелких площадей требуемой площади и пересчет оставшихся общих площадей ЗЗЗЫ блин так замудрить простейшую задачу это уметь надо.... |
|||
84
web_profiler
10.06.15
✎
17:49
|
(83) а пример кода, а тот все целый день мне помогают. У меня реальный разрыв ЦП происходит
|
|||
85
Garykom
гуру
10.06.15
✎
17:51
|
(84) какой еще нафик пример кода когда задачка даже в екселе без vba решается?
|
|||
86
web_profiler
10.06.15
✎
17:51
|
(83) та лучше бы вы меня на... послали,
чем понять и уложить в голове"...набора из мелких площадей требуемой площади и пересчет оставшихся общих площадей..." |
|||
87
web_profiler
10.06.15
✎
17:52
|
может как-то расшифруете попонятнее?
|
|||
88
Garykom
гуру
10.06.15
✎
17:52
|
1. вместо строки
0 4 50 200 сделать 4 строки код исходной|код|площадь| 0|0|50 0|1|50 0|2|50 0|3|50 |
|||
89
web_profiler
10.06.15
✎
17:54
|
(88) делал уже
|
|||
90
web_profiler
10.06.15
✎
17:54
|
и получал то что надо
|
|||
91
Garykom
гуру
10.06.15
✎
17:54
|
(88)+ вместо следующих строк будет
1|4|50 2|5|50 2|6|50 3|7|45 3|8|45 3|9|45 ... |
|||
92
web_profiler
10.06.15
✎
17:54
|
но у меня к примеру 50 м бралось из строки 4*50, хотя есть отдельная строка 1*50
|
|||
93
Garykom
гуру
10.06.15
✎
17:55
|
(90) тогда не понял?
|
|||
94
Garykom
гуру
10.06.15
✎
17:56
|
(92) а слабо условие добавить когда строку берешь что? чтобы сначала брало только самые мелкие или еще как нужно
|
|||
95
web_profiler
10.06.15
✎
17:56
|
(93) сумму набирал, но она выбиралась из тех позиций, где4*50=200, а не 1*50 = 50
|
|||
96
web_profiler
10.06.15
✎
17:56
|
(95) согласен, проесняется
|
|||
97
web_profiler
10.06.15
✎
17:58
|
(94) а есть толковый код набора сумм?
|
|||
98
Garykom
гуру
10.06.15
✎
18:10
|
(97) так просто циклы вложенные
или можно добавить колонку приоритет выбора, сортирнуть и тогда один цикл всего будет |
|||
99
web_profiler
10.06.15
✎
18:16
|
(98) голова не варит совсем, ткни носом как цикл сформировать
|
|||
100
Злопчинский
10.06.15
✎
18:17
|
наблюдаю.
пишите еще |
|||
101
Garykom
гуру
10.06.15
✎
18:18
|
(99) 500 руб в час дальше
|
|||
102
web_profiler
10.06.15
✎
18:21
|
а если у меня запрашиваемая сумма 235 будет, то по вашей логике у меня отбирутся строки все по 50 потом по 100 и должно произойти разделение строки 4*50=200 из которой возьмется колво = 1 - это тоже не верно,если можно взять строку 200 и строку 35. Ваш алгоритм не корректен
|
|||
103
Злопчинский
10.06.15
✎
18:23
|
Минимизировать надо итоговое число кусков, из которых получится требуемый "метраж", или надо минимизировать число разбиений/отрезов..?
|
|||
104
web_profiler
10.06.15
✎
18:29
|
(103) требуется минимизировать число разбиения строк, максимально использовать сущесствующие строки, а если уж это не возможно - то тогда разбивать
к примеру как с суммой 235: надо взять ОДНУ строку 4*50 в которой уже есть 200 и разбить строку где присутствует коэф 35 |
|||
105
web_profiler
10.06.15
✎
18:30
|
перекур на пару часиков
|
|||
106
Garykom
гуру
10.06.15
✎
18:34
|
(102)-(104) вот это уже ближе к теме задачки
тогда берем 1-м циклом по исходным целые-большие площади после этого убираем все мелкие площади (которые входят в забранные большие) далее цикл по мелким по приоритету как бы частности уже остались то |
|||
107
Garykom
гуру
10.06.15
✎
18:35
|
(106) 2 цикла и будет какое то решение либо его не будет ))
и придется циклы заново запускать только уже исключая к примеру 1-ю добавленную площадь, чтобы попытался по другому сложить паззл |
|||
108
Михаил Козлов
10.06.15
✎
18:59
|
(107) "2 цикла и будет какое то решение либо его не будет ))".
Еще раз повторю: не известно эффективных алгоритмов решения 1-ого линейного уравнения в булевых переменных. А Вы все пытаетесь это сделать. "и придется циклы заново запускать только уже исключая к примеру 1-ю добавленную площадь, чтобы попытался по другому сложить паззл" - это уже будет перебор, который неизвестно когда закончится. |
|||
109
Garykom
гуру
10.06.15
✎
19:02
|
(108) а что есть доп уравнение связывающее перемененные для решения учитываем?
в данном случае мы заранее знаем из чего может складываться решение |
|||
110
Михаил Козлов
10.06.15
✎
19:10
|
(109) "в данном случае мы заранее знаем из чего может складываться решение" - каким это образом Вы это знаете?
Я уж не знаю, как объяснить, что для выяснения можно ли сложить пазл (хоть из "рулонов", хоть из "кусков") может потребоваться перебор. Практически приемлемый алгоритм (который наверняка не выльется в перебор) может только очень редко промахиваться (вырабатывать, что решения нет, хотя оно есть). Либо перебор с надеждой, что он не заткнется. |
|||
111
Ildarovich
10.06.15
✎
20:20
|
Вот решение с учетом второго критерия: максимизации количества полностью списанных строк.
МодульФормы: Процедура КнопкаСформироватьНажатие(Кнопка) РабочаяТаблица = Метраж.Выгрузить(); РабочаяТаблица.ЗаполнитьЗначения(0, "НужноВзять"); Метраж.ЗагрузитьКолонку(РабочаяТаблица.ВыгрузитьКолонку("НужноВзять"), "НужноВзять"); Кусков = Метраж.Итог("Имеется"); Пустых = 0; РабочаяТаблица.Сортировать("Коэффициент Убыв"); ПодборКусков(РабочаяТаблица, РабочаяТаблица.Количество() - 1, Требуется, Кусков, Пустых) КонецПроцедуры МодульОбъекта Процедура ПодборКусков(РабочаяТаблица, ВГраница, ЗаданнаяСумма, Кусков, Пустых, УжеВзяли = 0, УжеПустых = 0) Экспорт Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ (УжеВзяли >= Кусков И УжеПустых <= Пустых) Тогда //промах Возврат ИначеЕсли ЗаданнаяСумма = 0 Тогда //фиксируем решение Кусков = УжеВзяли; Пустых = УжеПустых; Сообщить("Кусков, Пустых : " + Кусков + ", " + Пустых); Ответ = РабочаяТаблица.Скопировать( , "НомерСтроки, НужноВзять"); Ответ.Сортировать("НомерСтроки"); Метраж.ЗагрузитьКолонку(Ответ.ВыгрузитьКолонку("НужноВзять"), "НужноВзять"); КонецЕсли; Курсор = РабочаяТаблица[ВГраница]; Для НужноВзять = 0 По Курсор.Имеется Цикл Курсор.НужноВзять = НужноВзять; ПодборКусков(РабочаяТаблица, ВГраница - 1, ЗаданнаяСумма - НужноВзять * Курсор.Коэффициент, Кусков, Пустых, УжеВзяли + НужноВзять, УжеПустых + Число(НужноВзять = Курсор.Имеется)); Курсор.НужноВзять = 0 КонецЦикла КонецПроцедуры // ПодборКусков() |
|||
112
Ildarovich
10.06.15
✎
20:32
|
(69) Михаил, не все рассмотрели. Это точно МВГ. Порядок ветвления задается сортировкой!
РабочаяТаблица.Сортировать("Коэффициент Убыв"); В результате первыми берутся крупные куски. Так как задача стоит минимизировать их число, то это эффективная стратегия (ветвления) упорядочивания перебора. Вообще это, наверное, предельно эффективный алгоритм. Вряд ли тут еще что можно докрутить. По реализации можно рекурсию раскрыть - будет существенно быстрее, но экспоненциальная зависимость все равно будет. Также можно объекты с одинаковыми коэффициентами сгруппировать, чтобы степень чуть-чуть понизить. Я еше, возможно, запросом то же самое попытаюсь из интереса сделать. Ну а если число строк будет значительным (20 и больше), придется на другие методы переходить (методы отсечения, генетические алгоритмы и прочее). Знать бы что за задача. - Стоит ли она вложения сил? |
|||
113
web_profiler
10.06.15
✎
21:26
|
(112) есть неточность, хотя так тоже ничего
при сумме 145 выбираются 2 строки 1*50, хотя имеется одна строка 2*50. это как-то можно подправить? по яндекс... в почте |
|||
114
web_profiler
10.06.15
✎
21:26
|
||||
115
Ildarovich
10.06.15
✎
22:27
|
(113) Поправить можно, но лучше еще раз уточнить:
То есть получается, что сейчас видение критерия таково: Получить разложение заданной суммы с: 0) Использованием минимального количества кусков; 1) При равенстве количества кусков выбираем вариант с минимальным количеством использованных целых строк; 2) При равенстве количества использованных кусков и целых строк выбираем вариант с минимальным количеством использованных строк вообще. Все так? |
|||
116
Ildarovich
10.06.15
✎
22:52
|
+(115) но, может быть, сами попробуете? Там все дело вот в этом фрагменте кода:
(УжеВзяли >= Кусков И УжеПустых <= Пустых) Его лучше записать так: НЕ (УжеВзяли < Кусков ИЛИ УжеПустых > Пустых) В скобках () записано условие, при котором будет фиксироваться новое лучшее достижение. Правильно для (115) будет написать так: НЕ (УжеВзяли < Кусков ИЛИ УжеВзяли = Кусков И УжеПустых < Пустых ИЛИ УжеВзяли = Кусков И УжеПустых = Пустых И УжеИспользованных < Использованных) "Использованных" сделать в модуле формы нулем, сделать параметром его и УжеИспользованных, УжеИспользованных наращивать + Число(НужноВзять > 0) при входе в рекурсию... как то так |
|||
117
ХардHard
11.06.15
✎
09:41
|
Функция СоздатьТз()
СпЗнач = Новый ТаблицаЗначений; СпЗнач.Колонки.Добавить("НомСтроки",,"НомСтроки"); СпЗнач.Колонки.Добавить("Колво",,"Колво"); СпЗнач.Колонки.Добавить("Коэф",,"Коэф"); СпЗнач.Колонки.Добавить("Метры",,"Метры"); СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 0; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 200; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 1; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 2; СтрСпЗнач.Колво = 2; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 100; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 3; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 45; СтрСпЗнач.Метры = 135; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 4; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 5; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 60; СтрСпЗнач.Метры = 180; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 6; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 40; СтрСпЗнач.Метры = 160; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 7; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 35; СтрСпЗнач.Метры = 105; Возврат СпЗнач; КонецФункции Процедура ВыполнитьЗадачу() Экспорт НужноеКоличество = 425; Тз1 = СоздатьТз(); // По сути ТЗ остатков ТзРезультат = Тз1.СкопироватьКолонки(); ТзРезультат.Колонки.Добавить("ИспользуемыеНомераСтрок",,"ИспользуемыеНомераСтрок"); ТзРезультат.Колонки.Добавить("ОстаткиПоКомбинации",,"ОстаткиПоКомбинации"); Погнали(Тз1,ТзРезультат,НужноеКоличество); //В норм случае ответ будет в последней строке таблицы // иначе сортируй по коэф. КонецПроцедуры Процедура Погнали(Тз1,ТзРезультат,НужноеКоличество) ВариантовБольшеНет = Истина; ВсеКомбинацииУжеБольшеНужногоКоличества = Истина; КоличСтрокТзРез = ТзРезультат.Количество(); ИскомоеЗначениеНайдено = Ложь; Если КоличСтрокТзРез = 0 Тогда //первоначальное заполнение МассивОстатков1 = Новый Массив; //потом поправь обязательно ;) Для Каждого СтрокаТз2 из Тз1 Цикл МассивОстатков1.Добавить(СтрокаТз2.Колво); КонецЦикла; Для СЧ_СтрокаТз1 = 0 По Тз1.Количество()-1 Цикл СтрокаТз1 = Тз1[СЧ_СтрокаТз1]; НовСтрРез = ТзРезультат.Добавить(); ЗаполнитьЗначенияСвойств(НовСтрРез,СтрокаТз1); НовСтрРез.ИспользуемыеНомераСтрок = Строка(СтрокаТз1.НомСтроки); МассивОстатков = Новый Массив; //потом поправь обязательно ;) Для Каждого ЭлМасс из МассивОстатков1 Цикл МассивОстатков.Добавить(ЭлМасс); КонецЦикла; НовСтрРез.ОстаткиПоКомбинации = МассивОстатков; НовСтрРез.ОстаткиПоКомбинации[СЧ_СтрокаТз1] = НовСтрРез.ОстаткиПоКомбинации[СЧ_СтрокаТз1] - 1; КонецЦикла; ВариантовБольшеНет = Ложь; ВсеКомбинацииУжеБольшеНужногоКоличества = Ложь; Иначе //Нужно подчищать ненужные строки МассивСтрокКУдалению = Новый Массив; Для Каждого СтрРез Из ТзРезультат Цикл Если СтрРез.Коэф >= НужноеКоличество Тогда Продолжить; КонецЕсли; МассивСтрокКУдалению.Добавить(СтрРез); КонецЦикла; Для СЧ_СтрокаТз1 = 0 По Тз1.Количество()-1 Цикл если ИскомоеЗначениеНайдено Тогда Прервать; КонецЕсли; СтрокаТз1 = Тз1[СЧ_СтрокаТз1]; Для Сч = 0 по КоличСтрокТзРез - 1 Цикл //Для Каждого СтрРез из ТзРезультат Цикл СтрРез = ТзРезультат[Сч]; Если СтрРез.ОстаткиПоКомбинации[СЧ_СтрокаТз1] = 0 Тогда Продолжить; КонецЕсли; Если СтрРез.Коэф >= НужноеКоличество Тогда Продолжить; Иначе ВсеКомбинацииУжеБольшеНужногоКоличества = Ложь; КонецЕсли; НовСтрРез = ТзРезультат.Добавить(); ЗаполнитьЗначенияСвойств(НовСтрРез,СтрРез); НовСтрРез.Коэф = НовСтрРез.Коэф + СтрокаТз1.Коэф; НовСтрРез.ИспользуемыеНомераСтрок = НовСтрРез.ИспользуемыеНомераСтрок + ";" + Строка(СтрокаТз1.НомСтроки); СтрРез.ОстаткиПоКомбинации[СЧ_СтрокаТз1] = СтрРез.ОстаткиПоКомбинации[СЧ_СтрокаТз1] - 1; МассивОстатков = Новый Массив; //потом поправь обязательно ;) Для Каждого ЭлМасс из СтрРез.ОстаткиПоКомбинации Цикл МассивОстатков.Добавить(ЭлМасс); КонецЦикла; НовСтрРез.ОстаткиПоКомбинации = МассивОстатков; Если НовСтрРез.Коэф = НужноеКоличество Тогда ИскомоеЗначениеНайдено = Истина; Прервать; КонецЕсли; КонецЦикла; КонецЦикла; //Нужно подчищать ненужные строки Для Каждого СтрКУд из МассивСтрокКУдалению Цикл ТзРезультат.удалить(СтрКУд); КонецЦикла; КонецЕсли; Если ИскомоеЗначениеНайдено Тогда Возврат; КонецЕсли; Если ВсеКомбинацииУжеБольшеНужногоКоличества Тогда Возврат; КонецЕсли; Погнали(Тз1,ТзРезультат,НужноеКоличество); КонецПроцедуры |
|||
118
ХардHard
11.06.15
✎
09:42
|
+ (117) Доделал, тупо перебором с очисткой заведомо ненужных вариантов. Остатки пришлось хранить в массиве для каждой комбинации.
|
|||
119
web_profiler
11.06.15
✎
09:44
|
млж я че-то не то делаю? но у меня не получается добиться нужного результата
Процедура ПодборКусков(РабочаяТаблица, ВГраница, ЗаданнаяСумма, Кусков, Пустых, УжеВзяли = 0, УжеПустых = 0, УжеИспользованных = 0, Использованных = 0) Экспорт Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ НЕ (УжеВзяли < Кусков ИЛИ УжеВзяли = Кусков И УжеПустых < Пустых ИЛИ УжеВзяли = Кусков И УжеПустых = Пустых И УжеИспользованных < Использованных) Тогда //Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ (УжеВзяли >= Кусков И УжеПустых <= Пустых) Тогда //промах //Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ НЕ (УжеВзяли < Кусков ИЛИ УжеПустых > Пустых) Тогда Возврат ИначеЕсли ЗаданнаяСумма = 0 Тогда //фиксируем решение Кусков = УжеВзяли; Пустых = УжеПустых; Использованных = УжеИспользованных; Сообщить("Кусков, Пустых : " + Кусков + ", " + Пустых + ", " + Использованных); Ответ = РабочаяТаблица.Скопировать( , "НомерСтроки, НужноВзять"); Ответ.Сортировать("НомерСтроки"); Метраж.ЗагрузитьКолонку(Ответ.ВыгрузитьКолонку("НужноВзять"), "НужноВзять"); КонецЕсли; Курсор = РабочаяТаблица[ВГраница]; Для НужноВзять = 0 По Курсор.Имеется Цикл Курсор.НужноВзять = НужноВзять; //ПодборКусков(РабочаяТаблица, ВГраница - 1, ЗаданнаяСумма - НужноВзять * Курсор.Коэффициент, Кусков, Пустых, УжеВзяли + НужноВзять, УжеПустых + Число(НужноВзять = Курсор.Имеется)); ПодборКусков(РабочаяТаблица, ВГраница - 1, ЗаданнаяСумма - НужноВзять * Курсор.Коэффициент, Кусков, Пустых, УжеВзяли + НужноВзять, УжеПустых + Число(НужноВзять = Курсор.Имеется), УжеИспользованных + Число(НужноВзять > 0)); Курсор.НужноВзять = 0 КонецЦикла КонецПроцедуры // ПодборКусков() http://iscr.ru/1434005010/ для облегчения Процедура ПриОткрытии() Стр = Метраж.Добавить(); Стр.Имеется = 4; Стр.Коэффициент = 50; Стр = Метраж.Добавить(); Стр.Имеется = 1; Стр.Коэффициент = 50; Стр = Метраж.Добавить(); Стр.Имеется = 2; Стр.Коэффициент = 50; Стр = Метраж.Добавить(); Стр.Имеется = 3; Стр.Коэффициент = 45; Стр = Метраж.Добавить(); Стр.Имеется = 1; Стр.Коэффициент = 50; Стр = Метраж.Добавить(); Стр.Имеется = 3; Стр.Коэффициент = 60; Стр = Метраж.Добавить(); Стр.Имеется = 4; Стр.Коэффициент = 40; Стр = Метраж.Добавить(); Стр.Имеется = 3; Стр.Коэффициент = 35; КонецПроцедуры |
|||
120
web_profiler
11.06.15
✎
09:56
|
(117) а вы ее тестили? она около 30 сек работает
|
|||
121
web_profiler
11.06.15
✎
09:58
|
В послед. строке 5;5;0;0;0;2;5;3 - єто ответ на сумму 125?
|
|||
122
web_profiler
11.06.15
✎
09:59
|
(117) ссори не увидел, 425
|
|||
123
ХардHard
11.06.15
✎
10:00
|
(121) там 425 стоит. исправь на нужную. Хз будет ли у тебя работать быстрее с 425 %) . То что код не идеален я в курсе.
Писал абы как чтобы времени не тратить. |
|||
124
web_profiler
11.06.15
✎
10:01
|
7;6;0 на 125 - не верно
|
|||
125
web_profiler
11.06.15
✎
10:02
|
5;5;0 с сортировкой
|
|||
126
ХардHard
11.06.15
✎
10:02
|
(124) 50+40+35 = ?
|
|||
127
ХардHard
11.06.15
✎
10:03
|
(125) Там выход из цикла сразу после первой комбинации идет. Можешь цикл продолжить или даже еще 1 лишний запустить.
Потом отобрать все 125 и выбирай которая понравится. |
|||
128
web_profiler
11.06.15
✎
10:08
|
(127) Сумма 145 = 3;0;0 45+50+50 Зачем дробить строку 0? если есть цельная строка 2*50 (строка 2)
|
|||
129
ХардHard
11.06.15
✎
10:09
|
(128) см 127
|
|||
130
ХардHard
11.06.15
✎
10:10
|
+ к (129) Да и ты в курсе что такое перебор?
|
|||
131
web_profiler
11.06.15
✎
10:10
|
на числе 425 - оооочень долго, ну очень. Метод ветвей как-то пошустрей будет
|
|||
132
ХардHard
11.06.15
✎
10:12
|
(131) Скорее всего будет быстрее. Перебор быстрее реализовать КМК %)
|
|||
133
web_profiler
11.06.15
✎
11:52
|
Если ВГраница < 0 ИЛИ ЗаданнаяСумма < 0 ИЛИ НЕ (УжеПустых > Пустых И УжеИспользованных = Использованных ИЛИ УжеВзяли < Кусков ) Тогда
отрабатывает для 145, а вот для 150 берет три строки из 4*50, хотя должна взять строку 1*50 и строку 2*50 какое условие надо добавить? |
|||
134
Михаил Козлов
11.06.15
✎
11:57
|
(112) "Ну а если число строк будет значительным (20 и больше), придется на другие методы переходить (методы отсечения, генетические алгоритмы и прочее).".
Насколько я понимаю, в рюкзаке отсечения ничего не дают, а генетические алгоритмы для формальных задач дискретной оптимизации - профанация. Кстати, в примере ТС увеличил количества (при тех же 8 строках) - получил больше 60 000 вызовов рекурсивной процедуры. Не воспринимайте мое замечание как критику приведенного кода: это грамотная реализация перебора и реальная помощь ТС. |
|||
135
web_profiler
11.06.15
✎
11:59
|
михаил, хватит умничать у меня конкретная задача. И проблемы буду решать по мере их поступления
|
|||
136
web_profiler
11.06.15
✎
11:59
|
вы за все время ни одной строки кода не написали - будьте добры, не мешайте
|
|||
137
web_profiler
11.06.15
✎
12:00
|
сообщения адресуются Ildarovich
|
|||
138
Михаил Козлов
11.06.15
✎
12:13
|
(135) Вообще-то, я не к Вам обращался. Кстати, "Михаил" пишется с прописной буквы.
|
|||
139
web_profiler
11.06.15
✎
12:18
|
(138)нет смысла с Вами общаться
|
|||
140
ХардHard
11.06.15
✎
12:24
|
Перем Тз1,ТзРезультат,НужноеКоличество,ИмяСворачиваемыхКолонок;
Функция СоздатьТз() СпЗнач = Новый ТаблицаЗначений; СпЗнач.Колонки.Добавить("НомСтроки",,"НомСтроки"); СпЗнач.Колонки.Добавить("Колво",,"Колво"); СпЗнач.Колонки.Добавить("Коэф",,"Коэф"); СпЗнач.Колонки.Добавить("Метры",,"Метры"); СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 0; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 200; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 1; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 2; СтрСпЗнач.Колво = 2; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 100; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 3; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 45; СтрСпЗнач.Метры = 135; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 4; СтрСпЗнач.Колво = 1; СтрСпЗнач.Коэф = 50; СтрСпЗнач.Метры = 50; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 5; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 60; СтрСпЗнач.Метры = 180; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 6; СтрСпЗнач.Колво = 4; СтрСпЗнач.Коэф = 40; СтрСпЗнач.Метры = 160; СтрСпЗнач = СпЗнач.Добавить(); СтрСпЗнач.НомСтроки = 7; СтрСпЗнач.Колво = 3; СтрСпЗнач.Коэф = 35; СтрСпЗнач.Метры = 105; Возврат СпЗнач; КонецФункции Процедура ВыполнитьЗадачу() Экспорт НужноеКоличество = 425; Тз1 = СоздатьТз(); // По сути ТЗ остатков ТзРезультат = Тз1.СкопироватьКолонки(); ТзРезультат.Колонки.Удалить("НомСтроки"); ТзРезультат.Колонки.Удалить("Колво"); ТзРезультат.Колонки.Удалить("Метры"); //Добавим Строки Для хранения остатков Для Каждого строкаТз1 из Тз1 Цикл ТзРезультат.Колонки.Добавить("Использовано_"+Строка(строкаТз1.НомСтроки),ПолучитьОписаниеТиповЧисла1(3),"Использовано_"+Строка(строкаТз1.НомСтроки)); КонецЦикла; //первоначальное заполнение Для СЧ_СтрокаТз1 = 0 По Тз1.Количество() -1 Цикл СтрокаТз1 = Тз1[СЧ_СтрокаТз1]; НовСтрРез = ТзРезультат.Добавить(); ЗаполнитьЗначенияСвойств(НовСтрРез,СтрокаТз1); НовСтрРез["Использовано_"+Строка(СЧ_СтрокаТз1)] = 1; КонецЦикла; ИмяСворачиваемыхКолонок = ""; Для Каждого Колон Из ТзРезультат.Колонки Цикл ИмяСворачиваемыхКолонок = ИмяСворачиваемыхКолонок +?(ИмяСворачиваемыхКолонок = "","",",")+Колон.Имя; КонецЦикла; Погнали(); //В норм случае ответ будет в последней строке таблицы ТзРезультат // иначе сортируй по коэф. КонецПроцедуры Процедура Погнали() ВариантовБольшеНет = Истина; ВсеКомбинацииУжеБольшеНужногоКоличества = Истина; КоличСтрокТзРез = ТзРезультат.Количество(); КоличСтрокТз1 = Тз1.Количество(); ИскомоеЗначениеНайдено = Ложь; //Нужно подчищать ненужные строки МассивСтрокКУдалению = Новый Массив; Для Каждого СтрРез Из ТзРезультат Цикл Если СтрРез.Коэф >= НужноеКоличество Тогда Продолжить; КонецЕсли; МассивСтрокКУдалению.Добавить(СтрРез); КонецЦикла; Для Сч = 0 по КоличСтрокТзРез - 1 Цикл СтрРез = ТзРезультат[Сч]; Если СтрРез.Коэф >= НужноеКоличество Тогда Продолжить; Иначе ВсеКомбинацииУжеБольшеНужногоКоличества = Ложь; КонецЕсли; Если ИскомоеЗначениеНайдено Тогда Прервать; КонецЕсли; Для СЧ_СтрокаТз1 = 0 По КоличСтрокТз1-1 Цикл СтрокаТз1 = Тз1[СЧ_СтрокаТз1]; ИмяКолонкиИспользовано = "Использовано_"+Строка(СЧ_СтрокаТз1); ТекКоличИспользованых = СтрРез[ИмяКолонкиИспользовано]; Если ТекКоличИспользованых = СтрокаТз1.Колво Тогда Продолжить; КонецЕсли; НовСтрРез = ТзРезультат.Добавить(); ЗаполнитьЗначенияСвойств(НовСтрРез,СтрРез); НовСтрРез[ИмяКолонкиИспользовано] = ТекКоличИспользованых + 1; НовСтрРез.Коэф = НовСтрРез.Коэф + СтрокаТз1.Коэф; Если НовСтрРез.Коэф = НужноеКоличество Тогда ИскомоеЗначениеНайдено = Истина; Прервать; КонецЕсли; КонецЦикла; КонецЦикла; //Нужно подчищать ненужные строки Для Каждого СтрКУд из МассивСтрокКУдалению Цикл ТзРезультат.удалить(СтрКУд); КонецЦикла; ТзРезультат.Свернуть(ИмяСворачиваемыхКолонок); Если ИскомоеЗначениеНайдено Тогда Возврат; КонецЕсли; Если ВсеКомбинацииУжеБольшеНужногоКоличества Тогда Возврат; КонецЕсли; Погнали(); КонецПроцедуры Функция ПолучитьОписаниеТиповЧисла1(Разрядность,РазрядностьДробнойЧасти=0) Массив = Новый Массив; Массив.Добавить(Тип("Число")); КвалификаторЧисла = Новый КвалификаторыЧисла(Разрядность,РазрядностьДробнойЧасти); Возврат Новый ОписаниеТипов(Массив, КвалификаторЧисла); КонецФункции |
|||
141
ХардHard
11.06.15
✎
12:24
|
+ к (140) Кое что улучшил. Вполне шустро стала работать.
|
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |