Word generator based on random-weighted letters
Go to file
2022-04-16 18:18:01 +05:00
screenshots adding screenshots 2022-04-16 18:18:01 +05:00
index.html adding a feature to select the number of vowels and consonants in a row 2022-04-16 18:17:25 +05:00
LICENSE Initial commit 2022-04-14 21:46:46 +05:00
README.md addition to README 2022-04-16 18:15:19 +05:00
wordgen.js adding a feature to select the number of vowels and consonants in a row 2022-04-16 18:17:25 +05:00

nickname-generator

Данный скрипт является примером использования на практике алгоритма случайного выбора элементов массива с учётом веса.

В данном случае практикой будет являться генерирование случайных слов (никнеймов) на основе букв английского (латинского) алфавита.

Начнём для начала с самого простого подхода. Если мы просто будем брать случайные буквы и составлять их них слова, то они будут выглядеть неестественно и неприглядно. Примеры:

  • srjxdq
  • moyssj
  • ywtckmw
  • wjvzw
  • xtwey

и т.д.

Как видим, такой подход не позволяет нам генерировать слова, которые хотя бы отдалённо напоминали обычные - получается просто набор бысмысленных букв, который больше походит на пароли. Чтобы придать словам натуральность и "человечность", нам нужно сделать как минимум две вещи (на мой взгляд):

  1. Исключить появления более двух гласных/согласных при генерировании слова. Данная задача является тривиальной и ее не имеет смысла рассматривать.
  2. Подбирать случайные буквы для слова с учётом их веса. Весами в данном случае будут являться частотность букв в английском языке. Таким образом мы должны уменьшить/увеличить шанс того, что определенная буква попадёт в наше генерируемое слово, и таких редко используемых букв, как, например, Q, Z и X будут встречаться в наших словах гораздо реже, чем E, T, A, O, I, которые по статистике являются самыми частыми в английских словах.

Используя всего два этих подхода, мы генерируем гораздо более натуральные "слова". Примеры:

  • haetise
  • earyn
  • sayuri
  • oathe
  • aulnelo
  • ureon
  • reirgas
  • theele

и т.д.

Алгоритм выбора случайных элементов массива на основе весов в JS

Относительно простой имплементацией подобного алгоритма является преобразование ряда рациональных чисел s1 (массива), являющимися весами для элементов, в ряд чисел s2, который получается посредством кумулятивного сложения чисел:

equation

где Sn - это массив со значениями весов, ai - элементы этого массива.

Разберём алгоритм по шагам в JS:

  1. Создаём в качестве примера два массива items и weights, где items - это элементы, которые будут выбираться случайно, а weights - это весы этих элементов:

const items = [ '🍌', '🍎', '🥕' ];

const weights = [ 3, 7, 1 ];

  1. Подготавливаем массив весов посредством кумулятивного сложения (то есть список cumulativeWeights, который будет иметь то же количество элементов, что и исходный список весов weights). В нашем случае такой массив будет выглядеть следующим образом:

cumulativeWeights = [3, 3 + 7, 3 + 7 + 1] = [3, 10, 11]

  1. Генерируем случайное число randomNumber от 0 до самого высокого кумулятивного значения веса. В нашем случае случайное число будет находиться в диапазоне [0..11]. Допустим, что randomNumber = 8.

  2. Проходим с помощью цикла по массиву cumulativeWeights слева направо и выбираем первый элемент, который больше или равен randomNumber. Индекс такого элемента мы будем использовать для выбора элемента из массива элементов.

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

Попробую наглядно показать это на нашем примере:

 const weights =           [3, 7,  1 ]; 

 const cumulativeWeights = [3, 10, 11]; 

 // В псевдопредставлении мы можем представить cumulativeWeights следующим образом: 

 const pseudoCumulativeWeights = [ 

   1, 2, 3,               // <-- [3] числа 

   4, 5, 6, 7, 8, 9, 10,  // <-- [7] чисел 

 11,                    // <-- [1] число 

 ]; 

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

Элемент 3: ≈ 27%,

Элемент 7: ≈ 64%,

Элемент 1: ≈ 9%

Как можно еще лучше алгоритм генерации слов?

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

  • satlenl
  • tohhi
  • tiowh
  • aahepw

и т.д.

Самым простым решением этого вопроса является ограничение на чередование более двух гласных/согласных слов:

if (vowelCounter >= maxVowelsInRow) {
    i -= 1;
    continue;
}

и

if (consonantCounter >= maxConsonantsInRow) {
    i -= 1;
    continue;
}

Пусть значения maxConsonantsInRow = 1 и maxVowelsInRow = 1, тогда сгенерированые слова будут выглядеть примерно так:

Отметим, что th и ae являются диграмами, и считаются как одна буква.

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