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

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

Однажды меня клиент попросил сделать механизм случайно выпадающей скидки на следующую покупку. 
Собственно тут в случайном выборе скидки ничего сложного, как вариант — поместить все скидки в массив, и использовать генератор случайных чисел от 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

1 Comment

  1. scientes

    Могу предложить вот такой вариант.

    Функция СкидкаДля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
    конеццикла;
    возврат скидка;
    КонецФункции
    

    Показать

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *