Untitled

 avatar
unknown
plain_text
4 months ago
15 kB
9
Indexable
// Объявления функций
Далее Функция РазбитьПозициюПеребором(Цена, Количество, ОбщаяСумма);
Далее Функция ПеребратьКоличества(Цена, Количество, ОбщаяСумма, Шаг, ИзменятьКоличество);

// Функция разбивает позицию на две части методом перебора по количеству.
// Сохраняет исходную цену для первой позиции и подбирает цену для второй позиции.
//
// Параметры:
//  Цена - Число - Цена с точностью до 2 знаков после запятой
//  Количество - Число - Количество с точностью до 3 знаков после запятой
//  ОбщаяСумма - Число - Общая сумма с точностью до 2 знаков после запятой
//
// Возвращаемое значение:
//  ТаблицаЗначений - Содержит две строки с колонками: Цена, Количество, Сумма
//                   или 0, если разбиение невозможно
Функция РазбитьПозициюПеребором(Цена, Количество, ОбщаяСумма)
    // Округляем входные данные до нужной точности
    Цена = Окр(Цена, 2);
    Количество = Окр(Количество, 3);
    ОбщаяСумма = Окр(ОбщаяСумма, 2);
    
    // Проверяем, нужно ли разбиение
    ТеоретическаяСумма = Окр(Цена * Количество, 2);
    Если ТеоретическаяСумма = ОбщаяСумма Тогда
        Результат = СоздатьОбъект("ТаблицаЗначений");
        Результат.ИзменитьСтруктуру();
        Результат.ДобавитьКолонку("Цена");
        Результат.ДобавитьКолонку("Количество");
        Результат.ДобавитьКолонку("Сумма");
        
        Результат.ДобавитьСтроку();
        Результат.Цена.Значение = Цена;
        Результат.Количество.Значение = Количество;
        Результат.Сумма.Значение = ОбщаяСумма;
        
        Результат.ДобавитьСтроку();
        Результат.Цена.Значение = 0;
        Результат.Количество.Значение = 0;
        Результат.Сумма.Значение = 0;
        
        Возврат Результат;
    КонецЕсли;
    
    // Определяем шаг перебора в зависимости от дробной части количества
    Если Количество = Цел(Количество) Тогда
        // Если количество целое число
        Шаг = 1;
    ИначеЕсли Окр(Количество * 10, 0) = Цел(Количество * 10) Тогда
        // Если количество имеет только один десятичный знак
        Шаг = 0.001;
    ИначеЕсли Окр(Количество * 100, 0) = Цел(Количество * 100) Тогда
        // Если количество имеет два десятичных знака
        Шаг = 0.001;
    Иначе
        // Если количество имеет три десятичных знака
        Шаг = 0.001;
    КонецЕсли;
    
    // Вызываем функцию перебора
    Результат = ПеребратьКоличества(Цена, Количество, ОбщаяСумма, Шаг, 0);
    Если Результат = 0 Тогда
        Результат = ПеребратьКоличества(Цена, Количество, ОбщаяСумма, Шаг, 1);
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

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

// Процедура для тестирования алгоритма на нескольких примерах
Процедура ТестАлгоритмаПеребора()
    // Создаем таблицу тестовых случаев (Количество, Сумма)
    ТестовыеСлучаи = СоздатьОбъект("ТаблицаЗначений");
    ТестовыеСлучаи.ИзменитьСтруктуру();
    ТестовыеСлучаи.ДобавитьКолонку("Количество");
    ТестовыеСлучаи.ДобавитьКолонку("Сумма");
    
    // Добавляем тестовые случаи
    ТестовыеСлучаи.ДобавитьСтроку();
    ТестовыеСлучаи.Количество.Значение = 3.500;
    ТестовыеСлучаи.Сумма.Значение = 35.00;
    
    ТестовыеСлучаи.ДобавитьСтроку();
    ТестовыеСлучаи.Количество.Значение = 5.678;
    ТестовыеСлучаи.Сумма.Значение = 70.06;
    
    ТестовыеСлучаи.ДобавитьСтроку();
    ТестовыеСлучаи.Количество.Значение = 2.500;
    ТестовыеСлучаи.Сумма.Значение = 24.98;
    
    ТестовыеСлучаи.ДобавитьСтроку();
    ТестовыеСлучаи.Количество.Значение = 2.000;
    ТестовыеСлучаи.Сумма.Значение = 30.00;
    
    ТестовыеСлучаи.ДобавитьСтроку();
    ТестовыеСлучаи.Количество.Значение = 12.5;
    ТестовыеСлучаи.Сумма.Значение = 653.13;
    
    // Выполняем тесты
    ТестовыеСлучаи.ВыбратьСтроки();
    Индекс = 1;
    Пока ТестовыеСлучаи.ПолучитьСтроку() = 1 Цикл
        Сообщить("");
        Сообщить("Тест #" + Индекс + ":");
        
        Количество = ТестовыеСлучаи.Количество.Значение;
        ОбщаяСумма = ТестовыеСлучаи.Сумма.Значение;
        
        Цена = Окр(ОбщаяСумма / Количество, 2);
        
        Сообщить("Исходные данные: Цена = " + Цена + ", Количество = " + Количество + ", Сумма = " + ОбщаяСумма);
        Сообщить("Теоретическая сумма: " + Окр(Цена * Количество, 3));
        
        Результат = РазбитьПозициюПеребором(Цена, Количество, ОбщаяСумма);
        
        Если Результат = 0 Тогда
            Сообщить("Разбиение невозможно");
        Иначе
            // Получаем первую позицию
            Результат.ВыбратьСтроки();
            Результат.ПолучитьСтроку();
            Цена1 = Результат.Цена.Значение;
            Количество1 = Результат.Количество.Значение;
            Сумма1 = Результат.Сумма.Значение;
            
            // Получаем вторую позицию
            Результат.ПолучитьСтроку();
            Цена2 = Результат.Цена.Значение;
            Количество2 = Результат.Количество.Значение;
            Сумма2 = Результат.Сумма.Значение;
            
            Если Количество2 = 0 Тогда
                Сообщить("Разбиение не требуется, исходные данные корректны");
            Иначе
                Сообщить("");
                Сообщить("Результат разбиения:");
                Сообщить("Позиция 1: Цена = " + Цена1 + ", Количество = " + Количество1 + ", Сумма = " + Сумма1);
                Сообщить("Проверка: " + Цена1 + " * " + Количество1 + " = " + Окр(Цена1 * Количество1, 2));
                
                Сообщить("Позиция 2: Цена = " + Цена2 + ", Количество = " + Количество2 + ", Сумма = " + Сумма2);
                Сообщить("Проверка: " + Цена2 + " * " + Количество2 + " = " + Окр(Цена2 * Количество2, 2));
                
                Сообщить("Общая сумма после разбиения: " + Окр(Сумма1 + Сумма2, 2));
                Сообщить("Общее количество после разбиения: " + Окр(Количество1 + Количество2, 3));
                
                // Проверяем условия корректности разбиения
                СуммаПроверка = (Окр(Сумма1 + Сумма2, 2) - ОбщаяСумма) < 0.01;
                КоличествоПроверка = (Окр(Количество1 + Количество2, 3) - Количество) < 0.001;
                КоличествоПроверка = 1;
                Цена1Проверка = (Окр(Цена1 * Количество1, 2) - Сумма1) < 0.01;
                Цена2Проверка = (Окр(Цена2 * Количество2, 2) - Сумма2) < 0.01;
                
                Если СуммаПроверка И КоличествоПроверка И Цена1Проверка И Цена2Проверка Тогда
                    Сообщить("✅ Разбиение корректно");
                Иначе
                    Сообщить("❌ Разбиение некорректно");
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
        
        Индекс = Индекс + 1;
    КонецЦикла;
КонецПроцедуры

// Запуск тестирования
// ТестАлгоритмаПеребора();
Editor is loading...
Leave a Comment