Случайная неслучайная скидка

24.01.19

Разработка - Математика и алгоритмы

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

Скачать исходный код

Наименование Файл Версия Размер
Демонстрация нахождения случайной скидки.
.erf 10,49Kb
0
.erf 10,49Kb Скачать

Однажды меня клиент попросил сделать механизм случайно выпадающей скидки на следующую покупку. 
Собственно тут в случайном выборе скидки ничего сложного, как вариант - поместить все скидки в массив, и использовать генератор случайных чисел от 0 до размера массива -1, что бы определить нужный индекс элемента этого массива.

Но потом в задаче появилось уточнение - скидки должны выпадать по обратной экспоненциальной зависимости. Т.е. к примеру скидка 5% должна выпадать гораздо чаще скидки 95%.

Желание клиента для меня вполне понятно, нечего постоянно раскидываться большими скидками, но пусть они иногда выпадают.  Почему экспоненциально - будем считать что для красоты (я не знаю откуда взялась эта идея).

Опишу тут два варианта решения, которые я нашел для себя.

1. Создать массив скидок, где некоторые скидки повторяются N раз ( N это "вес" числа ).


// Для примера создадим массив чисел (скидок) от 1 до 99
МассивЧисел = Новый Массив(); 
Счетчик = 1;
Пока Счетчик <= 100 Цикл
	Массив.Добавить(Счетчик);
	Счетчик = Счетчик + 1;
КонецЦикла; 
 

// На основе массива значений скидок сделаем новый массив, где скидки будут повторятся пропорционально весу значения скидки 
МассивПоВесу = СоздатьМассивПоВесу(МассивЧисел); 

// Выберем случайное число в диапазоне от 0 до количества элементов массива
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, МассивПоВесу.Количество()-1);

// Нашли случайную скидку
СлучайнаяСкидка = МассивПоВесу[СлучайноеЧисло];



Функция СоздатьМассивПоВесу(МассивЧисел)
	
	МассивПоВесу = Новый Массив;
	
	// коэффициент, который показывает во сколько раз скидка 0% больше скидки 100% , можно выбрать по своему желанию
	Коэффициент = 20;

	Для каждого Элемент Из МассивЧисел Цикл
			
		Вес = Exp( (100 - Элемент) / (100 / Log(Коэффициент)) );
		Вес = Окр( Вес * 10, 0); // 10 - в данном случае подобрано эмпирически, для округления до удобного целого числа

		Для Счетчик = 1 По Вес Цикл
			МассивПоВесу.Добавить(Элемент);
		КонецЦикла; 
		
	КонецЦикла; 
	
	Возврат МассивПоВесу;
	
КонецФункции

 

2. Получение скидки сложением весов чисел в цикле.

Для получения случайной скидки суммируем веса скидок, пока эта сумма не превысит случайное число, умноженное на сумму всех весов.

 

// Для примера создадим массив чисел (скидок) от 1 до 99
МассивЧисел = Новый Массив(); 
Счетчик = 1;
Пока Счетчик <= 100 Цикл
	МассивЧисел .Добавить(Счетчик);
	Счетчик = Счетчик + 1;
КонецЦикла; 

// Для каждой скидки определим вес и поместим всё в таблицу значений
ТаблицаСкидок = СоздатьТаблицуВесовСкидок(МассивЧисел);

СуммаВесов = 0;
Для каждого СтрокаТЗ Из ТаблицаСкидок Цикл
	СуммаВесов = СуммаВесов + СтрокаТЗ.Вес;
КонецЦикла; 

// Случайное число 0 .. 1
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, 100) / 100;

Лямбда = СуммаВесов * СлучайноеЧисло;

// В цикле накапливаем вес, пока не сработает условие. Текущая строка при этом и будет найденная скидка
Накопление = 0;
Для Каждого СтрокаТЗ Из ТаблицаСкидок Цикл
	Накопление = Накопление + СтрокаТЗ.Вес;
	Если Накопление >= Лямбда Тогда
		СлучайнаяСкидка = СтрокаТЗ.Скидка; // Нашли скидку
		Прервать;
	КонецЕсли;
