Алгоритм рандома с учетом весовых коэффициентов

_savit
На сайте с 19.03.2006
Offline
135
5869

Всем привет.

Необходим алгоритм быстрого рандома с учетом весовых коэффициентов.

Имеется болшой набор пар 'ключ' => 'значение', где ключами являются какие-либо объекты ( пусть будут буквы в данном примере), а значениями их весовых коэффициенты

$m=array('a' =>1, 'b'=>0.7, 'c'=>1.1, 'd'=>'4', ..... );

Задача: каждый раз выбирать одну случайную букву, но что бы буквы имеющие бОльшие весовые коэффициенты "выпадали" чаще чем те у которых коэффициенты меньше.

Голова под вечер уже не соображает т.ч прибегаю к Вашей помощи. Есть ли у кого-нибудь мысли по этому поводу?

p.s считать сколько раз уже была выбрана та или иная буква ( вести статистику ) и исходить из этого по некоторым причинам не подходит ... нужен именно алгоритм производящий выборку исключительно по тем данным что имеются.

https://vk-botovod.ru - комбайн ВКонтакте, мультимессенджер, эмулятор жизни аккаунтов
psylosss
На сайте с 23.12.2005
Offline
126
#1

Вот на скорую руку набросал:


<?php

// Алгоритм
//Составляем "карту" распределения весов
//--------------------------------------| длина отрезка равна сумме коэффициентов
//---*-------*-*------------*-----------| расставляем на отрезке маркеры (*), отступая каждый раз от предыдущего на расстояние веса
//-a-*--b----*c*-----d------*---e-------| каждому диапазону между маркерами соответствует значение
//---*----@--*-*-----*------*-----------| бросаем на отрезок случайную точку и смотрим в какой диапазон она попала


$m=array('a' =>1, 'b'=>0.7, 'c'=>1.1, 'd'=>4);

$sum=array_sum($m); //Получаем сумму весов

$map=array(); //Это "отрезок"
$next_marker=0.0; //Это смещение следующего маркера
foreach ($m as $value=>$weight)
{
$range=array('from'=>$next_marker,'to'=>$next_marker+$weight);
$map[$value]=$range; //Ставим маркер по вычисленному смещению
$next_marker+=$weight;
}


$random_point=rand(0,$sum*1000)/1000; //Бросаем случайную "точку" на "отрезок", обеспечивая достаточную дискретность

//Смотрим в какой диапазон попала случайная точка
$result=null;
foreach ($map as $value=>$range)
{
if ($random_point>$range['from'] and $random_point<=$range['to'])
{
$result=$value;
break;
}

}

echo $result;


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

Веб-разработка. Сложные проекты. Проектирование. Проект-менеджмент. Стартапы.
_savit
На сайте с 19.03.2006
Offline
135
#2

psylosss, спасибо

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

<?php

$obj=array('a', 'b', 'c', 'd'); // объекты

$weights=array(15, 25, 10, 50); // отнормированные коэффициенты

$p=0;

$rnd=mt_rand(0,array_sum($weights)-1);

while($rnd>=0)

$rnd-=$weights[$p++];

print $obj[$p-1];

?>

зациклил это дело и прогнал 150000 раз с записью результатов в файл ... посчитал частоту .. посчитал процентное соотношение ... все работает как часы.

malls
На сайте с 08.08.2005
Offline
255
#3

А зачем так сложно то???

допустим есть пары:

a - 3

b - 2

c - 1

создаем массив:

[ a , a , a , b , b , c ]

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

остальное доделают теория вероятности и принципы нормального гауссова распределения...

_savit
На сайте с 19.03.2006
Offline
135
#4

Да, способ предложенный malls действительно лучший в случае с целочисленными коэффициентами и при не большом массиве объектов.

В общем то все коэффициенты можно сделать целочисленными умножив на 10 например ... но если объектов очень много, то создаваемый массив будет просто гигантским.

malls
На сайте с 08.08.2005
Offline
255
#5
_savit:
... но если объектов очень много, то создаваемый массив будет просто гигантским.

Тогда надо математику подключать...

Вот, например (не понял о чем речь, но название красивое):

О плотности распределения супремума случайного блуждания в субэкспоненциальном случае

😂😂😂

А если серьезно, то думаю схематика такая:

Есть веса, например, от 1 до 100...

1 этап

выбираем случайное число, допустим 34.

2 этап

выбираем все значения с весами до 34 включительно.

3 этап

выбираем случайное из данной группы значений.

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

как-то так, поправьте если ошибаюсь.

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

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий