Имя: Пароль:
1C
 
Алгоритм для подбора документов с лучшей цены
, ,
0 Pepeega
 
24.03.21
14:09
Добрый день, возникла проблема, понадобилось написать алгоритм, который будет искать документы с более подходящими ценами, то-есть пользователь допустим вводит сумму 10 тысяч, ему из всех документов выбираются документы те, у которых общая сумма будет 10 тысяч или близкая к этой сумме, но не больше, полдня уже ломаю голову и ничего не приходит на ум, если кто-то сталкивался с проблемой буду благодарен за помощь
1 Kassern
 
24.03.21
14:14
(0) документы одного типа? Что будет правильно вернуть 2 документа по 5тыс или 1 док на 10тыс?
2 HawkEye
 
24.03.21
14:15
(0) все комбинации документов?
3 Масянька
 
24.03.21
14:15
А вы про "не могут написать функцию, которая выводит строку в обратном порядке"...
4 Малыш Джон
 
24.03.21
14:16
(0) может быть куча решений

Надо накладывать какой то критерий оптимальности(почему один набор документов лучше другого набора с такой же суммой) - и получается задача о рюкзаке. Способы решения гуглятся.
5 Pepeega
 
24.03.21
14:17
(1) Да, документы одного типа, расходная накладная
(2) перебрать все документы и найти лучшую цену документов, исходя из той, которую ввел пользователь
6 Pepeega
 
24.03.21
14:18
(3) ну, если вы про "перебрать документы по убыванию цены" , то это не совсем правильный подход к такой задаче
7 piter3
 
24.03.21
14:19
(5) масло масляное.Если даже критерии не можешь узнать у заказчика
8 Pepeega
 
24.03.21
14:19
(4) да вот я погуглил решения и что-то как-то не совсем понимаю, как мне перебрать эти кучи документом и найти оптимальные документы, если я допустим перебрал все документы, как я узнаю, что допустим 3 документа 900р + 5000р + 4000р лучше, чем остальные, куда сохранять все наборы документов?
9 Pepeega
 
24.03.21
14:21
(7) Заказчик указал, что ему нужно, чтобы он вводил в поле цену "Допустим 10000р", и из всех документов которые были за последний месяц, ему отобрались документы с более подходящей суммой цен = 10000р
10 piter3
 
24.03.21
14:22
(9) Ноль он дал,дальше узнавай подробности [ с более подходящей суммой цен = 10000р]
11 Малыш Джон
 
24.03.21
14:22
(9) если у тебя 1000 вариантов наборов документов и у каждого сумма = 10000, какой выбирать?
12 Pepeega
 
24.03.21
14:24
перебрать все документы не составляет труда, возникла именно проблема с построением, допустим есть всего 5 документов за месяц, сумма всех документов 18000р, первые 3 допустим общую сумму 9500 имеют, а вот другие 2 имеют общую сумму 9990р, как я узнаю, что второй вариант лучше, чем первый, мне же куда-то его сохранить нужно, а если таких документов несколько тысяч?
13 Pepeega
 
24.03.21
14:25
(11) тут не играет роль, какие документы увидит пользователь, они будут отсортированы по дате, сейчас проблема всей задачи, это правильный алгоритм, который будет находить лучшею цену
14 yzimin
 
24.03.21
14:25
(12) ну так сохраняй, в чём проблема-то?
15 Малыш Джон
 
24.03.21
14:26
(14) а так можно было??? ))
16 Масянька
 
24.03.21
14:27
(15) Не-а :)
17 Pepeega
 
24.03.21
14:27
(14) не совсем понимаю куда, вот в чем проблема, ну окей, нашел я 3 документа с общей суммой 9000р, сохраняю их в массив, их же нужно отделить как-то, чтобы сравнить потом набор разных документов
18 Малыш Джон
 
24.03.21
14:28
(13) Слушай, ну по конкретной реализации алгоритма - тут уж поднапряги мозги. Как говорят герои - "никто, кроме тебя".
19 Масянька
 
24.03.21
14:29
(18) Нормальные герои всегда идут в обход (С)
20 yzimin
 
24.03.21
14:29
(17) Новый Структура(НомерИтерации, МассивДокументов, Сумма)
21 Pepeega
 
24.03.21
14:30
(18) Тут вы правы, но на самом деле не лезет в голову момент сохранения разных наборов документов, только для этого сюда написал.

Думаю, что нужно сходить на обед и в голову придут варианты, все равно спасибо, кто откликнулся!
22 El_Duke
 
гуру
24.03.21
14:30
(9) "ему отобрались документы с более подходящей суммой цен = 10000р"

Ты в 5 раз твердишь одно и тоже бессмысленное сочетание "с наиболее подходящей". Этот словесный критерий надо формализовать, положить на язык математики
Нихрена не понятно что заказчик имеет ввиду под "наиболее подходящим"
23 Pepeega
 
24.03.21
14:34
(22) Видимо объяснил и вправду не совсем корректно, сам имею больше информации, как и писал выше, если пользователь вводит сумму 10000р, после обхода рекурсией всех вариантов, у нас допустим в массиве получается 5 записей, 1) 8300р. 2) 9000р. 3)7800р. 4) 9800р. 5) 10000р. и мы должны будем указать вывести те документы, которые находятся в 5й записи, если бы не было 5й записи, то тогда 4, потому что она самая близкая к 10000р(введённая сумма пользователем)
24 Pepeega
 
24.03.21
14:36
(20) спасибо, как вариант подумать в эту сторону
25 Малыш Джон
 
24.03.21
14:43
(23) я конечно очень старомодный, но может не хранить все наборы, тупо на каждой итерации сравнивать сумму нового набора с суммой сохраненного ранее и если новый набор оптимальнее, то сохранять его вместо старого?
26 Масянька
 
24.03.21
14:44
(23) Запросом отобрать док-ты, у которых сумма меньше введенной (параметр + условие).
Результат запроса упорядочить по возрастанию суммы.
Вывести первую запись результата запроса.
27 Злопчинский
 
24.03.21
14:45
смотря сколько документов. для незначительного количества тупо перебором, отсекая варианты которые уже хуже найденных.
28 Злопчинский
 
24.03.21
14:46
лет 15 назад писал какую-то тупую хрень по тупым алгоритмам с тупыми недоработками. тупо работала.
https://infostart.ru/public/14526/
29 Pepeega
 
24.03.21
14:53
(25) с этой стороны хотел подойти, уже даже появилось полное решение, но на половине пути реализации, я понял, что первая "пачка" документов может быть 9000р, вторая 10000р, третья 8500р, четвёртая 9200р, если просто проверять, лучше наше значение чем последнее сохранённое или нет, то те самые 9000р и 10000р уже будут не в нашей видимости и мы о них не будем знать
30 H A D G E H O G s
 
24.03.21
14:54
ЦелеваяСумма=1000000;
    СуммаКСписанию=ЦелеваяСумма;
    МассивДокументов=Новый Массив;
    Пока Истина Цикл
        Запрос=Новый Запрос;
        Запрос.Текст=
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    РеализацияТоваровУслуг.Ссылка КАК Ссылка,
        |    РеализацияТоваровУслуг.СуммаДокумента КАК СуммаДокумента
        |ИЗ
        |    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
        |ГДЕ
        |    РеализацияТоваровУслуг.СуммаДокумента <= &СуммаКСписанию
        |
        |УПОРЯДОЧИТЬ ПО
        |    СуммаДокумента УБЫВ";
        Запрос.УстановитьПараметр("СуммаКСписанию",СуммаКСписанию);
        Выборка=Запрос.Выполнить().Выбрать();
        Если Выборка.Количество()=0 Тогда
            Прервать;
        КонецЕсли;
        Выборка.Следующий();
        Если Выборка.СуммаДокумента<=0 Тогда
            Прервать;
        КонецЕсли;
        
        СуммаКСписанию=СуммаКСписанию-Выборка.СуммаДокумента;
        МассивДокументов.Добавить(Выборка.Ссылка);
        Если СуммаКСписанию<=0 Тогда
            Прервать;
        КонецЕсли;        
    КонецЦикла;
31 Pepeega
 
24.03.21
14:55
(26) Тоже как вариант, но сумма может быть не 10000р, а больше, все равно придётся прибегать к алгоритму, который будет из всех полученных документом искать какую-то наилучшую общую сумму "пачки" документов
32 H A D G E H O G s
 
24.03.21
14:55
Запрос плох, в промсистеме нужно рассмотреть добавления индеска по сумме.
33 El_Duke
 
гуру
24.03.21
14:59
(29) с чего вдруг ?
Первая пачка 9000, вторая 8500, ее не храним, сравниваем с первой. Третья 10000, не храним первую ибо третья лучше. Т.о. всегда сравниваем с лучшей пачкой
34 Pepeega
 
24.03.21
15:00
(30) Спасибо за пример, уже хотел прибегнуть к чему-то похожему, но стараюсь отказаться от запросов в цикле, хоть мы и выбираем 1 документ всего, но всё же, но вариант вполне рабочий, спасибо за потраченное время!
я тут подумал, что можно же один раз получить все документы, оставить только нужные и уже дальше циклом перебирать по аналогии
35 Pepeega
 
24.03.21
15:02
(33) Не сразу понял такой вариант, но да, если такой вариант тоже вполне подходит для решения.

