Вход | Регистрация
    1  2   
1С:Предприятие :: 1С:Предприятие 8 общая

Помогите с алгоритмом распределения

Помогите с алгоритмом распределения
Я
   Документовед
 
10.12.20 - 16:05
Допустим есть две таблиц ТЗ в одной есть суммовая колонка в виде базы, в другой суммировавшая колонка для распределния и сколько было распределено по этой строчке.

Есть третья тз куда в суммовую колонку пишется результат распределния.

Все суммы точностью до 2х знаков.

Допустим мы распределяем, округляем. Однако естественно в результате в таблицу попадает не вся сумма. Для упрощения Округляем всегда вниз:
ПредвСуммаРаспред = стрТЗКсраспред.Сумма * стрТЗ.СуммаБаза/тзБаза.Итог("СуммаБаза");
СуммаРаспред = Цел( ПредвСумма   * 100)/100

В результате набегают копейки отклонений.

Вопрос как распределить эти копейки?

Кинуть на строчку с самой большой базой - слишком много приходится.

Если перебирать все строки базы и по очереди раскидывать копейки, то на строки с маленькой базой могут придется слишком много.
   Dmitry1c
 
1 - 10.12.20 - 16:08
(0) глобальный поиск "РаспределитьПропорционально".

готовая функция есть для этого в прикладных конфах
   Документовед
 
2 - 10.12.20 - 16:28
(1) Там отклонения пихаются в максимальюн базу.

           Если МаксЗнач < МодульЧисла Тогда
                      МаксЗнач = МодульЧисла;
                    ИндексМакс = К;
                 КонецЕсли;
....

        Если Не РаспрСумма = ИсхСумма Тогда
                МассивСумм[ИндексМакс] = МассивСумм[ИндексМакс] + ИсхСумма - РаспрСумма;
           КонецЕсли;


Вот тут интереснее вариант: http://catalog.mista.ru/1c/articles/416217/

Но я пока не могу понять как матиматически распределяется отклонение. И еще если допустить такой гипотетический варианат, что в базе есть плюсы и минусы, и в некий момент база равна нулю.
   Chameleon1980
 
3 - 10.12.20 - 16:36
остаток распределить так же как осноау
   mistеr
 
4 - 10.12.20 - 16:36
(0) >Кинуть на строчку с самой большой базой - слишком много приходится.

"Слишком много" это сколько? Если есть критерий, то кидай то, что сверху, на следующую по величине строчку. И т.д.

Но обычно, если остается "слишком много", значит где-то в алгоритме ошибка.
   Документовед
 
5 - 10.12.20 - 16:41
(4) Если в таблице базы 100 строк, и в таблице к распределению 100 строк. За каждый проход может набраться по 0.5 копейки. Т.е. теоретически под 5000.
   fisher
 
6 - 10.12.20 - 16:44
(2) Странно. Вроде давно уже применяется "более лучший" алгоритм - запоминается погрешность округлений на текущей строке и плюсуется к следующей перед очередным округлением. Таким образом погрешность не накапливается, ибо как только она вылазит в значащие числа, то тут же корректируется на очередной строке.
   Документовед
 
7 - 10.12.20 - 22:16
(6) Любопытный подход. Есть пример кода в типовых?
   Злопчинский
 
8 - 11.12.20 - 00:08
на инфостарте есть публикация где рассматривается вариантов шесть распределения отклонений при округлениях.
   Злопчинский
 
9 - 11.12.20 - 00:10
округляй четные строки вверх, а нечетные строки вниз. при большом количестве строк - будет итог отклонений мелкий. остаток на последнюю строку.
   Bigbro
 
10 - 11.12.20 - 04:41
в простом случае делал, как в (6)
когда однажды потребовалось улучшить точность - добавлял сортировку отклонений и разбрасывал копейки округления начиная с самых искаженных результатов - в обратную сторону разумеется.
то есть есть строка где после округления ошибка +0,003 и -0,004, а остальные меньше
если по общему итогу округлений копейки в плюсе - то добавляем копейку туда где ошибка в минус, получаем отклонение +0,006
а если по общему округление в плюс - то снимаем копейку оттуда плюс, получаем по той ячейке отклонение -0,007
если добавлять в произвольные места - суммарное отклонение будет выше.
   Документовед
 
11 - 11.12.20 - 10:37
А у 1С нет какого-нибудь алгоритма?
   Bigbro
 
12 - 11.12.20 - 10:39
никакой команды типа РаспределитьОстаткиОкругления - нет, алгоритмы тебе уже привели, делай)
   Конструктор1С
 
13 - 11.12.20 - 10:40
(2) там отклонение распихивается по копейке между элементами
   Конструктор1С
 
14 - 11.12.20 - 10:41
   Конструктор1С
 
15 - 11.12.20 - 10:44
   Документовед
 
16 - 11.12.20 - 11:36
(14) (15)  Я не думаю, что тут речь про уравнения. В любом случае если у тебя база 1:1:1, а к распределению 10, то в любом случае будет 3.33, 3.33, 3.34 - и кто из них будет  "3.34 " - определится случайно.
   fisher
 
17 - 11.12.20 - 11:40
(7) Ну, вот это я из какой-то типовой выдирал:
// Выполняет округление числовых значений с накоплением погрешностей округления, образовавшихся

//        в результате предыдущих вызовов функции
//

// Параметры
//  Число         – Число. Округляемое значение

//  Точность    – Число. Точность округления
//    Погрешность    - Число. Переменная, в которой накапливается погрешность с предыдущих вызовов

//
// Возвращаемое значение:

//   Число   – округленное значение
//

Функция ОкруглитьСУчетомПогрешности(Число, Точность, Погрешность = 0, 
                   СоответствиеПогрешностей = Неопределено, Ключ = Неопределено) Экспорт

    Если НЕ СоответствиеПогрешностей = Неопределено И ЗначениеЗаполнено(Ключ) Тогда
    
        // считываем погрешность округления, накопленную ранее при расчетах

        Погрешность = СоответствиеПогрешностей[Ключ];
        // погрешности округления еще нет -- первая сумма

        Если Погрешность = Неопределено Тогда
            Погрешность = 0;
        КонецЕсли;
        // округлим с учетом погрешности

        Округленное = ОкруглитьСУчетомПогрешности(Число, Точность, Погрешность);
        // сохраним погрешность округления

        СоответствиеПогрешностей.Вставить(Ключ, Погрешность);
    
    Иначе
        
        Если Число = 0 Тогда
            Возврат 0;
        КонецЕсли; 
    
        // выравнивание разрядности

        Число = Окр(Число, 27, ?(Число<0, РежимОкругления.Окр15как10, РежимОкругления.Окр15как20));
        
        // сумма с учетом погрешности предыдущих вычислений

        Округляемое = Число + Погрешность;

        // для отрицательного числа меняем направление округления, чтобы избежать ошибки Окр(-0.5) = -1

        Округленное    = Окр(Округляемое, Точность, ?(Округляемое<0, РежимОкругления.Окр15как10, РежимОкругления.Окр15как20));
        
        // рассчитаем новую погрешность округления

        Погрешность    = Округляемое - Округленное;
        
    КонецЕсли;
    
    Возврат Округленное;

КонецФункции// ОкруглитьСУчетомПогрешности()
   Homer
 
18 - 11.12.20 - 11:42
(7) В УПП страховые взносы так работали раньше. сейчас не знаю
   fisher
 
19 - 11.12.20 - 11:43
(17) + Тут надо до цикла распределения объявить соответствие, в котором будут накапливаться погрешности и при вызове передавать на него ссылку и указывать ключ ресурса (ибо их может же быть несколько одновременно в одном цикле распределения). А иначе будет обычное округление.
   fisher
 
20 - 11.12.20 - 11:46
Прелесть в том, что в конце цикла ничего проверять уже не нужно. Погрешность гарантирована распределена.
   fisher
 
21 - 11.12.20 - 11:46
"гарантированно"
   Bigbro
 
22 - 11.12.20 - 11:49
(16) зачем случайности.
определите для себя четкий алгоритм как должно быть и по нему распределяйте.
чтобы при любом вопросе от буха а почему вот так - было куда ткнуть носом.
   fisher
 
23 - 11.12.20 - 11:58
(10) В смысле, улучшить точность? Вроде особо некуда улучшать...
   Злопчинский
 
24 - 11.12.20 - 12:41
   RetardedToBoot
 
25 - 11.12.20 - 12:44
Во первых, нужно всегда округлять от 0.5. Тогда копейки не будут накапливаться, а ошибка строки будет в пределах 1 копейки.

Во вторых, нужно каждую распределенную сумму вычитать из распределяемой суммы, а сумму базы на которую распределилась строка из суммы общей базы. Т.е. при каждой итерации уменьшается и распределяемая сумма и сумма базы. И при последней строке распределения останется, что база-делитель будет равен базе-множителю - в последней строке не будет округлений.
   RetardedToBoot
 
26 - 11.12.20 - 12:58
При таком принципе копейки копиться не будут.

Осаждаются ли копейки в строках при больших базах или при меньших, это не правильное представление. Правильным будет: как округлять при маленьких суммах базы - никак кроме как меньше 0.5 - в меньшую, больше 0.5 в большую, так же как при больших суммах базы. Такой способ дает наибольшую правильность.

Дальше правильность можно увеличивать только за счет увеличении точности сохраняемых сумм.
   Classic
 
27 - 11.12.20 - 13:13
(17)
+1

Жаль в запросах так не получается
   Bigbro
 
28 - 11.12.20 - 16:13
(23) всегда есть куда)
поясню если докидываем к округленному наугад - то к 1,111 добавится копейка и станет 1,12 - итоговая ошибка по строке 0,009
а если раскидываем по самым отклонившимся - то в первую очередь добавится к 1,115 - итоговая ошибка по строке 0,005

ну и суммарная ошибка будет меньше. вроде мелочи, но однажды понадобилось раскидать именно так.
   Документовед
 
29 - 11.12.20 - 19:53
(17) Это в какой конфе? В УПП, ЕРП, БП, ЗУП - не нашел. Это как я понимаю только часть.
   Документовед
 
30 - 11.12.20 - 20:20
(24) Тут не совсем то что нужно.

Это пример для одной суммы, а у меня таких сумм к распределению много. Допустим все суммы к распределению одинаковые, тогда по этим алгоритмам копейки лягут, на одни и те же базы - и так столько раз сколько сумм к распределению. Т.е. будет перекос о котором писал ранее.

Допустим вот есть такие данные для задачи, с примитивным распределением:





тзБазы = Новый ТаблицаЗначений;
тзБазы.Колонки.Добавить("АналитикаБазы");
тзБазы.Колонки.Добавить("СуммаБазы");

тзКРаспределению = Новый ТаблицаЗначений;
тзКРаспределению.Колонки.Добавить("АналитикаРаспределения");
тзКРаспределению.Колонки.Добавить("СуммаКРаспределению");

тзРезульт  = Новый ТаблицаЗначений;
тзРезульт.Колонки.Добавить("АналитикаБазы");
тзРезульт.Колонки.Добавить("АналитикаРаспределения");
тзРезульт.Колонки.Добавить("СуммаРезультат");


стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х01Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х02Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х03Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х04Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х05Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х06Х"; стрТЗБазы.СуммаБазы =  95;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х07Х"; стрТЗБазы.СуммаБазы =  190;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х08Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х09Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х10Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х11Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х12Х"; стрТЗБазы.СуммаБазы =  1;

стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж01Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж02Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж03Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж04Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж05Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж06Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж07Ж"; стрТЗКрасп.СуммаКРаспределению =  1000.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж08Ж"; стрТЗКрасп.СуммаКРаспределению = 10000.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж09Ж"; стрТЗКрасп.СуммаКРаспределению =  -100.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж10Ж"; стрТЗКрасп.СуммаКРаспределению = -1000.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж11Ж"; стрТЗКрасп.СуммаКРаспределению =     0.01;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж12Ж"; стрТЗКрасп.СуммаКРаспределению =     0.03;


ИтогБазы = тзБазы.Итог("СуммаБазы");
Для Каждого стрТЗБазы из  тзБазы Цикл
    Для Каждого стрТЗКрасп из  тзКРаспределению Цикл
        стрРезульт = тзРезульт.Добавить();
        стрРезульт.АналитикаБазы = стрТЗБазы.АналитикаБазы;
        стрРезульт.АналитикаРаспределения = стрТЗКрасп.АналитикаРаспределения;
        
        стрРезульт.СуммаРезультат = Окр( стрТЗКрасп.СуммаКРаспределению * стрТЗБазы.СуммаБазы / ИтогБазы    ,2);
    КонецЦикла;    
КонецЦикла;

тзПоБазе =  тзРезульт.Скопировать();
тзПоБазе.Свернуть("АналитикаБазы", "СуммаРезультат");

Сообщить("тзКРаспределению = "+тзКРаспределению.Итог("СуммаКРаспределению")+" тзРезульт = " + тзРезульт.Итог("СуммаРезультат"));
 
 Рекламное место пустует
   Документовед
 
31 - 11.12.20 - 20:21
+З.Ы. Я конечно что-то наваял, но получилось довольно громоздко.
   Креатив
 
32 - 11.12.20 - 20:49
(0)Если не нужно, чтобы в нарастающем всё плясало, то можно пойти следующим способом.
Выделяешь в отдельную колонку дробную часть от каждого. Оставляешь в исходном только целую часть. Суммируешь, округляешь дробную часть. Получаешь к примеру к. Сортируешь дробную часть по убыванию. И сверху добавляешь единичку в к строчек.
   Bigbro
 
33 - 11.12.20 - 21:08
(32) это я в (10) написал)
   Злопчинский
 
34 - 11.12.20 - 21:08
нихера не понял что не устраивает ТС
   МимохожийОднако
 
35 - 11.12.20 - 21:12
(34) ОФФ: Дефицит общения )
   Злопчинский
 
36 - 11.12.20 - 21:13
(35) а!
   Креатив
 
37 - 11.12.20 - 21:35
(33)У тебя в (10) алгоритм какой-то неявный. Да  и не читал я.
   rudnitskij
 
38 - 11.12.20 - 22:36
(0) "Вопрос как распределить эти копейки?" - потратить несколько тысяч на оплату услуг программиста, чтобы распределить копейки? Наш подход!!!
   Документовед
 
39 - 11.12.20 - 23:05
(34) > нихера не понял что не устраивает ТС

Вот смотри есть у тебя баз 10 строк и есть распределяемые суммы 100 строк.

Вот берем первую распределяемую строку раскидываем по базе и по некоторому алгоритму кидаем копейки в некую строку.

И так 100 раз, только вот если копейки по алгоритму будут скидываться в те же строки базы (например, во всех распределяемых строках одно значение),  то по этим строкам будет задир.
   Злопчинский
 
40 - 11.12.20 - 23:24
(39) а может не надо 100 раз распределять? сначала посчитай сумму этих 100 распределяемых строк, получи одно число и раскидай его. ошибка будет минимальная...
   Документовед
 
41 - 11.12.20 - 23:41
(40) 1. Надо. Условно говоря допустим надо распределить 26 счет на 20 счет. Есть 10 номенклатурных групп по 20ке и 100 статей затрат по 26. Тебе нужно 1000 проводок вида Дт 20 номгруппа Кт 26 статья затрат.


2. Я примерно так и делаю но получается очень громоздко.
   Злопчинский
 
42 - 12.12.20 - 00:41
(41) а не кажется, что стреляешь из пушки по воробьям? сколько в итоге погрешность будет накопленная? 20%? или 0.000002%?
   RetardedToBoot
 
43 - 12.12.20 - 07:42
сумма_распределения = ХХХ;
сумма_база_всего = таблица_база.итог("сумма");
Для каждого строка_базы из таблица_база цикл
    строка_результат = таблица_результат.Добавить();
    ... заполняются поля строки результата ...
    строка_результат.сумма = окр(сумма_распределения * строка_базы.сумма / сумма_база_всего, 2);// сначала умножаем, потом делим

    сумма_распределения = сумма_распределения - строка_результат.сумма;
    сумма_база_всего = сумма_база_всего - строка_базы.сумма;
КонецЦикла;

И никаких копеек не возникает. Если распределяемых сумм много, то так для каждой суммы в отдельности.
   Документовед
 
44 - 12.12.20 - 09:21
(43) Какой будет алгоритм для таких данных?

тзБазы = Новый ТаблицаЗначений;
тзБазы.Колонки.Добавить("АналитикаБазы");
тзБазы.Колонки.Добавить("СуммаБазы");

тзКРаспределению = Новый ТаблицаЗначений;
тзКРаспределению.Колонки.Добавить("АналитикаРаспределения");
тзКРаспределению.Колонки.Добавить("СуммаКРаспределению");

тзРезульт  = Новый ТаблицаЗначений;
тзРезульт.Колонки.Добавить("АналитикаБазы");
тзРезульт.Колонки.Добавить("АналитикаРаспределения");
тзРезульт.Колонки.Добавить("СуммаРезультат");


стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х01Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х02Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х03Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х04Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х05Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х06Х"; стрТЗБазы.СуммаБазы =  95;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х07Х"; стрТЗБазы.СуммаБазы =  190;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х08Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х09Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х10Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х11Х"; стрТЗБазы.СуммаБазы =  1;
стрТЗБазы = тзБазы.Добавить(); стрТЗБазы.АналитикаБазы = "Х12Х"; стрТЗБазы.СуммаБазы =  1;

стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж01Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж02Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж03Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж04Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж05Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж06Ж"; стрТЗКрасп.СуммаКРаспределению =     1.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж07Ж"; стрТЗКрасп.СуммаКРаспределению =  1000.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж08Ж"; стрТЗКрасп.СуммаКРаспределению = 10000.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж09Ж"; стрТЗКрасп.СуммаКРаспределению =  -100.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж10Ж"; стрТЗКрасп.СуммаКРаспределению = -1000.00;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж11Ж"; стрТЗКрасп.СуммаКРаспределению =     0.01;
стрТЗКрасп = тзКРаспределению.Добавить(); стрТЗКрасп.АналитикаРаспределения = "Ж12Ж"; стрТЗКрасп.СуммаКРаспределению =     0.03;


ИтогБазы = тзБазы.Итог("СуммаБазы");
Для Каждого стрТЗБазы из  тзБазы Цикл
    Для Каждого стрТЗКрасп из  тзКРаспределению Цикл
        стрРезульт = тзРезульт.Добавить();
        стрРезульт.АналитикаБазы = стрТЗБазы.АналитикаБазы;
        стрРезульт.АналитикаРаспределения = стрТЗКрасп.АналитикаРаспределения;
        
        стрРезульт.СуммаРезультат = Окр( стрТЗКрасп.СуммаКРаспределению * стрТЗБазы.СуммаБазы / ИтогБазы    ,2);
    КонецЦикла;    
КонецЦикла;
   Документовед
 
45 - 12.12.20 - 09:23
(43) > Если распределяемых сумм много, то так для каждой суммы в отдельности.

Да но потом если сгруппировать по одной аналитике то вылезет перекос.
   RetardedToBoot
 
46 - 12.12.20 - 12:59
(44) (45) ну как я и написал, каждую сумму к распределению по отдельности. В твоем примере для этого нужно поменять местами вложенность циклов.

Копеечные перекосы будут в любом случае. Но в таком случае они минимальны.

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

А вот еще вариант, но посложней (писал без проверки и отладки):

таблица_базы.Колонки.Добавить("НакопленнаяСуммаРезультат", ОписаниеТипов("Число"));

нарастающая_сумма_распределения = 0;

Для каждого строка_распределения из таблица_к_распределению Цикл

    нарастающая_сумма_распределения = нарастающая_сумма_распределения + строка_распределения.Сумма;
    суммированная_сумма_распределения = нарастающая_сумма_распределения;

    сумма_базы_всего = таблица_базы.Итог("Сумма");
    Для каждого строка_базы из таблица_базы Цикл
    
        суммированная_сумма_результат = Окр(суммированная_сумма_распределения * строка_базы.Сумма / сумма_базы_всего, 2);
        
        сумма_результат = суммированная_сумма_результат - строка_базы.НакопленнаяСуммаРезультат;
    
        суммированная_сумма_распределения = суммированная_сумма_распределения - суммированная_сумма_результат;
        сумма_базы_всего = сумма_базы_всего - строка_базы.Сумма;
        строка_базы.НакопленнаяСуммаРезультат = суммированная_сумма_результат;
        
        строка_результат = таблица_результат.Добавить();
        строка_результат.Сумма = сумма_результат;
        строка_результат.АналитикаРаспределения = строка_распределения.Аналитика;
        строка_результат.АналитикаБазы = строка_базы.Аналитика;
        
    КонецЦикла;
КонецЦикла;
   Документовед
 
47 - 12.12.20 - 13:43
.


Я придумал критерии выполнения задачи.

Вот есть две таблицы с базой и к распределению.

Предположим что итоговая сумма по базе и итоговая сумма по по распределению совпадает.

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

Второй критерий: В результируюйщей таблице не должно быть, сумм по знаку не соответствующих.

Т.е. Если в тзБазы сумма положительная и в тзКраспределению сумма положительная  то в соответствующей строчке не может быть отрицательной суммы.
   RetardedToBoot
 
48 - 12.12.20 - 13:54
>Предположим что итоговая сумма по базе и итоговая сумма по по распределению совпадает.
>Надо сделать так, чтобы если свернуть результат по аналитике базы получились суммы из базы, а если свернуть по аналитике к распределению получилась таблица к распределению.

Не получится. Только если как случайно сойдется.

>Если в тзБазы сумма положительная
В базе не должно быть отрицательных цифр, или как минимум сумма базы должна быть положительна. Упрощенное объяснение, это если в базе 1 и -1 - итоговый делитель базы равен нулю - деление на ноль.
   RetardedToBoot
 
49 - 12.12.20 - 14:07
>Надо сделать так, чтобы если свернуть результат по аналитике базы получились суммы из базы, а если свернуть по аналитике к распределению получилась таблица к распределению.
>Не получится. Только если как случайно сойдется.

С этим я и поторопился. В моем последнем варианте в (46) именно так и получится.

Если рассмотреть последнюю итерацию строки распределения, то если она равна сумме базы, то каждое умножение и деление на базу будут приводить к числу равному сумме строки базы. А накопленной суммой считаться разница, сколько до этой суммы не хватает.
   RomanYS
 
50 - 12.12.20 - 14:26
(47) Что-то то тебя понесло куда-то в альтернативную арифметику.
Если исключить случаи когда распределяемая сумма или база равна нулю, то задача математически имеет единственное решение.

А вот дальше вступает округление, и тут уже могут быть какие-то критерий. Самым очевидным критерием является сумма модулей отклонений округленного решения от того самого математического, более правильным критерием считается сумма квадратов отклонений (и отсюда метод МНК).

Самым точным очевидно является вариант 4 из (24), здесь он тоже предлагался несколько раз. Из минусов - он же самый ресурсоемкий.

Для большинства бухзадач более чем достаточно варианта 3, он же в (43)
   Документовед
 
51 - 12.12.20 - 15:27
(48) > Не получится. Только если как случайно сойдется.

Простыми алгоритмами.

> В базе не должно быть отрицательных цифр, или как минимум сумма базы должна быть положительна. Упрощенное объяснение, это если в базе 1 и -1 - итоговый делитель базы равен нулю - деление на ноль.

Можно разбить колонку  сумму на колонки модуль и знак. И базу считать по модулю.

(49) > Если рассмотреть последнюю итерацию строки распределения, то если она равна сумме базы

Ты не понял не отдельная строчка к распределению равна сумме базы, а сумма всех строчек к распределению равна сумме базы.
   Документовед
 
52 - 12.12.20 - 15:31
(50) > Что-то то тебя понесло куда-то в альтернативную арифметику.

Почему?

Простой пример
ТЗ Базы
Строка 1: База1 Сумма 1
Строка 1: База2 Сумма 2

ТЗ к распределению 
Строка 1: Распред1 Сумма 0.01
Строка 1: Распред2 Сумма 2.99

Очевидно что если мы берем результат распределения строк,  то по строчкам содержащим База1 - сумма должна 1, а по строчкам содержащим Распред1  - 0.01
   Документовед
 
53 - 12.12.20 - 15:37
(50) > Если исключить случаи когда распределяемая сумма или база равна нулю, то задача математически имеет единственное решение.

Это еще почему?
Строка 1: База1 Сумма 1
Строка 1: База2 Сумма 2

ТЗ к распределению 
Строка 1: Распред1 Сумма 0.01
Строка 1: Распред2 Сумма 2.99

Может быть например

База1 Распред1  0.00
База1 Распред2  1.00
База2 Распред1  0.01
База2 Распред2  1.99
и
База1 Распред1  0.01
База1 Распред2  0.99
База2 Распред1  0.00
База2 Распред2  2.00
   RetardedToBoot
 
54 - 12.12.20 - 15:38
(51) Это ты не понял, а я не точно по русски написал. В коде точнее. Там распределяются нарастающие суммы - суммы всех предыдущих сумм к распределению. И для случая, когда сумма всех сумм к распределению равна сумме базы, как раз и получится, что последняя итерация к распределению распределяет сумму равную сумме базы.
   RetardedToBoot
 
55 - 12.12.20 - 15:43
(51) >Можно разбить колонку  сумму на колонки модуль и знак. И базу считать по модулю.

Похоже ты продумываешь не конкретную задачу, а универсальную тулсу на все случаи жизни. Даже не знаю, для какого случая такая заморочка нужна, поэтому пока не понятно, как это нужно считать.
   RetardedToBoot
 
56 - 12.12.20 - 15:46
+(55) тут может это быть это либо ошибка в учете, и тогда это один алгоритм. Тут скорее сообщение пользователю что он имеет кривые мозги. А может быть сторно предыдущих периодов, и тогда это другой алгоритм - перерасчет предыдущих распределений, и никак не связаный с текущим периодом.
   RetardedToBoot
 
57 - 12.12.20 - 15:52
+(55) но если ты хочешь универсальную тулсу на все случаи жизни, то ты забыл про комплексные числа. Если в суммы базы распределения закрадутся комплексные числа, то как их будешь обрабатывать?
   Документовед
 
58 - 12.12.20 - 16:08
(54) Сейчас проверяю вроде работает. Но я пока не понял как.
   Документовед
 
59 - 12.12.20 - 16:09
(55) > Похоже ты продумываешь не конкретную задачу, а универсальную тулсу на все случаи жизни

Нет. Хотя конечно у меня база и сумма к распределению в ноль не выходит. Минусы встречают 1-2%
   RetardedToBoot
 
60 - 12.12.20 - 16:42
(58) еще можно поэкспериментировать с сортировкой табицы базы и таблицы распределяемого по возрастанию или убыванию суммы. Так на сяк, всего четыре комбинации. Определять лучший вариант следует считая квадратичную ошибку от варианта распределения, в котором не будет округления совсем (и колонки результатов соответственно должны быть без ограничения точности). Где наименьшая квадратичная ошибка, тот вариант и лучше (можно и просто ошибку по модулю - дело вкуса).

Если описанное выше покажет, что от порядка сортировки зависит качество, то есть еще одно монструозное улучшение.

Нужно таблицу базы разбивать пополам, что бы примерно каждая половина имела одинаковую сумму. Из этих двух половинок формируем новую таблицу базы из двух строк - каждая строка это суммированная одна из двух таблиц. Распределяем на эти две строки. После каждое из двух распределений нужно распределять уже на свою подтаблицу базы. Но только если в этой подтаблице базы более двух строк, то смотрим на возможность повторения фазы с разбивкой на две подтаблицы. И так рекурсивно.

Надеюсь тебе хватит этого варианта точности.
 
 Рекламное место пустует
   Документовед
 
61 - 12.12.20 - 16:56
(57) Да так и требовалось. Спасибо. Очень толково.
   RomanYS
 
62 - 12.12.20 - 17:15
(53) "математически" имеется в виду без округления
Строка 1: База1 Сумма 1
Строка 1: База2 Сумма 2

ТЗ к распределению 
Строка 1: Распред1 Сумма 0.01
Строка 1: Распред2 Сумма 2.99

математический результат:
База1 Распред1  0.00(3)
База1 Распред2  0.99(6)
База2 Распред1  0.00(6)
База2 Распред2  1.99(3)

и очевидно (в т.ч. по объективным критериям) лучшее округление:
База1 Распред1  0.00
База1 Распред2  1.00
База2 Распред1  0.01
База2 Распред2  1.99

Получить его можно (в общем случае) тем самым вариантом 4.
   RetardedToBoot
 
63 - 12.12.20 - 17:34
(61) но есть еще одно неоспоримое улучшение. Нужно реализовать нарастающее распределение от начала начал периодов, и в проводки сажать разницу от нарастающего распределения до текущего месяца и нарастающего распределения до предыдущего месяца. Под нарастающим распределеним подразумеваю суммированные распределяемое и базы от начала начал до текущего месяца без разбивки на периоды.
   DomovoiRad
 
64 - 12.12.20 - 17:40
(63)Это наверное уже слишком :)
Я пользуюсь (43) с предварительной сортировкой по возрастанию баз распределения. Для полета космических тел может и будет недостаточно, но для расчета себестоимости ГП по бух вполне хватает.
   RetardedToBoot
 
65 - 12.12.20 - 17:43
(64) посмотри (46) там улучшенный вариант от (43)
   fisher
 
66 - 12.12.20 - 20:31
(29) Не помню откуда. Возможно переименовал.
Это не часть. Это все. Читай (19).
Просто в цикле где выполняется стандартное распределение пропорционально базе с округлением - вместо обычного округления вызываешь эту хрень. И все.
ЗЫ. Алгоритмы с уменьшением базы искажают пропорцию распределения.
   RetardedToBoot
 
67 - 13.12.20 - 02:46
(66) вот переделанный вариант из (43) без искажения пропорции:

сумма_распределения = ХХХ;
сумма_база_всего = таблица_база.итог("сумма");
накопленная_сумма_базы = 0;
предыдущая_сумма_результат_с_накоплением = 0;

Для каждого строка_базы из таблица_база цикл

    накопленная_сумма_базы = накопленная_сумма_базы + строка_базы.Сумма;

    сумма_результат_с_накоплением = окр(сумма_распределения * накопленная_сумма_базы / сумма_базы_всего, 2);
    
    сумма_результат = предыдущая_сумма_результат_с_накоплением - сумма_результат_с_накоплением;
    предыдущая_сумма_результат_с_накоплением = сумма_результат_с_накоплением;
    
    строка_результат = таблица_результат.Добавить();
    строка_результат.Сумма = сумма_результат;
    строка_результат.АналитикаБазы = строка_базы.Аналитика;
КонецЦикла;
   RetardedToBoot
 
68 - 13.12.20 - 03:00
и тогда переделанный вариант из (46) (писал снова без тестирования):

таблица_базы.Колонки.Добавить("НакопленнаяСуммаРезультат", ОписаниеТипов("Число"));

нарастающая_сумма_распределения = 0;

Для каждого строка_распределения из таблица_к_распределению Цикл

    нарастающая_сумма_распределения = нарастающая_сумма_распределения + строка_распределения.Сумма;
    // сокращение сс - суммированная_сумма

    сс_распределения = нарастающая_сумма_распределения;

    сумма_базы_всего = таблица_базы.Итог("Сумма");
    накопленная_сумма_базы = 0;
    пред_сс_результат_с_накоплением = 0;
    
    Для каждого строка_базы из таблица_базы Цикл
    
        накопленная_сумма_базы = накопленная_сумма_базы + строка_базы.Сумма;
        сс_результат_с_накоплением = окр(сс_распределения * накопленная_сумма_базы / сумма_базы_всего, 2);
        
        сс_результат = пред_сс_результат_с_накоплением - сс_результат_с_накоплением;
        пред_сс_результат_с_накоплением = сс_результат_с_накоплением;
        
        сумма_результат = сс_результат - строка_базы.НакопленнаяСуммаРезультат;
        строка_базы.НакопленнаяСуммаРезультат = сс_результат;
        
        строка_результат = таблица_результат.Добавить();
        строка_результат.Сумма = сумма_результат;
        строка_результат.АналитикаРаспределения = строка_распределения.Аналитика;
        строка_результат.АналитикаБазы = строка_базы.Аналитика;
        
    КонецЦикла;
КонецЦикла;
   RetardedToBoot
 
69 - 13.12.20 - 03:08
Это минимизирует ошибку с точки зрения бухгалтерии.

Но есть еще ошибка с точки зрения себестоимости, в отличии от упомянутой в (60) она будет как квадратичный разброс относительных ошибок - ошибка погрешности деленная на себестоимость строки посчитанной без округления, и от всего этого среднее квадратичное. (себ-ть без округления можно считать параллельно в этих же циклах, это не сложно, здесь я это не делал для упрощения читаемости кода).

Вот для минимизации такой ошибки уже нужно разбираться, как лучше их перетаскивать на наибольшие суммы (больший делитель - меньше ошибка).

Одновременно оптимизировать и бух ошибку и упр ошибку не получится.
   Asirius
 
70 - 13.12.20 - 10:16
(0) Алгоритм округления должен зависеть от контекста и проходить проверки на "вшивость". Я бы алгоритмам из типовых не доверял. Иногда даже необходимо накопившееся округление сохранять для последующих алгоритмов, а в типовых я этого пока не встречал. Например, в Зуп 3.0 сумма начислений и сумма удержаний и сумма выплат распределяются независимо и очень часто получается, что если человек работает в двух обособленных подразделениях по совместительству, то в разрезе этих подразделений начинают копиться копейки в остатках взаиморасчетов.
   Документовед
 
71 - 13.12.20 - 12:37
(70) см  (47) - там я описал критерии выполнения задачи.
   Bigbro
 
72 - 14.12.20 - 06:05
(66) это 2 разных цели - минимизировать абсолютное отклонение в копейках и минимизировать отклонение от процентного распределения.
соответственно 2 разных алгоритма.
ну и наверное еще можно вариантов придумать для распределения, которые тоже будут иметь право на жизнь.
   Документовед
 
73 - 14.12.20 - 06:36
(68)     пред_сс_результат_с_накоплением = 0;
    
    
    Для каждого строка_базы из тзБазы Цикл
        
        
        
        накопленная_сумма_базы = накопленная_сумма_базы + строка_базы.СуммаБазы;
        сс_результат_с_накоплением = окр(сс_распределения * накопленная_сумма_базы / сумма_базы_всего, 2);
        
        сс_результат = пред_сс_результат_с_накоплением - сс_результат_с_накоплением;


С самого начала  пред_сс_результат_с_накоплением = 0 а потом с_результат = пред_сс_результат_с_накоплением - сс_результат_с_накоплением; - результата получается минусовой.
   fisher
 
74 - 14.12.20 - 11:02
(67) По-моему, все-таки с искажением. А с (17) будет так:
сумма_распределения = ХХХ;
сумма_база_всего = таблица_база.итог("сумма");
ПогрешностиОкруглений = Новый Соответствие;
Для каждого строка_базы из таблица_база цикл
    строка_результат = таблица_результат.Добавить();
    строка_результат.Сумма = ОкруглитьСУчетомПогрешности(сумма_распределения * строкабазы.Сумма / сумма_базы_всего, 2, , ПогрешностиОкруглений, "СуммаРаспределения");
    строка_результат.АналитикаБазы = строка_базы.Аналитика;
КонецЦикла;
   fisher
 
75 - 14.12.20 - 11:21
(67) Не, все-таки без искажения. А результат, ИМХО, будет такой же как в (74).
   fisher
 
76 - 14.12.20 - 11:41
(69) Да. Так и есть. Но существенное искажение по позиции может возникнуть только на очень малых суммах, так как отклонение не превышает копейки. Этим, как правило, можно пренебречь.
В противном случае - да. Придется проводить дополнительную оптимизацию распределения погрешностей для минимизации отклонений по позициям.
   RetardedToBoot
 
77 - 14.12.20 - 14:32
(73) да, ошибочка. Развернуть оператор минуса нужно.

в (67):
сумма_результат = сумма_результат_с_накоплением - предыдущая_сумма_результат_с_накоплением;

и в (68):
сс_результат = сс_результат_с_накоплением - пред_сс_результат_с_накоплением;

(76) в производстве мелочевки, где себ-ть позции порядка 50-200 рублей, а затраты хотят видеть развернуто по всем статья, которых сотни, получается что строка затрат дробится на копейки.

Из алгоритма (68) я впринципе прикинул как лучше оптимизировать упр.ошибку, но писать лень.
1. Таблица базы сортируется по возрастанию.
2. Цикл таблицы базы выполняется по индексу, причем строки в некоторых случаях берутся от конца, а в некоторых от начала.
3. Для всех вычислений параллельно считается распределение без округления.
4. Наибольшие отклонения будут возникать при меньших значениях базы, их и оптимизируем.
5. Проверяются варианты откуда лучше брать очередную строку - строка с меньшей базой или строка с наибольшей базой. Считается распределение строки с наименьшей базой - вычисляется ошибка. После отдельно считаем, как будто бы не было расчета с наименьшей базой, считаем распределение от строки с наибольшей базой, и после нее считаем еще распределение строки с наименьшей базой. Получилось, что строка с наименьшей базой посчитана в двух вариантах - только она одна, и как если она будет посчитана после строки с наибольшей базой. В каком варианте ошибка у нее меньше, тот вариант и используем.
6. Какая ошибка при строке с наибольшей базой без разницы, на нее не ориентируемся.
7. Если использовали вариант с наибольшей базой, строку с наименьшей базой не спешим сразу добавлять после в расчет, а снова выполняется весь расчет п.5, но уже с новой строкой наибольшей базы.
   DTX 4th
 
78 - 14.12.20 - 14:42
Какие все странные.
Каждый раз распределять надо по новой:

100кг распределяем на 5 строк
На первую ушло 7.91. Вторую мы считаем не как 
сумм*количество/100
А как 
сумм*количетсво/(100-7.91)

ПредвСуммаРаспред = стрТЗКсраспред.Сумма * стрТЗ.СуммаБаза/БазаОсталось;
СуммаРаспред = Цел( ПредвСумма   * 100)/100
   RetardedToBoot
 
79 - 14.12.20 - 14:46
(78) такой вариант был в (43). Его забраковали в (66). Его улучшенный вариант, но с ошибкой был в (67). И т.д.
   RomanYS
 
80 - 14.12.20 - 14:50
(78)
1. Это лишь один из методов, объективно не лучше других
2. А вот использовать Цел() для округления - это действительно странно. Таким образом ты вносишь вполне конкретное искажение - округленные значения в начале будут заведомо ниже точных, в конце - заведомо больше.
   DTX 4th
 
81 - 14.12.20 - 14:52
(66) Где пруфы? О каких искажениях речь? Потерять полкопейки?

(80) Не лучше? Минимальное количество кода и телодвижений. Какую альтернативу предлагаешь ты?
   RomanYS
 
82 - 14.12.20 - 14:56
(81) Альтернатива зависит от целей и критериев. Здесь как бы вся ветка об этом.

>> Потерять полкопейки?
Твоя формула и 0.9999 копейки откинет
   DTX 4th
 
83 - 14.12.20 - 15:08
(82) Не откинет. С чего бы?
   fisher
 
84 - 14.12.20 - 15:51
(77) Оптимальное распределение может получиться ресурсоемким.
Если нужно оптимальное распределение, то по-идее можно вообще выкинуть "прошлые" алгоритмы и тупо распределять погрешность "покопеечно":
1) стандартно округлить по строкам и получить итоговую погрешность, которую надо раскидать
2) потом просто идти по убыванию сумм и раскидывать эти копейки по одной сверху-вниз, постоянно проверяя не выгоднее ли закинуть очередную копейку не на очередную строку, а - "наверх" (иначе оптимальное распределение мы не получим)
3) для этого держать указатель на очередную строку-кандидата "сверху" (который начнет свой путь с первой строки). И при этом нужно постоянно проверять, не стала ли самая верхняя строка опять более выгодным кандидатом (тогда переставляем указатель опять на нее).
В итоге получится эдакое возвратно-поступательное покопеечное "рассыпание" погрешности, которое в итоге должно дать оптимальное распределение.
   RomanYS
 
85 - 14.12.20 - 15:56
(83) Цел() - это <!>целая</!> часть
Цел(0.9999) == 0//это факт
   fisher
 
86 - 14.12.20 - 16:04
(84) +
Меня привлекает функция из (17) просто потому, что несмотря на неоптимальное распределение в большинстве практических случаев оно вполне приемлемо. Как минимум гораздо лучше, чем рабоче-крестьянские "на одну строку" и иже с ними. А код при этом она вообще не усложняет. Просто заменяем обычный Окр() на нее и - вуаля. Красиво же :)
   torgm
 
87 - 14.12.20 - 16:16
(0)  делай плавающий коэфициент распределения... и все до копейки распределиться
   RetardedToBoot
 
88 - 14.12.20 - 17:30
(84) > 1) стандартно округлить по строкам и получить итоговую погрешность, которую надо раскидать

Нет, это неверное утверждение. От того, что мы получим итоговую погрешность на грубом алгоритме, не означает что именно эту сумму нужно раскидывать - ошибка расчета это суммированные ошибки по модулю - здесь все скомкано. А если суммировать ошибки без модуля - то оно равно нулю.

Самый оптимальный вариант действительно крайне ресурсоемкий. Это нужно перебрать все комбинации последовательности строк, и для каждого из вариантов выполнить распределение. И выбрать среди расчетов лучший. Кол-во перестановок это факториал комбинаций.

А фразой "возвратно-поступательное покопеечное "рассыпание" погрешности" ты вероятно и пытаешься описать алгоритм перебора комбинаций.
   fisher
 
89 - 14.12.20 - 18:07
(88) > А если суммировать ошибки без модуля - то оно равно нулю.
Это с чего это вдруг? Если внезапно окажется, что сумма ошибок без модуля равна нулю - это значит что мы вытащили золотую карту и ничего раскидывать не нужно. Потому что у нас и по каждой строке уже идеальный результат (совпадающий с результатом честного округления) и общий итог дает распределяемую сумму с точностью до копейки.
   fisher
 
90 - 14.12.20 - 18:21
(88) Я предлагал вообще не вычислять по каждой строке ошибку округления как в предыдущих алгоритмах. А делать на старте "в лоб" - просто честное округление. Тогда разница распределившейся суммы и суммы к распределению даст точно ту сумму, которую нужно раскидать (ту самую, которую некоторые тупо на последнюю строку раньше вешали). И вот ее распределять покопеечно с контролем оптимальности распределения. Потому что при распределении каждой очередной копейки мы в состоянии относительно легко проконтролировать - оптимально мы ее распределяем или нет. Без всяких там NP
   RetardedToBoot
 
91 - 14.12.20 - 18:46
(89) >Это с чего это вдруг? Если внезапно окажется, что сумма ошибок без модуля равна нулю - это значит что мы вытащили золотую карту и ничего раскидывать не нужно.

С того, что распределившаяся сумма равна исходной к распределению. Мне казалось с этим уже давно определились, у меня в (43)(44)(67)(68) именно так и есть.

Но если использовать обозначенный тобой вариант в (17), то там копейка-две могут не сойтись. Для бухгалтерских целей это не приемлемо совсем.

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

Если такую разницу строки поделить на сумму строки распределения без округления, то получим относительную ошибку (в процентах), которую так же можно суммировать по модулю или квадратично. И именно такую я и описывал как оптимизировать.
   Злопчинский
 
92 - 14.12.20 - 20:35
(91) "Для бухгалтерских целей это не приемлемо совсем."
- для каких бухгалтерских целей не приемлемо? и что с чем может не сойтись?
   RetardedToBoot
 
93 - 15.12.20 - 06:14
(92) бухгалтера потом нервы мотают, почему копеечки висят на затратных счетах.
В некоторых случаях делают списания ТМЦ с распределением, тогда копеечки висят на счетах ТМЦ.
Как то не понятно твое отношение к не доконца закрывающимся бухгалтерским счетам. То же самое можно сказать и про регистры накопления. Типа а почему у вас так раздут такой-то регистр? Видно у вас поработал некомпетентный программист.
   fisher
 
94 - 15.12.20 - 10:08
(91) > С того, что распределившаяся сумма равна исходной к распределению.
Ты опять не понял. Или я опять не донес. Перечитай (90). Это для оптимального распределения.
> если использовать обозначенный тобой вариант в (17), то там копейка-две могут не сойтись
Это при каких условиях? Когда 27 знаков после запятой не хватит? Теоретически - да, на практике - нет.
   Bigbro
 
95 - 15.12.20 - 10:28
жесть. почти сто постов терки на пустом месте.
   RetardedToBoot
 
96 - 15.12.20 - 10:59
(94) ну так ты бы расшифровал бы:

>А делать на старте "в лоб" - просто честное округление. Тогда разница распределившейся суммы и суммы к распределению даст точно ту сумму, которую нужно раскидать (ту самую, которую некоторые тупо на последнюю строку раньше вешали)

Что такое "на старте"? При первой строке? Что такое "в лоб"? Это совсем не понятно. Очень путает фраза "честное округление", можно предположить что это "классическое округление", но вдруг я ошибаюсь. Ведь до этого в (84) ты использовал фразу "стандартно округлить". И как следствие совсем не понятно как это даст или не даст точную сумму.

Я уже в (67) предложил вполне оптимальный вариан без остаточных копеек и без искажения базы каковое было в предыдущей версии. Здесь я его повторю, т.к. там было с ошибочкой:

сумма_распределения = ХХХ;
сумма_база_всего = таблица_база.итог("сумма");
накопленная_сумма_базы = 0;
предыдущая_сумма_результат_с_накоплением = 0;

Для каждого строка_базы из таблица_база цикл

    накопленная_сумма_базы = накопленная_сумма_базы + строка_базы.Сумма;

    сумма_результат_с_накоплением = окр(сумма_распределения * накопленная_сумма_базы / сумма_базы_всего, 2);
    
    сумма_результат = сумма_результат_с_накоплением - предыдущая_сумма_результат_с_накоплением;
    предыдущая_сумма_результат_с_накоплением = сумма_результат_с_накоплением;
    
    строка_результат = таблица_результат.Добавить();
    строка_результат.Сумма = сумма_результат;
    строка_результат.АналитикаБазы = строка_базы.Аналитика;
КонецЦикла;


И дальше, ты же сам в (84) и (86) рассуждаешь куда девать копейки, в частности:

>Если внезапно окажется, что сумма ошибок без модуля равна нулю - это значит что мы вытащили золотую карту и ничего раскидывать не нужно.

Ссылаешься на мои посты, где у меня распределяемое равно распределенному. И упоминаешь пост (17), при использовании которого как раз может образовываться лишняя или нехватающая копейка.

Напиши пожалуйста поаккуратней, что ты подразумеваешь. Не все же здесь телепаты.
   RetardedToBoot
 
97 - 15.12.20 - 11:02
(95) перфекционизм требует жертв.
   Злопчинский
 
98 - 15.12.20 - 11:26
(93) При списании со счета десятков тысяч рублей и при этом из-за ошибок округления останется на счете висеть пара копеек? вам не кажется что вы по воробьям из пушки стрелляете? вариант обработки округлений , котгорый не дает зависания копеек ликвидирует проблему. хреначится на то, чтобы на сумму 35000 рублей распределилось не 23 коп, а 24 - имхо когда коту делать нечего...
   fisher
 
99 - 15.12.20 - 11:46
(96) > Что такое "на старте"
При первом проходе.
> Что такое "в лоб"? Это совсем не понятно
"в лоб" = "честное округление" = "классическое округление" = "стандартное округление"
> Ссылаешься на мои посты, где у меня распределяемое равно распределенному.
Когда начали обсуждать оптимальное распределении погрешности округления с точки зрения минимального искажения себестоимости позиции, я несколько раз подчеркнул, что "не как в предыдущих алгоритмах", "выкинуть прошлые алгоритмы"
> упоминаешь пост (17), при использовании которого как раз может образовываться лишняя или нехватающая копейка
В том контексте, что мне просто нравится этот способ. Не в контексте того, что он дает оптимальное распределение. Читаем - "несмотря на неоптимальное распределение". Вроде бы даже нетелепатам должно быть понятно. И я попросил объяснить, в каком случае при этом алгоритме может образоваться отклонение на копейку. Ибо это не так.
Что касается алгоритма (67) - разве он дает оптимальное распределение погрешности с точки зрения минимального искажения себестоимости позиции? Начиная с (76) я обсуждал только это. С упоминанием того, что если этот критерий не является критичным (а чаще всего - не является), то (17) мне нравится больше всего в силу простоты использования и минимальной инвазивности кода.
   fisher
 
100 - 15.12.20 - 11:56
(93) Не понял, причем тут незакрытые счета затрат. Каким образом к этому может привести обсуждаемая тема? Я думал, у тебя просто душа болит за искажение распределения копеечной суммы на стопицот статей затрат. Что мол на какой-то статье должно было быть ноль, а повесило копейку. И мы тут с этим горем сейчас воюем.
  1  2   

Список тем форума
Рекламное место пустует  Рекламное место пустует
ВНИМАНИЕ! Если вы потеряли окно ввода сообщения, нажмите Ctrl-F5 или Ctrl-R или кнопку "Обновить" в браузере.