Дано:
Массив длиной L, каждый элемент массива имеет вес weight (для простоты - целочисленный)
Задача:
Выбрать рандомно N элементов из массива (N < L), при этом не должно быть одинаковых элементов, и вероятность выборки каждого элемента должна быть прямо пропорциональна весу элемента.
Решение:
Разместим элементы массива на оси весов, при этом длина отрезка каждого элемента равна весу этого элемента.
Посчитаем суммарный вес элементов - это максимально возможная координата.
Теперь итерируем массив от начала (столько итераций, сколько нужно выбрать элементов). На каждой итерации:
- Выбираем рандомно точку от 1 до макс. координаты $weight
- Ищем элемент, соответствующий отрезку, содержащему эту точку
- Меняем местами выбранный элемент с элементом, соответствующем текущей итерации - т.о. "запоминаем" его.
- Сдвигаем начало координат (и соотв. макс. координату) на величину, равную длине отрезка выбранного элемента
Всё. Теперь N начальных элементов - и есть интересующая нас выборка. Осталось только отбросить лишнюю часть массива от N+1 до L
# @res - исходный массив # $need_count - сколько нужно выбрать элементов # $weight - суммарный вес всех элементов исходного массива for (my $i=0; $i<$need_count; $i++) { my $w = int(rand $weight)+1; # выбираем точку на оси весов for (my $j=$i; $j<=$#res; $j++) { # ищем элемент, соотв. точке if ($res[$j]->{weight} >= $w) { $weight -= $res[$j]->{weight}; ($res[$i], $res[$j]) = ($res[$j], $res[$i]) if $i != $j; last; } else { $w -= $res[$j]->{weight}; } } } @res = splice(@res,0,$need_count); # обрезаем массив