Спасибо всем кто откликнулся, думаю что-то придумать на свежую голову получится!
36 NorthWind
 
24.03.21
15:03
(0) задача о рюкзаке, что ли?
37 NorthWind
 
24.03.21
15:03
ну да, она. у вас вместимость рюкзака это сумма на которую набирать, а документы - предметы.
38 SleepyHead
 
гуру
24.03.21
15:57
(0) Задача о рюкзаке.
39 mikecool
 
24.03.21
16:47
рюкзак еще не предлагали?
40 HawkEye
 
24.03.21
16:49
(39) лучше деньгами....)
41 Pepeega
 
24.03.21
16:50
да, подобие задачи о рюкзаке
(37) это понятно, осталось только с алгоритмом закончить :)
42 Малыш Джон
 
24.03.21
16:57
(39) Пока нет. Думаю, надо предложить, а то никто так и не вспомнит.
43 NorthWind
 
24.03.21
17:01
а рассматривали применить что-нибудь от рюкзака? Метод ветвей и границ, динамическое программирование, еще что-нибудь? Там же куча методов, все они описаны, и, насколько я помню, не сильно сложны. Первый курс института.
44 Pepeega
 
24.03.21
17:06
(43) Да, решаю с помощью динамического программирования
45 mistеr
 
24.03.21
17:08
(43) Не торопи события. Сначала у ТС запрос, генерирующий все варианты, должен колом встать.
Потом он задумается о других методах.
46 Deal with it
 
24.03.21
17:32
(0) задача рюкзака отлично решается "Динамическим программированием" из высшей математики
https://ru.wikipedia.org/wiki/Динамическое_программирование

https://ru.wikipedia.org/wiki/Задача_о_рюкзаке
47 Deal with it
 
24.03.21
17:33
(44) опоздал я с ответом))
48 Михаил Козлов
 
24.03.21
17:45
(41) Ваша задача немного отличается от рюкзака: нужно подобрать не максимальную сумму (без превышения лимита), а наименее отличающуюся от него.
Для рюкзака неплохо работает жадный алгоритм:
- упорядочить документы по убыванию суммы;
- выбирать документы в этом порядке. Если текущая сумма + сумма документа укладывается в лимит, добавить документ в массив отобранных.
Может быть имеет смысл поискать более хитрый (чем жадный) алгоритм для рюкзака. Можно попробовать и метод динамического программирования, только нужно иметь в виду, что рюкзак "почти" полная переборная задача, и метод динамического программирования, вообще говоря, может привести к перебору. Метод ветвей и границ в рюкзаке не сработает: не будет эффективного отсечения.
Я бы попробовал жадным алгоритмом решить 2 задачи о рюкзаке. Одна - на максимум с меньше лимита, вторая - на минимум с больше лимита. Вторую можно свести к первой заменив суммы документов на их отрицательные значения (и лимит также).
49 NorthWind
 
24.03.21
19:00
(48) да, ветвей и границ здесь даст время близкое к полному перебору, непонятно как определять неоптимальность ветки.
А вот генетика тут может быть интересной. Фитнес-функция определяется легко - как раз дельта с итоговой суммой, чем меньше, тем лучше. И можно скрещивать сочетания доков, добиваясь хорошего результата.
50 Михаил Козлов
 
24.03.21
19:39
(49) Генетика - буржуазная лженаука.
51 Pepeega
 
25.03.21
05:53
В общем вот что получилось, но не совсем корректно работает, потому что у меня все массивы имею значения 0, не совсем понимаю почему...



Для каждого стр из ВсеДокументы Цикл
        МассивДокументов.Добавить(Новый структура("ТребуемаяСумма, Сумма",
                                        ТребуемаяСумма, стр.Сумма));
КонецЦикла;

МассивПроверки = Новый Массив(ТребуемаяСумма,ВсеДокументы.Количество());
    
    Для сч = 0 по МассивДокументов[0].ТребуемаяСумма-1 Цикл
        МассивПроверки[сч][0] = 0;
    КонецЦикла;
                                                      
    Для сч = МассивДокументов[0].ТребуемаяСумма по ВсеДокументы.Количество() Цикл                        
        МассивПроверки[сч][0] = МассивДокументов[0].Сумма;
    КонецЦикла;
                                                                          
    Для Счетчик = 1 по МассивДокументов.ВГраница() Цикл
        Для сч = 0 по МассивДокументов[Счетчик].ТребуемаяСумма-1 Цикл
            МассивПроверки[сч][Счетчик] = МассивПроверки[сч][Счетчик-1];
        КонецЦикла;
        Для сч = МассивДокументов[Счетчик].ТребуемаяСумма по ВсеДокументы.Количество() Цикл
            МассивПроверки[сч][Счетчик] = Макс(МассивПроверки[сч][Счетчик-1], МассивПроверки[сч - МассивДокументов[Счетчик].ТребуемаяСумма][Счетчик - 1] + МассивДокументов[Счетчик].Сумма);
        КонецЦикла;
    КонецЦикла;
        
    ОставшаясяСумма = ТребуемаяСумма;
    МассивЛучшихДокументов = Новый Массив;
    ПервыйПроход = Истина;
    
    Для Х = МассивЛучшихДокументов.ВГраница() по 1 Цикл
        Если ПервыйПроход Тогда
            Если МассивПроверки[ОставшаясяСумма-1][Х+2] <> МассивПроверки[ОставшаясяСумма-1][Х+1] Тогда
                МассивЛучшихДокументов.Добавить(МассивДокументов[Х]);
                ОставшаясяСумма = ОставшаясяСумма - МассивДокументов[Х].Сумма;
            КонецЕсли;
            ПервыйПроход = Ложь;
        ИначеЕсли ОставшаясяСумма = ТребуемаяСумма Тогда
            Если МассивПроверки[ОставшаясяСумма-1][Х] <> МассивПроверки[ОставшаясяСумма-1][Х+1] Тогда
                МассивЛучшихДокументов.Добавить(МассивДокументов[Х]);
                ОставшаясяСумма = ОставшаясяСумма - МассивДокументов[Х].Сумма;
            КонецЕсли;
        Иначе
            Если МассивПроверки[ОставшаясяСумма][Х] <> МассивПроверки[ОставшаясяСумма][Х-1] Тогда
                МассивЛучшихДокументов.Добавить(МассивДокументов[Х]);
                ОставшаясяСумма = ОставшаясяСумма - МассивДокументов[Х].Сумма;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    Если МассивПроверки[ОставшаясяСумма][0] <> 0 Тогда
        МассивЛучшихДокументов.Добавить(МассивДокументов[0]);
    КонецЕсли;
52 Михаил Козлов
 
25.03.21
17:59
Разбираться не стал. Вот вариант жадного алгоритма (для УФ). Предполагается, что все суммы в одной валюте.
&НаСервере
Процедура ПодобратьНаСервере()
    Объект.Документы.Очистить();
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    РТиУ.Ссылка КАК Ссылка,
    |    РТиУ.СуммаДокумента КАК Сумма
    |ИЗ Документ.РеализацияТоваровУслуг КАК РТиУ
    |ГДЕ РТиУ.Дата МЕЖДУ &датаНач И &датаКон И РТиУ.СуммаДокумента<=&максСумма
    |УПОРЯДОЧИТЬ ПО Сумма УБЫВ";
    Запрос.УстановитьПараметр("датаНач", Объект.датаНач);
    Запрос.УстановитьПараметр("датаКон", Объект.датаКон);
    Запрос.УстановитьПараметр("максСумма", Объект.ТребуемаяСумма);
    минВнеЛимитаРТиУ = НЕОПРЕДЕЛЕНО; суммаМинРТиУ = 0; // для не вошедшего документа с минимальной суммой
    подобрано = 0;
    выборка = Запрос.Выполнить().Выбрать();
    ПОКА выборка.Следующий() Цикл
        Если подобрано+выборка.Сумма<Объект.ТребуемаяСумма Тогда
            нов = Объект.Документы.Добавить();
            нов.РТиУ = выборка.Ссылка;
            нов.Сумма = выборка.Сумма;
            подобрано = подобрано+выборка.Сумма;
        ИначеЕсли (минВнеЛимитаРТиУ = НЕОПРЕДЕЛЕНО) ИЛИ (суммаМинРТиУ>выборка.Сумма) Тогда
            минВнеЛимитаРТиУ = выборка.Ссылка;
            суммаМинРТиУ = выборка.Сумма;
        КонецЕсли;
    КонецЦикла;
    // проверяем, не нужно ли включить не вошедший документ с минимальной суммой
    Если минВнеЛимитаРТиУ <> НЕОПРЕДЕЛЕНО И (подобрано+суммаМинРТиУ-Объект.ТребуемаяСумма<Объект.ТребуемаяСумма-подобрано) Тогда
        нов = Объект.Документы.Добавить();
        нов.РТиУ = минВнеЛимитаРТиУ;
        нов.Сумма = суммаМинРТиУ;
        подобрано = подобрано+суммаМинРТиУ;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура Подобрать(Команда)
    ПодобратьНаСервере();
КонецПроцедуры

Могу выслать обработку (мое мыло в профиле).
53 Pepeega
 
02.04.21
09:50
(52) Спасибо за пример, помог в написании!