КонецЦикла;


Функция СоздатьТаблицуВесовСкидок(Массив)

	ТаблицаСкидок = Новый ТаблицаЗначений;
	ТаблицаСкидок.Колонки.Добавить("Скидка");
	ТаблицаСкидок.Колонки.Добавить("Вес");
	
	Для каждого Элемент Из Массив Цикл
		
		НоваяСтрока = ТаблицаСкидок.Добавить();
		НоваяСтрока.Скидка = Элемент;

		// коэффициент, который показывает во сколько раз скидка 0% больше скидки 100% , можно выбрать по своему желанию
		Коэффициент = 20;
		
		Коэф = 100 / Log(Коэффициент);
		НоваяСтрока.Вес = Exp( (100 - Элемент) / Коэф);
		
	КонецЦикла;  
	
	// На случай не отсортированного массива на входе
	ТаблицаСкидок.Сортировать("Скидка");

	Возврат ТаблицаСкидок;
	
КонецФункции

 

Ключевые моменты описаны в коде выше.

В обработке есть демонстрация работы обоих алгоритмов и отрисовка графика для метода сложения сумм весов. Кроме этого добавил еще вариант линейной зависимости.

Тестировалось в BAS ERP 2.1.6.3

См. также

Метод Дугласа-Пойкера для эффективного хранения метрик

Математика и алгоритмы Программист Платформа 1C v8.2 Конфигурации 1cv8 Россия Абонемент ($m)

На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL. Я же покажу как можно сократить объем данных в 49.9 раз при этом: 1. Сохранить значения локальных экстремумов 2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.

1 стартмани

30.01.2024    2065    stopa85    12    

34

Алгоритм симплекс-метода для решения задачи раскроя

Математика и алгоритмы Бесплатно (free)

Разработка алгоритма, построенного на модели симплекс-метода, для нахождения оптимального раскроя.

19.10.2023    5099    user1959478    50    

35

Регулярные выражения на 1С

Математика и алгоритмы Инструментарий разработчика Программист Платформа 1С v8.3 Мобильная платформа Россия Абонемент ($m)

Что ж... лучше поздно, чем никогда. Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.

1 стартмани

09.06.2023    8253    5    SpaceOfMyHead    17    

60

Мини-обзор разных решений задач

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

Три задачи - три идеи - три решения. Мало кода, много смысла. Мини-статья.

03.04.2023    3280    RustIG    7    

25

Модель распределения суммы по базе

Математика и алгоритмы Программист Платформа 1С v8.3 Россия Абонемент ($m)

Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса

1 стартмани

21.03.2022    8119    7    kalyaka    11    

44

Интересная задача на Yandex cup 2021

Математика и алгоритмы Программист Бесплатно (free)

Мое решение задачи на Yandex cup 2021 (frontend). Лабиринт. JavaScript.

12.10.2021    9148    John_d    73    

46
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. scientes 290 25.01.19 14:47 Сейчас в теме
Могу предложить вот такой вариант.
Функция СкидкаДляInfostart(сигма) экспорт
	перем скидка;
	ГСЧ = Новый ГенераторСлучайныхЧисел(ТекущаяУниверсальнаяДатаВМиллисекундах());
	пока истина цикл
		u=-1+2*ГСЧ.СлучайноеЧисло(0,100)/100;
		v=-1+2*ГСЧ.СлучайноеЧисло(0,100)/100;
		s=u*u+v*v;
		if s<1 then
			r=sqrt(-2*Log(s)/s);
			z=цел(max(u,-u)*r*сигма);
			скидка=мин(z,99);
			прервать;
		endif	   
	конеццикла;	 
	возврат скидка;
КонецФункции	
Показать
Оставьте свое сообщение