Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Рандом с вероятносью, хелп пж (https://javascript.ru/forum/misc/81553-random-s-veroyatnosyu-khelp-pzh.html)

LimonDK 14.12.2020 16:02

Рандом с вероятносью, хелп пж
 
Допустим есть функцию randomChanses(map)
Т.е. на входе у нас словарь, в котором ключ-символ, а значение-вероятность появление этого символа. Т.е. допустим есть такой набор:
{'a'=10,'b'=20,'c'=30}. Т.е. появление a 1к10, b 1к20, с 1к30. Помогите накидать функцию, которое бы возвращала рандомное значение, учитываю вероятность. Была идея просто создать список, в который поместить 'a' 10 раз, 'b' 20 раз и т.д. Но это не совсем то, да и если шансы будут 1к1000, то слишком долго будет создаваться список.

Alexandroppolus 14.12.2020 16:44

Цитата:

Сообщение от LimonDK
{'a'=10,'b'=20,'c'=30}

что означают эти числа?

Цитата:

Сообщение от LimonDK
функцию, которое бы возвращала рандомное значение

значения как должны выглядеть?

в общем, поподробнее бы условие.

рони 14.12.2020 16:49

LimonDK,
const fun = n => n < 0.16 ? 'a' : n < .5 ? 'b' : 'c';
alert(fun(Math.random()));

voraa 14.12.2020 16:53

рони,
Цитата:

Сообщение от LimonDK
.е. появление a 1к10, b 1к20, с 1к30.

Как это понимать?
30 раз запустили.
В среднем должно быть 1 раз с, 1.5 раза b, 3 раза а. Разве не так?

рони 14.12.2020 17:19

voraa,
Цитата:

Сообщение от LimonDK
Была идея просто создать список, в который поместить 'a' 10 раз, 'b' 20 раз и т.д

<script>
const fun = n => n < 0.16 ? 'a' : n < .5 ? 'b' : 'c';
const obj = {};
for (let i = 0; i < 60; i++) {
   let key = fun(Math.random());
   obj[key] = 1 + (obj[key] >> 0);
}
document.write(JSON.stringify(obj, "", 4))
  </script>

Alexandroppolus 14.12.2020 17:20

Цитата:

Сообщение от LimonDK
Была идея просто создать список, в который поместить 'a' 10 раз, 'b' 20 раз и т.д.

кажется, понял.
потом из этого списка равновероятно выбираем элемент, и он оказывается одним из символов.
это как-раз то, что рони сделал. В общем виде примерно так:

function rnd(map) {
    const keys = Object.keys(map);
    if (!keys.length) {
        return undefined;
    }
    const sum = keys.reduce((s, key) => s + map[key], 0);
    const r = Math.random() * sum;
    let acc = 0;
    for (let i = 0; i < keys.length; ++i) {
        acc += map[keys[i]];
        if (acc > r) {
            return keys[i];
        }
    }
    return keys[keys.length - 1];
}

// для проверки рандомности
function getStat(map, iters) {
    const stat = Object.create(null);
    Object.keys(map).forEach(key => { stat[key] = 0; });
    for (let i = 0; i < iters; ++i) {
        const v = rnd(map);
        stat[v]++;
    }
    Object.keys(map).forEach(key => { stat[key] = stat[key] / iters; });
    return stat;
}

alert(JSON.stringify(getStat({'a': 10,'b': 20,'c': 30}, 40000), '', 4));

LimonDK 14.12.2020 17:37

Цитата:

Сообщение от voraa (Сообщение 531713)
рони,

Как это понимать?
30 раз запустили.
В среднем должно быть 1 раз с, 1.5 раза b, 3 раза а. Разве не так?

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

Alexandroppolus 14.12.2020 18:23

то есть вероятности - это на самом деле обратные значения от тех, что в словаре?
гарантируется, что сумма этих обратных значений всегда не больше 1 ?

рони 14.12.2020 18:40

Цитата:

Сообщение от LimonDK
Вот такое хотелось сделать, только не особо понимаю, как каждый раз определять вероятности.

a : 10, b : 20, с : 30
находим сумму всех весов 10 + 20 + 30 = 60//порядок не имеет значения
первый if 10 * 1/60
второй if (10 + 20) * 1/60
else всё остальное

итого
if(n < 0.16) return 'a';
else if(n < .5) return 'b';
else return 'c'

для примера другой порядок с : 30 , a : 10, b : 20
30 * 1/60
(30 + 10) * 1/60

if(n < .5) return 'c';
else if(n < .66) return 'a';
else return 'b'

voraa 14.12.2020 18:51

Тогда с таким же успехом можно было написать
{a:1, b: 2: c:3}
Никакой разницы.
Но между 1/2 и 1/20 для b все таки есть разница.


Часовой пояс GMT +3, время: 11:10.