The Time Machine by H. G. Wells

Прочитал то, что должен был прочесть еще давно – роман Герберта Уэллса “Машина Времени”.

Сюжет описывает деградацию и упадок человечества далёкого будушего – к 800 000 году люди пришли к двум разным эволюционным путям: к элоям – расе добрых беспечных, глупых беспомощных существ, которые весь день поют, танцуют и смеются, не работая и не имея никаких забот. И к морлокам – злобным жестоким подземным тварям, живущим в ночи и питающимися элоями (в буквальном смысле). Роман считается классикой научно-фантастического жанра, хотя на мой взгляд, в нём больше политико философского.
Новелла написана в далёком 19 веке, в контексте классового расслоения того времени между рабочими и буржуазной элитой в британском обществе, но в наше время она выглядит ещё более актуальной, на мой взгляд.
Сейчас, по прошествии времени, стало очевидно, что в постиндустриальном обществе классовые различия в странах первого мира стираются с каждым поколением, и рабочий класс уже перестал быть низшим, перейдя на ступень среднего класса. Сейчас роли морлоков и элоев отведены не классам, а цивилизациям. Элои сегодняшнего времени – это население золотого миллиарда, представители европейской цивилизации. Так же, как в романе, они беспечны, наивны, слишком добры,  миролюбивы, склонны к травоядной жизни, трусости и к моральному упадку. С другой стороны мы имеем другие цивилизации – русскую (которая отдаляется от европейской вот уже 100 лет подряд), африканскую, исламскую и китайскую. Это и есть те самые морлоки, описанные Уэллсом, которые постепенно пожирают представителей деградировавшего золотого миллиарда.
Во время чтения у меня не было пиета ни в сторону злобных ночных каннибалов, ни в сторону улыбчивых солнечных дегенератов.  Две эти эволюционные ветви человечества имели один вектор направления – направление в пропасть.  К “элоям” и “морлокам” нашего времени у меня точно такое же отношение.

Генерирование случайных слов в JS

Допустим, у нас возникла идея создать скрипт на JS, который бы генерировал случайные слова (никнеймы).

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

  • srjxdq
  • moyssj
  • ywtckmw
  • wjvzw
  • xtwey

и т.д.

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

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

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

screenshot_0.png

Разберём 2-й пункт поподробнее.

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

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

equation

const items = [ 'a', 'b', 'c' ]; 
const weights = [ 3, 7, 1 ];
  • Подготавливаем массив весов посредством кумулятивного сложения (то есть список cumulativeWeights, который будет иметь то же количество элементов, что и исходный список весов weights). В нашем случае такой массив будет выглядеть следующим образом:
cumulativeWeights = [3, 3 + 7, 3 + 7 + 1] = [3, 10, 11]
  • Генерируем случайное число randomNumber от 0 до самого высокого кумулятивного значения веса. В нашем случае случайное число будет находиться в диапазоне [0..11]. Допустим, что randomNumber = 8.

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

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

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

Read more