Доброго времени суток! Имеется ТаблицаЗначений со списком товаров, которые имеют колонки с суммами и количеством, нужно разбить эту таблицу на указанное количество ТаблицЗначений которые бы имели приблизительно одинаковые суммы. Хотел быстро справиться, делал "влоб", но погрешность может быть неприятно большая. Товар абсолютно разный, с разным количеством товара и соответственно суммами, нужно "насобирать" из всего списка.
Спасибо!
---
Согласен, поторопился с выводом. Не учитывал изменения в основной ТЗ. Позаимствовал у вас несколько строк. Проверку прошла заказчиком, так что все работает

```1c
    Счетчик = 1;
    СредняяСумма = Окр(ИтогоСуммаРН() / КоличествоРН,2);
    Пока Счетчик <= КоличествоРН Цикл
    НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

    ВключаемыеТовары = Товары.НайтиСтроки(Новый Структура("Включать", Истина));
    Для Каждого Строка Из ВключаемыеТовары Цикл
    //Если Строка.Включать Тогда

    КоличествоВТЧ = Строка.КоличествоРН;
    СуммаРН = Строка.СуммаРН;
    Если НовыйДокумент.Товары.Итог("Сумма") + СуммаРН > СредняяСумма Тогда
    КоличествоВТЧ = Строка.КоличествоРН;
    Пока НЕ НовыйДокумент.Товары.Итог("Сумма") + СуммаРН <= СредняяСумма Цикл
    СуммаРН = Окр(Строка.ЦенаРН * КоличествоВТЧ,2);
    КоличествоВТЧ = КоличествоВТЧ - 1;
    СтрокаНайдена = Истина;
    Если КоличествоВТЧ = 0 Тогда
    СтрокаНайдена = Ложь;
    Прервать;
    КонецЕсли;
    КонецЦикла;
    Если НЕ СтрокаНайдена Тогда
    Продолжить;
    КонецЕсли;
    КонецЕсли;

    Строка.КоличествоРН = Строка.КоличествоРН - КоличествоВТЧ;
    Строка.СуммаРН = Строка.ЦенаРН * Строка.КоличествоРН;
    Строка.Включать = (Строка.КоличествоРН > 0);

    НоваяСтрока = НовыйДокумент.Товары.Добавить();
    НоваяСтрока.Номенклатура= Строка.Номенклатура;
    НоваяСтрока.Цена = Строка.ЦенаРН;
    НоваяСтрока.Количество = КоличествоВТЧ;
    НоваяСтрока.КоличествоМест = КоличествоВТЧ;
    НоваяСтрока.Сумма = СуммаРН;
```
---
Показывайте, как делали.
---
Не использовал никаких коэффициентов распределения, просто проходил по ТаблицеЗначений и собирал товар, если перебор товар возвращал, есть мысль если перебор по какому-то товару проходится сначала по главной Таблице и искать максимально подходящую сумму, но объем кода от этого значительно увеличивается

```1c
    Счетчик = 1;
    СредняяСумма = Цел(ИтогоСуммаРН() / КоличествоРН);
    Пока Счетчик <= КоличествоРН Цикл
    НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

    Для Каждого Строка Из Товары Цикл
    Если Строка.Включать Тогда

    НоваяСтрока = НовыйДокумент.Товары.Добавить();
    НоваяСтрока.Номенклатура= Строка.Номенклатура;
    НоваяСтрока.Цена = Строка.ЦенаРН;
    НоваяСтрока.Количество = Строка.КоличествоРН;
    НоваяСтрока.КоличествоМест = Строка.КоличествоРН;
    НоваяСтрока.Сумма = Строка.СуммаРН;

    Строка.Включать = Ложь;
    Если НовыйДокумент.Товары.Итог("Сумма") > СредняяСумма Тогда
    Строка.КоличествоРН = 0;
    Пока НЕ НовыйДокумент.Товары.Итог("Сумма") <= СредняяСумма Цикл
    НоваяСтрока.Количество = Строка.КоличествоРН - 1;
    НоваяСтрока.КоличествоМест = Строка.КоличествоРН - 1;
    НоваяСтрока.Сумма = Окр(Строка.ЦенаРН * НоваяСтрока.Количество,2);
    Строка.КоличествоРН = Строка.КоличествоРН + 1;
    Если НоваяСтрока.Количество = 0 Тогда
    НовыйДокумент.Товары.Удалить(НоваяСтрока);
    Строка.Включать = Истина;
    Прервать;
    КонецЕсли;
    КонецЦикла;
    Прервать;
    КонецЕсли;
```
---
1. Ну, с весовым товаром сразу можете пролететь исходя из этого алгоритма...
2. При обратном "изъятии" товара из документа в исходную ТЗ вы увеличиваете количество, начиная от нуля, но при этом сумма в ТЗ у вас остается неизменной. И скорее всего в следующий документ пойдет этот товар с уменьшенным количеством и несоответствующей суммой.
3. Надо сначала целиком проверять сумму строки ТЗ, вычислить "возможное превышение", и только тогда решать - отправлять строку в документ целиком, или разделять количество.
4. Не надо каждый раз делать полный цикл по ТЗ, можно воспользоваться отбором по колонке "Включать"...

Это навскидку чисто "по математике"...
---
2. Сумму нет смысла возвращать так как из первоначальной ТЗ сумма не изымается в строке

```1c
    Строка.КоличествоРН = 0;
```

я специально обнуляю количество потому что я не знаю сколько нужно вернуть
3. Возможное превышение я проверяю строкой

```1c
    Пока НЕ НовыйДокумент.Товары.Итог("Сумма") <= СредняяСумма
```

Но тут проверяется только последняя строка, которая попала в документ реализации и если она превышает при любом количестве товара, то я строку удаляю

```1c
    Если НоваяСтрока.Количество = 0 Тогда
    НовыйДокумент.Товары.Удалить(НоваяСтрока);
```

4. Я и так работаю только со строками которые имеют статус "Включать"

```1c
    Если Строка.Включать Тогда
```
---
Сумму нет смысла возвращать так как из первоначальной ТЗ сумма не изымается в строке Есть, потому что в следующей итерации у тебя
```1c
    НоваяСтрока.Сумма = Строка.СуммаРН;
```
 а не
```1c
    НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
```

4. Я и так работаю только со строками которые имеют статус "Включать"
То есть если из миллиона строк ТЗ ты уже отобрал 999999, то ты все равно будешь перебирать в цикле весь миллион строк, пока не найдешь ту единственную оставшуюся?
Я бы вообще в принципе даже не флаг веша Включать/Исключать, а удалял бы нафиг вообще строку из таблицы. Тогда перебирай сколько душе угодно безо всяких отборов.
---
Если я строку использовал, все количество списал, то ставлю флаг

```1c
    Строка.Включать = Ложь;
```
---
Ну и? У тебя 999998 Флагов включать и ты все равно будешь делать миллион циклов и миллион раз проверять флаг, чтобы наконец найти те два бедных выключенных флага?
Ты меня не слышишь?
---
С удаление строк, я в принципе согласен, будет проще, но у меня нет таких объемов данных чтобы у меня было 999999 строк, там 100-500 строк, проверить условие "Если" не доставит проблем. Да и проблема не в эффективности сейчас)
---
Моё распределение работает, но очень легко может получится погрешность, я хочу его максимально минимизировать
---
Ну как вариант, добавить доп. проверку.
Перед добавлением в новую таблицу сначала проверять - если добавление из текущей строки превысит среднюю сумму, то фиксировать на сколько. Дальше обойти таблицу, чтобы поискать товар, добавление которого (вместо исходного) позволяет уменьшить сумму превышения.

Но тоже стоит понимать, что это не всегда реально.
Если в ТЧ всего 2 товара по 1 шт каждый, и первый стоит млн, а второй 10р, то...
---
Я бы отсортировал исходную ТЗ по суммам и заполнял бы сразу несколько документов ;-) Добавлял бы текущую строку в тот документ, в котором после добавления этой строки будет наименьшая сумма (за исключением нуля)... Таким образом меньше итераций получится. И скорее всего можно даже не разбивать строку с одним товаром на несколько документов...
---
Я в своем рассуждении эту мысль описал, но надеялся, что есть более изящное решение, чем по несколько раз бегать по ТЗ.
Понимаю что не всегда, но меня интересует лишь постараться минимизировать отклонение. То есть сделать всё возможно
---
В чем выражается погрешность?
---
"На указанное" может не всегда получиться... Но - да, интересен будет ваш подход. Я-то свой в голове уже накрутил...
---
Если кому вдруг понадобится. Решение не будет таким хорошим на больших объемах данных потому что приходится бегать по всей таблице каждый раз, но это нетрудно подкорректировать, мне это не нужно. Да решение довольно тривиальное, но погрешность хоть как-то, но минимизируется

```1c
    Счетчик = 1;
    СредняяСумма = Окр(ИтогоСуммаРН() / КоличествоРН,2);
    Пока Счетчик <= КоличествоРН Цикл
    НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

    Для Каждого Строка Из Товары Цикл
    Если Строка.Включать Тогда

    Количество = Строка.КоличествоРН;
    Сумма = Строка.СуммаРН;
    Если НовыйДокумент.Товары.Итог("Сумма") + Строка.СуммаРН > СредняяСумма Тогда
    Количество = Строка.КоличествоРН;
    Пока НЕ НовыйДокумент.Товары.Итог("Сумма") + Сумма <= СредняяСумма Цикл
    Сумма = Окр(Строка.ЦенаРН * Количество,2);
    Количество = Количество - 1;
    СтрокаНайдена = Истина;
    Если Количество = 0 Тогда
    СтрокаНайдена = Ложь;
    Прервать;
    КонецЕсли;
    КонецЦикла;
    Если НЕ СтрокаНайдена Тогда
    Продолжить;
    КонецЕсли;
    КонецЕсли;

    Строка.Включать = Ложь;

    НоваяСтрока = НовыйДокумент.Товары.Добавить();
    НоваяСтрока.Номенклатура= Строка.Номенклатура;
    НоваяСтрока.Цена = Строка.ЦенаРН;
    НоваяСтрока.Количество = Количество;
    НоваяСтрока.КоличествоМест = Количество;
    НоваяСтрока.Сумма = Сумма;
```
---
Сумма = Окр(Строка.ЦенаРН * Количество,2);
                            Количество = Количество - 1; Ошибка.
---
```1c
    Количество = Строка.КоличествоРН;
    Сумма = Строка.СуммаРН;
    Если НовыйДокумент.Товары.Итог("Сумма") + Строка.СуммаРН > СредняяСумма Тогда
    Количество = Строка.КоличествоРН;
    Пока НЕ НовыйДокумент.Товары.Итог("Сумма") + Сумма <= СредняяСумма Цикл
    Сумма = Окр(Строка.ЦенаРН * Количество,2);
    Количество = Количество - 1;
    СтрокаНайдена = Истина;
    Если Количество = 0 Тогда
    СтрокаНайдена = Ложь;
    Прервать;
    КонецЕсли;
    КонецЦикла;
    Если НЕ СтрокаНайдена Тогда
    Продолжить;
    КонецЕсли;
    КонецЕсли;
```
 Вообще весь кусок насквозь ошибочен...
---
```1c
    ВключаемыеТовары = Товары.НайтиСтроки(Новый Структура("Включать", Истина));

    Для Каждого СтрокаТЗ Из ВключаемыеТовары Цикл

    ОбщаяСуммаДокумента = НовыйДокумент.Товары.Итог("Сумма");

    Если ОбщаяСуммаДокумента + СтрокаТЗ.ЦенаРН > СредняяСумма Тогда
    Продолжить;
    КонецЕсли;

    ТекущееКоличество = СтрокаТЗ.КоличествоРН;
    ТекущаяСумма = СтрокаТЗ.СуммаРН;
    ТекущаяСуммаДокумента = ОбщаяСуммаДокумента + ТекущаяСумма;

    Пока ТекущаяСуммаДокумента > СредняяСумма Цикл
    ТекущееКоличество = ТекущееКоличество - 1;
    ТекущаяСумма = ТекущееКоличество * СтрокаТЗ.ЦенаРН;
    ТекущаяСуммаДокумента = ОбщаяСуммаДокумента + ТекущаяСумма;
    КонецЦикла;

    НоваяСтрока = НовыйДокумент.Товары.Добавить();
    НоваяСтрока.Номенклатура = СтрокаТЗ.Номенклатура;
    НоваяСтрока.Цена = СтрокаТЗ.ЦенаРН;
    НоваяСтрока.Количество = ТекущееКоличество;
    НоваяСтрока.КоличествоМест = ТекущееКоличество;
    НоваяСтрока.Сумма = ТекущаяСумма;

    СтрокаТЗ.КоличествоРН = СтрокаТЗ.КоличествоРН - ТекущееКоличество;
    СтрокаТЗ.СуммаРН = СтрокаТЗ.КоличествоРН * СтрокаТЗ.ЦенаРН;
    СтрокаТЗ.Включать = (СтрокаТЗ.КоличествоРН > 0);

    КонецЦикла;
```

Только для целочисленного количества, конечно же.
---
Согласен, поторопился с выводом. Не учитывал изменения в основной ТЗ. Позаимствовал у вас несколько строк. Проверку прошла заказчиком, так что все работает

```1c
    Счетчик = 1;
    СредняяСумма = Окр(ИтогоСуммаРН() / КоличествоРН,2);
    Пока Счетчик <= КоличествоРН Цикл
    НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

    ВключаемыеТовары = Товары.НайтиСтроки(Новый Структура("Включать", Истина));
    Для Каждого Строка Из ВключаемыеТовары Цикл
    //Если Строка.Включать Тогда

    КоличествоВТЧ = Строка.КоличествоРН;
    СуммаРН = Строка.СуммаРН;
    Если НовыйДокумент.Товары.Итог("Сумма") + СуммаРН > СредняяСумма Тогда
    КоличествоВТЧ = Строка.КоличествоРН;
    Пока НЕ НовыйДокумент.Товары.Итог("Сумма") + СуммаРН <= СредняяСумма Цикл
    СуммаРН = Окр(Строка.ЦенаРН * КоличествоВТЧ,2);
    КоличествоВТЧ = КоличествоВТЧ - 1;
    СтрокаНайдена = Истина;
    Если КоличествоВТЧ = 0 Тогда
    СтрокаНайдена = Ложь;
    Прервать;
    КонецЕсли;
    КонецЦикла;
    Если НЕ СтрокаНайдена Тогда
    Продолжить;
    КонецЕсли;
    КонецЕсли;

    Строка.КоличествоРН = Строка.КоличествоРН - КоличествоВТЧ;
    Строка.СуммаРН = Строка.ЦенаРН * Строка.КоличествоРН;
    Строка.Включать = (Строка.КоличествоРН > 0);

    НоваяСтрока = НовыйДокумент.Товары.Добавить();
    НоваяСтрока.Номенклатура= Строка.Номенклатура;
    НоваяСтрока.Цена = Строка.ЦенаРН;
    НоваяСтрока.Количество = КоличествоВТЧ;
    НоваяСтрока.КоличествоМест = КоличествоВТЧ;
    НоваяСтрока.Сумма = СуммаРН;
```
---
Товар штучный или весовой или и то и другое?
---
Только штучный
---
погрешность всегда будет... не будете же разбивать 1 шт на части
---
Вообще задача имеет конечное решение. "Погрешность", а точнее степень отклонения от идеального распределения, когда везде поровну, можно оценить через среднеквадратическое отклонение по Сумме.
---
есть хорошая статья по вашей теме: "Жадина с рюкзаком, или немножко о поиске лучшей жизни"

 https://infostart.ru/1c/articles/437890/
---
О! Оказывается, для моего метода и термин даже есть...