Читабельно

Круговой random() для самых редких черт

Представьте, что вы делаете генеративный проект. Хотите, чтобы работа имела одну из 8 черт, и пишете так:

let options=['🍒','🍓','🍇','🍎','🍋','🍍','🍏','🥥',]
let optionsIndex = Math.floor(Math.random() * options.length)
let trait = options[optionsIndex]

Запускаете скрипт много раз и смотрите статистику:

let options = ["🍒", "🍓", "🍇", "🍎", "🍋", "🍍", "🍏", "🥥"];
let stats = Array(8).fill(0)
for (let i = 0; i < 100; i++) {
  let optionsIndex = Math.floor(Math.random() * options.length);
  let trait = options[optionsIndex];
  stats[optionsIndex] ++
}
for(let i=0;i<8;i++){
  console.log(options[i].repeat(stats[i]))
}

Да, вы правы, строка #5 бесполезна

Вот что получается:

🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒
🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓
🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇
🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎
🍋🍋🍋🍋🍋🍋🍋🍋🍋🍋🍋🍋🍋
🍍🍍🍍🍍🍍🍍🍍🍍🍍🍍🍍
🍏🍏🍏🍏🍏🍏🍏🍏🍏🍏🍏🍏🍏
🥥🥥🥥🥥🥥🥥🥥🥥🥥🥥🥥🥥🥥

Яблокам повезло в этот раз

Потом вы решаете, что кокосы должны быть реже вишен. Нет, кокосы должны быть самой редкой чертой в проекте! Один на сотни! Поэтому меняете функцию random на возведённую в квадрат:

let optionsIndex = Math.floor(Math.random()**2 * options.length);

И получаете распределение вроде такого:

🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒
🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓
🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇
🍎🍎🍎🍎🍎🍎🍎
🍋🍋🍋🍋🍋🍋🍋🍋
🍍🍍🍍🍍🍍🍍🍍🍍🍍
🍏🍏🍏🍏🍏🍏
🥥🥥🥥🥥🥥🥥🥥

Яблоки в пролёте

Вишни теперь самые частые! Здорово, да? Стоп, кокос должен был быть самым редким, но он такой же редкий, как и нижняя половина списка!

Давайте посмотрим, почему так происходит:

Видите, область вишни на горизонтальной оси огромная, а область кокоса такая же широкая, как у соседей.

Виновата парабола! Нам нужен круг:

let optionsIndex = Math.floor((1-Math.sqrt(1-Math.random()**2)) * options.length);

Вот как это отличается:

Обратите внимание, какая крошечная полоска у кокоса! Запустим тест ещё раз:

🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒
🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓🍓
🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇🍇
🍎🍎🍎🍎🍎🍎🍎
🍋🍋🍋🍋
🍍🍍🍍🍍
🍏🍏
🥥

Результаты теста кругового рандома

Нам повезло получить хотя бы один кокос, чаще всего их вообще нет.

Мы справились, кокосы теперь супер редкие, ура.