Решение задачи о наполняемости ранца с помощью запросов.

26.04.13

Разработка - Запросы

Представляю Вашему вниманию алгоритм перебора вариантов «наполняемости ранца» с помощью запросов. При определенных условиях он работает быстрее чем обычный рекурсивный перебор.

Немного предыстории. Организация периодически проводит маркетинговую акцию в розничной торговле. На какую сумму товара пробито в чеке, на столько же можно выбрать еще «подарков». По факту часть товара в чеке ставится со 100% скидкой. И эта часть товара должна быть максимально приближена к половине общей суммы (без скидок), но не превышать ее.

Т.о. приходится решать задачу о наполняемости рюкзака, причем за максимально короткое время. Существующий алгоритм перебора с помощью рекурсии был оптимизирован более чем. Я решил попробовать принципиально другой подход.

Сначала объясню в теории. Весь товар разбивается по количеству на несколько строк. То есть в каждой строке «Количество = 1». В итоге есть массив сумм построчно X1…Xn. Для тех, кто знаком с комбинаторикой понятно, что количество вариантов для перебора равно 2 в степени N.

Формируем таблицу (матрицу) размерности N столбцов, 2 в степени N строк, состоящую из 0 и 1, которая по сути представляет все возможные варианты.

00..00

00..01

…….

11..01

11..11

Далее производим «перемножение» массива X1…Xn и данной матрицы по алгебраическим правилам. В итоге получаем массив Y(1)…Y(2 в степени N) результатов, из которых просто необходимо выбрать наиболее подходящий вариант. Тот, который наиболее близок к некому пороговому значению P, но не превышает его.

Теперь собственно как это реализовать с помощью запросов. Таблицу из 0 и 1 я несколько модифицировал. В итоге получил таблицу из 3 столбцов и N*(2 в степени N) строк, вида

Ai | Bj | (0 или 1)

Где Ai – номер строки в исходной таблице, Bj – номер столбца в исходной таблице, (0 или 1) – соответствующее значение.Все таблицы с N = 2 по (некое приемлемое значение, при котором расчет не зависает окончательно) я буферизовал в базе. Т.о. время на их составление не тратится. Эти таблицы передаются как параметр в запрос.

Массив  X1…Xn представляем в виде таблицы из 2 колонок:

1 | X1

2 | X2

N | Xn

И тоже передаем в запрос. В запросе происходит соединение по равенству Bj (для первой таблицы)  и первой колонки второй таблицы. В итоге получаем таблицу из 4 колонок вида:

Ai | Bj | (0 или 1) | Xj

Перемножаем 3 и 4 колонку. Получаем таблицу вида:

Ai | Bj | (0 или Xj)

Далее группируем по Ai, суммируя по третьей колонке. Получаем:

Ai | ∑ (0 или Xj)  То есть таблицу из двух колонок вида Ai | Ci.

Далее элементарно отбор по условию Ci

На выходе получаем Ai – это номер строки в нашей исходной таблице 0 и 1, который соответствует нужному варианту.

 

Теперь собственно о плохом. Как показали замеры производительности, на файловой базе подобный способ безбожно проигрывает рекурсивному перебору вех вариантов. Поэтому он так и не использовался. На SQL проявляет себя очень неплохо.

Ну и основная проблема – большие значения N. Я не проверял, как будет отрабатывать на больших числах, т.к. такой необходимости не было. Но что-то мне подсказывает,  что для N больше 30, например, таблицу в миллиард записей SQL если и обработает, то за не очень приемлемое время.

 

 

p.s. По просьбе трудящихся выкладываю пример запроса.

Каждый шаг вынес в отдельный запрос пакета, чтобы было понятнее.

ТЗ1 = ПолучитьТаблицу0и1РазмерностиN(Н); //Таблица из 3 колонок вида  Ai | Bj | (0 или 1)
ТЗ2 = ПолучитьТаблицуДляМассиваЗначений(); //Таблица из 2 колонок вида  i | Xi

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|    Тз1.А,
|    Тз1.В,
|    Тз1.Х
|ПОМЕСТИТЬ Тз1
|ИЗ
|    &Тз1 КАК Тз1
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    Тз2.Н,
|    Тз2.ХН
|ПОМЕСТИТЬ Тз2
|ИЗ
|    &Тз2 КАК Тз2
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    Тз1.А,
|    Тз1.В,
|    Тз1.Х * Тз2.ХН КАК Х
|ПОМЕСТИТЬ НеСгруппированно
|ИЗ
|    Тз1 КАК Тз1
|        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Тз2 КАК Тз2
|        ПО Тз1.В = Тз2.Н
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    НеСгрупированно.А,
|    СУММА(НеСгрупированно.Х) КАК Х
|ПОМЕСТИТЬ Сгруппировано
|ИЗ
|    НеСгруппированно КАК НеСгрупированно
|
|СГРУППИРОВАТЬ ПО
|    НеСгрупированно.А
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ ПЕРВЫЕ 1
|    Сгруппировано.А
|ИЗ
|    Сгруппировано КАК Сгруппировано
|ГДЕ
|    Сгруппировано.Х |
|УПОРЯДОЧИТЬ ПО
|    Сгруппировано.Х УБЫВ";

Запрос.УстановитьПараметр("ТЗ1",ТЗ1 );
Запрос.УстановитьПараметр("ТЗ2",ТЗ2 );
Запрос.УстановитьПараметр("Р",НекоеПороговоеЗначение);


Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();

Если Выборка.Следующий() тогда
    ОптимальнаяСтрокаИз0и1 = ПолучитьСтроку0и1ИзТаблицыПоНомеру(Выборка.А);
КонецЕсли;

 

 

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    124936    682    389    

732

Пропорциональное распределение в запросе с использованием АвтоНомерЗаписи()

Запросы Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    2247    andrey_sag    10    

28

Для чего используют конструкцию запроса "ГДЕ ЛОЖЬ" в СКД на примере конфигурации 1С:ERP

Запросы СКД Платформа 1С v8.3 Запросы Система компоновки данных 1С:ERP Управление предприятием 2 Бесплатно (free)

В типовых конфигурациях разработчики компании 1С иногда используют в отчетах, построенных на СКД, такую конструкцию, как "ГДЕ ЛОЖЬ". Такая конструкция говорит о том, что данные в запросе не будут получены совсем. Для чего же нужен тогда запрос?

13.02.2024    6006    KawaNoNeko    23    

25

Набор-объект для СКД по тексту или запросу

Запросы СКД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Есть список полей в виде текста, или запрос - закидываем в набор СКД.

1 стартмани

31.01.2024    2149    2    Yashazz    0    

31

Запрос 1С copilot

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Пишем на человеческом языке, что нам надо, и получаем текст запроса на языке 1С. Используются большие языковые модели (LLM GPT) от OpenAI или Яндекс на выбор.

5 стартмани

15.01.2024    6641    31    mkalimulin    27    

51

PrintWizard: поддержка представлений ЗУП в конструкторе

Инструментарий разработчика Запросы Платформа 1С v8.3 Бесплатно (free)

Одной из интересных задач, стоящих в процессе разработки, была поддержка механизма представлений в ЗУП. Но не просто возможность исполнения запросов с ними. Основная проблема была в том, чтобы с ними было удобно работать, а именно: создавать, модифицировать и отлаживать. Кратко о том, что в итоге получилось...

14.12.2023    1880    vandalsvq    7    

29

Объектная модель запроса "Схема запроса" 2

Запросы Платформа 1С v8.3 Запросы Конфигурации 1cv8 Бесплатно (free)

Далеко уже не новый тип данных "Схема запроса". Статья о том, как использовать его "попроще". Примеры создания текста запроса с нуля и изменение имеющегося запроса.

06.12.2023    5626    user1923546    26    

46

Начните уже использовать хранилище запросов

HighLoad оптимизация Запросы

Очень немногие из тех, кто занимается поддержкой MS SQL, работают с хранилищем запросов. А ведь хранилище запросов – это очень удобный, мощный и, главное, бесплатный инструмент, позволяющий быстро найти и локализовать проблему производительности и потребления ресурсов запросами. В статье расскажем о том, как использовать хранилище запросов в MS SQL и какие плюсы и минусы у него есть.

11.10.2023    16593    skovpin_sa    14    

101
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. tolyan_ekb 104 26.04.13 05:44 Сейчас в теме
Кому полезна данная статья? В ней даже примера кода нет для дальнейшего использованиея )) В чем преимущества/недостатки перед описаными алгоритмами в вики?
+
2. Alex_grem 256 26.04.13 09:25 Сейчас в теме
Текст запроса в студию!
+
3. serg17 26 26.04.13 09:34 Сейчас в теме
В статье описывается не какой то "новый" алгоритм. Это полный перебор в чистом виде. Просто немного необычная программная реализация. Я пробовал его реализовать таким образом у одного клиента. Но, как я уже писал, там была необходимость работы в файловом режиме. Поэтому "не пригодилось". И поэтому я не проводил полномасштабных исследований с замерами производительности.
По поводу отсутствия примера кода - согласен, это недостаток. Просто примера уже реализованного кода не сохранилось. А написать снова - банальная нехватка времени. Может быть добавлю чуть позже.
На данный момент просьба рассматривать статью как некую "абстрактную" идею :)
+
4. ildarovich 7861 26.04.13 11:11 Сейчас в теме
Просьба пояснить исходную задачу: чем определяется число N? - Это число позиций в чеке? - Число позиций в магазине? - Задача решается "за покупателя"? - Какова была идея рекурсивного алгоритма? - Использовался ли там метод ветвей и границ?
+
5. serg17 26 26.04.13 12:19 Сейчас в теме
Пожалуй задача в чистом виде отличается от классической задачи о наполнении ранца.
Число N - суммарное количество позиций в документе.
Т.е. изначально весь товар разбивался на строки с количеством = 1. Но с произвольной суммой.
Далее определялось, на какие строки ставить скидку 100%.

Да, задача решается "за покупателя".
Рекурсивный алгоритм был написан еще до меня.
Насколько я помню, это был алгоритм полного перебора, по следующему принципу:

На текущую строку ставится ли скидка 100% Да | Нет - считается общая сумма.
Соответственно вызов рекурсии для следующей строки для каждого из 2 вариантов.
Если накопленная сумма превысила некое пороговое значение, значит далее рекурсия не идет.
Также было ограничение, если по какому то варианту сумма стала РАВНА пороговому значению, перебор заканчивался.

Решайте сами, можно ли данный способ отнести к методу ветвей и границ.
+
6. ildarovich 7861 26.04.13 12:52 Сейчас в теме
(5) Мне непонятен экономический смысл данной задачи. Что я пока понял: покупатель взял в торговом зале N нужных ему товаров. Каждый в количестве 1, причем могут быть одинаковые. У каждого товара есть фиксированная цена. По условиям акции он может заплатить примерно половину их суммарной стоимости по ценнику. Считается общая стоимость, а затем товар делится на две как можно более равные по стоимости группы. За одну группу в чеке указывается полная стоимость, вторая показывается как бесплатная - со скидкой 100%.
А в чем смысл всего этого? Зачем это делать? - Не проще ли дать скидку 50% на весь товар? Подтолкнуть покупателя брать всего по два? - Тогда скидка будет максимальной. Странно решать оптимизационную задачу (перебирать миллиард вариантов) после того как покупатель уже выбрал товар и ничего не может изменить. Нет обратной связи. Бессмысленная задача. Математически интересная, но бессмысленная. Не стоило заниматься. Или я не прав? Думаю проблема в том, что заказчик где-то слышал про подобную акцию, но неправильно понял как она правильно проводится.
bulpi; +1
13. Prometeus2011 211 12.08.15 17:40 Сейчас в теме
(6) ildarovich, Смысл скрывается в контроле прибыли по итогам закрытия квартала. Например, есть организация, которая продает мелочь всякую (в России еще качают, правда, но эта отдельная организация - продает). Вот в конце квартала надо этой организации показать определенную прибыль на счете 90.9. Причем, прибыль должна быть заданной величины........... В общем, для этого и применяется заполнение документов.... причем номенклатурное соответствие документов бух. учета и оперативного учета, как это сказать по-мягче... не является условием ТЗ Даже наоборот.
+
7. serg17 26 26.04.13 13:27 Сейчас в теме
Это если рассматривать задачу с технической точки зрения.
А для покупателя условия акции звучали примерно так:
Если вы покупаете товара БОЛЬШЕ чем на 10 000, то вы можете выбрать подарков ЕЩЕ на столько же.
Но не более половины чека.
Т.е. если купили на 9000 - подарков нет.
Если на 11000 - могут набрать еще подарков на 11000. Т.е чек без скидок 22000, но платят покупатели 11000.
+
8. ildarovich 7861 26.04.13 14:08 Сейчас в теме
(7) Теперь понятнее. Но все же кажется логичным отдать расчеты покупателю: он раскладывает покупки на две кучки: это больше 10000 для получения подарков, а это - не более суммы первой "кучки" - сами подарки. Так что остаюсь пока при своем мнении: эта задача - ошибка заказчика. А жаль - очень интересуют оптимизационные задачи применительно к 1С.
По сути Вашего решения могу сказать, что таблица вариантов необходима всего одна. В ней должны быть строки "00000...01", "00000...10". Эти строки должны быть отсортированы по числу единиц. В запросе должно быть Выбрать первые 1. Сортировка - долгая операция, поэтому, хотя таблицу легко динамически построить методом Порождающего запроса, ее действительно лучше предпостроить и хранить. Условие соединения таблиц можно записать по примеру из статьи "Выразить строку как число и строку как дату в запросе", только вместо веса позиции будет цена товара. Файловая версия и SQL не должны сильно различаться по скорости решения - здесь в любом случае идет сканирование индекса.
Но этот подход, хотя и улучит Ваше решение, будет работать только до примерно 20-ти позиций (как и рекурсия). Так что нужны другие подходы.
+
10. serg17 26 26.04.13 18:51 Сейчас в теме
(8) ildarovich,
Как раз покупателю отдавать расчеты не вариант. Придет большой лысый дядечка. Наберет товара на 30000. Об акции узнает только на кассе обрадуется. И спросит "Так сколько с меня денег". Или другой случай. Покупатель (или продавец) как то посчитал. А потом придя домой и внимательно глядя на чек увидел другой вариант раскидывания "подарков", при котором он бы заплатил меньше денег. Нет рассчитывать надо по возможности на автомате, причем наилучшим способом. Поэтому задача и имеет вполне прикладную часть(to 9).
На самом деле при N больше 14 время на расчет уходило "непозволительно" много. Больше минуты точно. А при большой очереди это не вариант. В таком случае расчет "перекладывался" на продавца. С учетом того, что стоимость одной номенклатурной позиции была от 1500 - вполне всех все устраивало.
Сортировка по числу единиц - очень хорошая идея. Согласен.
На счет файловой версии и SQL... На 100% утверждать не могу т.к. не проверял. Но что то мне не верится что файловая СУБД соединяет таблицы в многие миллионы записей с той же скоростью что и SQL.
Соединения таблиц по приведенному примеру с ходу не понял, а долго разбираться как то лень если честно.
+
9. petrov_al 10 26.04.13 17:31 Сейчас в теме
Опять математические изыскания...господа пожалуйста будьте ближе к реалиям нашей жизни. Хотя вроде интересно, но блин нет времени этим заниматься работы валом.
+
11. Zebar 69 30.04.13 15:49 Сейчас в теме
Собственно, а почему бы не просто предоставить скидку 50% на все товары?
bulpi; +1
12. serg17 26 30.04.13 20:22 Сейчас в теме
Потому что условия акции оговорены так:
"Если вы покупаете товара БОЛЬШЕ чем на 10 000, то вы можете выбрать подарков ЕЩЕ на столько же."
Этот же текст в массовой СМС рассылке покупателям.
Такой маркетинговый ход. И требование было именно выделять в чеке подарки путем 100%ной скидки.
На самом деле здесь игра на психологии покупателя.
И т.к. подобные акции проводятся не первый год - думаю результатом они довольны.
+
Оставьте свое сообщение