Создание псевдослучайных последовательностей символов

Потребовалось мне не так давно написать генератор псевдослучайных последовательностей символов (по образцу ID видеоролика на Youtube) с двумя требованиями – большой диапазон значений (минимум 1 миллиард) и неповторяемость в пределах этого диапазона.

Решение оказалось достаточно простым, думаю, что аналоги известны, но я пришёл к этому варианту самостоятельно, независимо от того, что уже существует. Вообще, генерация песвдослучайных последовательностей – занятие с точки зрения математики весьма интересное, к тому же это неплохая разминка для мозга.

Изначально диапазон значений планировался равным одному миллиарду (это немного меньше, чем 2 в степени 30), но потом было решено расширить его до 2 в степени 36 – около 68 миллиардов. Для обозначения идентификаторов используются цифры, большие и малые английские буквы, а также символы минус и подчёркивание – итого 64 символа, что тоже достаточно удобно, идентификатор получается из 6 знаков.

То есть, мы берём число в диапазоне от 0 до 2^36 - 1, переводим его в двоичную форму, разбиваем на группы по 6 бит и каждой группе сопоставляем символ из нашего диапазона. Всё просто, но есть один момент – последовательность будет иметь вид «AAAAAA», «AAAAAB» и так далее, что, по сути, является теми же числами. Алгоритм в таком случае легко раскрывается и смысл в этом пропадает.

Второй шаг оказался не сложнее первого. Математического обоснования приводить не буду, кому интересно, может поискать в сети учебники. Суть в том, что мы наш номер умножаем на число, взаимно простое с верхней границей диапазона (2^36), берём остаток от деления на значение этой границы и уже полученный результат разбиваем на группы по 6 бит. Главная особенность в том, что пока мы не переберём все 68 с лишним миллиардов чисел, остаток повторяться не будет, причём это обусловлено именно использованием взаимно простых чисел. А так как верхняя граница у нас является степенью двойки и иных делителей не имеет, взаимно простым будет любое нечётное число.

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

В результате код на PHP (на другие языки переписать несложно) получился таким:

function getUID($x) {
	$sym = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; // 64 (!) символа 
	$range = 68719476736; // Максимальное значение диапазона (не включая), 2^36
	$delta = 46913843653; // Шаг генерации псевдослучайных чисел

	$result = '';

	$k = gmp_mul($delta, $x);
	$x = gmp_mod($k, $range);

	for ($i = 0; $i < 6; $i++) {
		$j = intval(gmp_mod($x, 64));
		$result = substr($sym, $j, 1) . $result;
		$x = gmp_div($x, 64);
	}

	return $result;
}

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

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *