// Решает систему линейных уравнений методом простых итераций (метод Якоби)
//
Функция РешитьСЛУБезИспользованияВременныхТаблиц(ДатаНач, ДатаКон, РегламентныйДокумент, РегистрУчета, ВидОтраженияВУчете, МенеджерВременныхТаблиц,СтруктураПараметров,ОперацияРасчетаСебестоимостиВыпуска)
Перем МассивСвободныхЧленов, МассивСвободныхЧленовСтоимостьНУ, МассивСвободныхЧленовПостояннаяРазница;
// Стоимость товара на складе равна стоимости, полученной извне (например, при покупке товара) плюс начальный остаток
// плюс стоимость товара перемещенного с других складов, умноженная на количество перемещенного товара
//
// Это можно представить в виде СЛУ
// K(1) * X(1) = S(1) + K(2) * X(2) + K(3) * X(3) + ... + K(j) * X(j)
// K(2) * X(2) = S(1) + K(1) * X(1) + K(3) * X(3) + ... + K(j) * X(j)
// ...
// K(i) * X(i) = S(i) + K(1) * X(1) + K(2) * X(2) + ... + K(j) * X(j)
//
// где
// K(i) - количество товара на складе i
// X(i) - стоимость товара на складе i
// K(j) - количество перемещенного товара со склада j на склад i
// X(j) - стоимость товара на складе j
// S(i) - стоимость товара, полученного извне плюс начальный остаток на складе i
// При решении уравнения используются следующие данные
// S(i)/K(i) - берется средняя стоимость внешних поступлений из регистра УчетЗатрат
// (перед расчетом помещается в регистр УзлыКорректировкиСтоимостиСписания)
// K(i) - берется общее количество поступления из регистра УчетЗатрат
// (перед расчетом помещается в регистр УзлыКорректировкиСтоимостиСписания)
// K(j) - берутся движения "расход" из регистра УчетЗатрат
// X(j) - берется из решения предыдущей итерации (для первой итерации берется S(i)/K(i))
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("Регистратор", РегламентныйДокумент);
Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
Запрос.УстановитьПараметр("ДатаКон", ДатаКон);
//Сформируем список состояний
КоличествоУравненийСЛУ = СформироватьДвиженияПоРегиструУзлыКорректировкиСтоимостиСписания(
Запрос,
РегистрУчета,
ВидОтраженияВУчете,
СтруктураПараметров,
ОперацияРасчетаСебестоимостиВыпуска
);
Если КоличествоУравненийСЛУ = 0 Тогда
Возврат Ложь;
КонецЕсли;
МаксимальноеКоличествоИтераций = СтруктураПараметров.МаксимальноеКоличествоИтераций;
ТребуемаяТочность = СтруктураПараметров.СреднееОтклонениеСЛУ;
// Получим S(i)/K(i)
ЗаполнитьМассивыСвободныхЧленов(МассивСвободныхЧленов, МассивСвободныхЧленовСтоимостьНУ, МассивСвободныхЧленовПостояннаяРазница, ВидОтраженияВУчете, Запрос);
КоличествоУравненийСЛУ = МассивСвободныхЧленов.Количество();
// Выберем начальное приближение X(j)
ТекущееРешение = СкопироватьМассив(МассивСвободныхЧленов);
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
ТекущееРешениеСтоимостьНУ = СкопироватьМассив(МассивСвободныхЧленовСтоимостьНУ);
ТекущееРешениеПостояннаяРазница = СкопироватьМассив(МассивСвободныхЧленовПостояннаяРазница);
КонецЕсли;
// Получим K(j) для всех уравнений системы
МассивСЛУ = ПолучитьСЛУ(ВидОтраженияВУчете, КоличествоУравненийСЛУ, Запрос);
// По умолчанию примем за решение начальное приближение
НовоеРешение = СкопироватьМассив(ТекущееРешение);
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
НовоеРешениеСтоимостьНУ = СкопироватьМассив(ТекущееРешениеСтоимостьНУ);
НовоеРешениеПостояннаяРазница = СкопироватьМассив(ТекущееРешениеПостояннаяРазница);
КонецЕсли;
// Решим СЛУ методом простых итераций
ТекущееОтклонение = КоличествоУравненийСЛУ;
КоличествоИтераций = 0;
Пока (ТекущееОтклонение/КоличествоУравненийСЛУ > ТребуемаяТочность)
И (КоличествоИтераций < МаксимальноеКоличествоИтераций) Цикл
ТекущееОтклонение = 0;
ТекущееОтклонениеПР = 0;
ТекущееОтклонениеНУ = 0;
Для НомерТекущегоУравнения = 0 По МассивСЛУ.Количество() - 1 Цикл
Если МассивСЛУ[НомерТекущегоУравнения] = Неопределено Тогда
// Для этой переменной считать ничего не нужно, первое приближение - это и есть точное решение
Продолжить;
КонецЕсли;
// Получим первое выражение уравнения: S(i)/K(i)
НовоеРешение[НомерТекущегоУравнения] = МассивСвободныхЧленов[НомерТекущегоУравнения];
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
НовоеРешениеСтоимостьНУ[НомерТекущегоУравнения] = МассивСвободныхЧленовСтоимостьНУ[НомерТекущегоУравнения];
НовоеРешениеПостояннаяРазница[НомерТекущегоУравнения] = МассивСвободныхЧленовПостояннаяРазница[НомерТекущегоУравнения];
КонецЕсли;
// Добавим к уравнению остальные выражения: (K(1) * X(1) + K(2) * X(2) + ... + K(j) * X(j)) / K(i)
Для Каждого ТекущийКоэффициент Из МассивСЛУ[НомерТекущегоУравнения] Цикл
КоличествоПеремещенногоТовара = ?(ТекущийКоэффициент.ЗначениеКоэффициента = 0, ТекущийКоэффициент.Стоимость, ТекущийКоэффициент.ЗначениеКоэффициента);
НовоеРешение[НомерТекущегоУравнения] = ДобавитьСтоимостьТовараПеремещенногоСДругихСкладов(
НовоеРешение[НомерТекущегоУравнения],
КоличествоПеремещенногоТовара, // K(j)
ТекущийКоэффициент.УзелКоличество, // K(i)
ТекущееРешение[ТекущийКоэффициент.НомерПеременной]); // X(j)
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
КоличествоПеремещенногоТовара = ?(ТекущийКоэффициент.КоличествоНУ = 0, ТекущийКоэффициент.СтоимостьНУ + ТекущийКоэффициент.ПостояннаяРазница, ТекущийКоэффициент.КоличествоНУ);
НовоеРешениеСтоимостьНУ[НомерТекущегоУравнения] = ДобавитьСтоимостьТовараПеремещенногоСДругихСкладов(
НовоеРешениеСтоимостьНУ[НомерТекущегоУравнения],
КоличествоПеремещенногоТовара, // K(j)
ТекущийКоэффициент.УзелКоличествоНУ, // K(i)
ТекущееРешениеСтоимостьНУ[ТекущийКоэффициент.НомерПеременной]); // X(j)
НовоеРешениеПостояннаяРазница[НомерТекущегоУравнения] = ДобавитьСтоимостьТовараПеремещенногоСДругихСкладов(
НовоеРешениеПостояннаяРазница[НомерТекущегоУравнения],
КоличествоПеремещенногоТовара, // K(j)
ТекущийКоэффициент.УзелКоличествоНУ, // K(i)
ТекущееРешениеПостояннаяРазница[ТекущийКоэффициент.НомерПеременной]); // X(j)
КонецЕсли;
КонецЦикла;
АА = НовоеРешение[НомерТекущегоУравнения] - ТекущееРешение[НомерТекущегоУравнения];
Если АА < 0 Тогда
АА = -АА;
КонецЕсли;
ТекущееОтклонение = ТекущееОтклонение + АА;
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
АА = НовоеРешениеСтоимостьНУ[НомерТекущегоУравнения] - ТекущееРешениеСтоимостьНУ[НомерТекущегоУравнения];
Если АА < 0 Тогда
АА = -АА;
КонецЕсли;
ТекущееОтклонениеНУ = ТекущееОтклонениеНУ + АА;
АА = НовоеРешениеПостояннаяРазница[НомерТекущегоУравнения] - ТекущееРешениеПостояннаяРазница[НомерТекущегоУравнения];
Если АА < 0 Тогда
АА = -АА;
КонецЕсли;
ТекущееОтклонениеПР = ТекущееОтклонениеПР + АА;
КонецЕсли;
КонецЦикла;
ТекущееОтклонение = Макс(ТекущееОтклонение, ТекущееОтклонениеПР, ТекущееОтклонениеНУ);
// Запомним решение текущей итерации чтобы использовать его в следующей итерации
ТекущееРешение = СкопироватьМассив(НовоеРешение);
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
ТекущееРешениеСтоимостьНУ = СкопироватьМассив(НовоеРешениеСтоимостьНУ);
ТекущееРешениеПостояннаяРазница = СкопироватьМассив(НовоеРешениеПостояннаяРазница);
КонецЕсли;
КоличествоИтераций = КоличествоИтераций + 1;
КонецЦикла;
ВременнаяТаблицаРешений = Новый ТаблицаЗначений;
ВременнаяТаблицаРешений.Колонки.Добавить("НомерУзла", Новый ОписаниеТипов("Число"));
ВременнаяТаблицаРешений.Колонки.Добавить("ПостояннаяРазница", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(23, 10)));
ВременнаяТаблицаРешений.Колонки.Добавить("СтоимостьНУ", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(23, 10)));
ВременнаяТаблицаРешений.Колонки.Добавить("Стоимость", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(23, 10)));
НомерУзла = 1;
Для каждого ЭлКоллекции Из ТекущееРешение Цикл
НовоеРешение = ВременнаяТаблицаРешений.Добавить();
НовоеРешение.НомерУзла = НомерУзла;
НовоеРешение.Стоимость = ЭлКоллекции;
Если ВидОтраженияВУчете = Перечисления.ВидыОтраженияВУчете.ОтражатьВРегламентированномУчете Тогда
НовоеРешение.СтоимостьНУ = ТекущееРешениеСтоимостьНУ[НомерУзла-1];
НовоеРешение.ПостояннаяРазница = ТекущееРешениеПостояннаяРазница[НомерУзла-1];
КонецЕсли;
НомерУзла = НомерУзла + 1;
КонецЦикла;
Запрос.Текст = "
|УНИЧТОЖИТЬ ТаблицаРешений
|;
|ВЫБРАТЬ * ПОМЕСТИТЬ ВременнаяТаблицаРешений ИЗ &ВременнаяТаблицаРешений КАК ВременнаяТаблицаРешений
|;
|ВЫБРАТЬ
| УзлыКорректировкиСтоимостиСписания.НомерУзла КАК НомерУзла,
| //ДляРеглУчета ЕСТЬNULL(ВременнаяТаблицаРешений.ПостояннаяРазница, 0) КАК ПостояннаяРазница,
| //ДляРеглУчета ЕСТЬNULL(ВременнаяТаблицаРешений.СтоимостьНУ, 0) КАК СтоимостьНУ,
| ЕСТЬNULL(ВременнаяТаблицаРешений.Стоимость, 0) КАК Стоимость
|
|ПОМЕСТИТЬ ТаблицаРешений
|ИЗ
| РегистрСведений.УзлыКорректировкиСтоимостиСписания КАК УзлыКорректировкиСтоимостиСписания
|
| ЛЕВОЕ СОЕДИНЕНИЕ
| ВременнаяТаблицаРешений КАК ВременнаяТаблицаРешений
| ПО
| ВременнаяТаблицаРешений.НомерУзла = УзлыКорректировкиСтоимостиСписания.НомерУзла
|ГДЕ
| УзлыКорректировкиСтоимостиСписания.Регистратор = &Регистратор
|
|ИНДЕКСИРОВАТЬ ПО
| НомерУзла
|;
|УНИЧТОЖИТЬ ВременнаяТаблицаРешений";
Запрос.УстановитьПараметр("ВременнаяТаблицаРешений", ВременнаяТаблицаРешений);
Запрос.Текст = УправлениеЗатратами.ЗаменитьКомментарииВТекстеЗапроса(Запрос.Текст, ВидОтраженияВУчете);
Запрос.Выполнить();
ОбщегоНазначения.Сообщение("Выполненное количество итераций расчета стоимости: " + Строка(КоличествоИтераций));
ОбщегоНазначения.Сообщение("Полученное среднее отклонение решений: " + Строка(Окр(ТекущееОтклонение/КоличествоУравненийСЛУ, 13)));
Возврат Истина;
КонецФункции
Показать