vitvad
@vitvad

Какие есть алгоритмы для выдачи результата из заданого списка с определенной вероятностью?

Задача:
Есть исходный набор данных в виде массива. Для каждого элемента массива указана вероятность его появления в результате.
Нужно написать функцию которая каждый раз при вызове будет возвращать один случайный элемент из массива с заданой вероятностью.

примеры применения:
- AB тестирование - есть 3 страницы которые должны показываться пользователям при заходе на сайт с вероятность 50%, 35% и 15% соответственно
- Словарь который будет показывать одно из слов содержащихся в памяти каждые 3 минуты. При этом новые слова будут показываться чаще чем старые.

Мне в голову приходит только решение в "лоб". УПРОЩЕННЫЙ вариант ниже (JavaScript):
function probabilityRandom(chanceA, chanceB, chanceC, items) {
	var totalWeight = chanceA + chanceB + chanceC;
	// generate random number in range from 1 to `totalWeight`
	var tmp = 1 + (Math.random() * totalWeight);

	if(0 < tmp && tmp <= chanceA){
		return items[0];
	}
	if(chanceA < tmp && tmp <= chanceA + chanceB){
		return items[0];
	}

	if(chanceA + chanceB < tmp && tmp <= chanceA + chanceB + chanceC){
		return items[0];
	}
}

probabilityRandom(50, 35, 15, ["first", "second", "third"]);


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

PS.
41591611cffe4cad8efbace0fd7d6059.gif
  • Вопрос задан
  • 1124 просмотра
Решения вопроса 1
barmaley_exe
@barmaley_exe
Метод Уолкера (см. тут, снизу). Хорош тем, что после линейного предподсчёта можно отвечать на запрос за O(1).
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
bobrovskyserg
@bobrovskyserg
Самая сложная математика:
на оси x откладываете отрезки:
[0.0 .. 0.2) - Вася
[0.2 .. 0.5) - Петя
[0.5 .. 1.0) - Кузя
Генерите random в диапазоне 0 .. 1
На чей отрезок выпало - тому и водить.
Код на Питоне:
from random import random
from bisect import bisect_left

data = {"Вася": 2,
        "Петя": 3,
        "Кузя": 5}

scalefactor = sum(data.values())
names, scale = [], [0.]
for name, weight in data.items():
    names.append(name)
    scale.append(scale[-1] + weight / scalefactor)

#  посмотрим, хорошо ли работает
counter = {name: 0 for name in names}

for i in range(10000):
    x = random()
    idx = bisect_left(scale, x) - 1
    name = names[idx]
    counter[name] += 1
print(counter)
Ответ написан
Adamos
@Adamos
Самая простая математика: генерите ДВА случайных числа.
Одно - индекс элемента в массиве.
Второе - от 0 до 1, сравниваете со значением вероятности для этого элемента, заданным в том же виде.
Если сгенерированное больше заданного - повторяете алгоритм.
Ответ написан
k12th
@k12th
console.log(`You're pulling my leg, right?`);
Probability and Games, есть псевдокод
github://jotson/LootTable.js, имплементация на JS.
Ответ написан
@DISaccount
Элементарно, Ватсон.
С Вашего позволения, я переформулирую задачу.
У Вас имеется гистограмма (частоты появления) элементов. Вам необходимо, чтобы на тысячу экспериментов, у Вас частота появления заданного элемента, приближалась к некой заданной частоте из гистограммы. Тогда, берете произвольный промежуток, скажем от 1 до 1000, и делите его в пропорциях, согласно Вашей гистограмме. Например. У Вас 2 элемента: один появляется с частотой 2/3, второй с частотой 1/3. Тогда делим промежуток от 1-1000 на 2 куска, один от 1 до ~300, второй от ~301 до 1000. В зависимости от того, на какой участок попала случайно сгенерированная величина, тот элемент и берете.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы