- Конгруэнтные генераторы. Линейные конгруэнтные генераторы.
- Линейные конгруэнтные генераторы (ЛКГ)
- Длина периода
- \(m\) простое, \(c=0\)
- \(m=2^n\) , \(c=0\)
- \(c\neq 0\)
- Теорема Халла-Добелла
- Следствия теоремы
- Генерация случайных чисел
- Генерация псевдослучайных чисел
- Линейный конгруэнтный (linear congruential) метод генерации случайных чисел
- Выбор m
- Множитель a
- Инкремент c
- Начальное значение (seed)
- Реализация генерации случайных чисел
- Простые числа
- Код Грея
- Простые числа Мерсенна
- Поразрядные логические операции
- Степень числа
- Другие генераторы случайных чисел
- Упражнения:
Конгруэнтные генераторы. Линейные конгруэнтные генераторы.
Все конгруэнтные генераторы используют для получения нового состояния рекуррентную формулу вида \[s_ = f(s_i) \mod m,\] где \(m\) – модуль, являющийся положительной целой степенью простого числа. Выходная последовательность обычно совпадает с последовательностью состояний.
Семейство конгруэнтных генераторов определяется общим видом функции \(f: S\to S\) , например, линейные конгруэнтные генераторы используют линейную функцию \(f\) . Конкретный генератор в семействе определяется конкретными параметрами функции \(f\) .
Линейные конгруэнтные генераторы (ЛКГ)
ЛКГ используют рекуррентную формулу вида \[s_ = a\cdot s_i + c \mod m,\] где \(m\) – модуль, \(s_i\) – состояния, \(a\) – “множитель”, \(c\) – “приращение”. Так же необходимо задать \(s_0\) – начальное состояние.
Конкретный генератор из семейства задаётся значениями \(a\) , \(c\) , \(m\) .
Если \(c = 0\) , такие генераторы называются мультипликативными, или генераторами Лемера.
Длина периода
Удачный выбор параметров ЛКГ даёт известную и достаточно большую длину периода. Однако, на практике, неудачный выбор параметров сделать гораздо проще, чем удачный. Так, например, при \(a=1, c=1\) , ЛКГ превращается в обычный счётчик по модулю \(m\) , который явно не похож на случайную последовательность.
Исторически, неудачный выбор параметров приводил к крайне сомнительным результатам. Одним из примеров является генератор RANDU, разработанный в IBM и широко использовавшийся в 1970-х годах (в частности, компиляторами FORTRAN). В нём использовались параметры \(a=65539=2^<16>+3, c=0, m=2^<31>\) , и чётным начальным значением. Хотя этот метод даёт равномерно распределённые значения, они крайне очевидно не являются независимыми. Действительно, рассмотрим последовательность 3 значений \(\
\[s_
То есть каждое третье значение однозначно определяется двумя предыдущими. По сути, это уравнение описывает плоскость в 3-мерном пространстве, и если рассматривать каждые три последовательных числа как координаты точки, то все эти точки будут лежать на такой плоскости (технически, из-за модульной арифметики, это семейство 15 плоскостей)
Нормированное распределение точек в RANDU
Существует три варианта выбора параметров. Рассмотрим каждый из них отдельно.
\(m\) простое, \(c=0\)
Этот случай соответствует генератору Лемера. Если \(a\) является примитивным элементом мультипликативной группы целых по модулю \(m\) , то период генератора очевидно равен \(m-1\) . Начальное состояние \(s_0\neq 0\) .
Один из недостатков простого модуля в том, что при использовании двоичной арифметики при вычислениях, в отличие от случая \(m=2^n\) , необходим явный шаг вычисления модуля, и двойной объем памяти при умножении.
Второй недостаток заключается в том, что если необходимо получить равномерно распределённую последовательность бит, числа в диапазоне \([1,m)\) не всегда тривиально можно преобразовать.
В качестве \(m\) часто выбираются числа Мерсенна, например \(2^<31>-1\) или \(2^<61>-1\) .
Для вычисления в ограниченном числе разрядов можно применить алгоритм Шража 1 . Представим \(m\) в виде \[m = qa+r,\] \(q = \left\lfloor \frac
Затем рассчитаем \[as \mod m = a(s \mod q) − r \left\lfloor \frac \right\rfloor\]
Поскольку \(s\mod q , первый член строго меньше чем \(am/a = m\) . Если выбрать \(a\) таким образом, чтобы \(r \le q\) (т.е. \(r/q \le 1\) ), то второй член так же меньше \(m\) : \[r \left\lfloor \frac \right\rfloor \le r\frac
≤ x
Тогда оба этих члена можно вычислить в том же числе разрядов, в которых представимо \(m-1\) , а их разность лежит в пределах \([1−m, m−1]\) , который легко привести к диапазону \([0, m−1]\) , просто прибавляя \(m\) , если первый член меньше второго.
\(m=2^n\) , \(c=0\)
Если \(m=2^n\) , то при использовании двоичной арифметики в \(n\) разрядах, вычисление резко упрощается. Действительно, поскольку беззнаковые вычисления в \(n\) разрядах по сути являются арифметикой \(\mod n\) , для вычисления достаточно одного умножения.
Есть, однако, обратная сторона.
Возьмём ЛКГ \[s_ = as_i \mod 2^n\] и рассмотрим младшие \(l\) бит состояния \(s_i\) : \[t_i = s_i \mod 2^l.\] Тогда \[t_ = (as_i \mod 2^n) \mod 2^l\] \[t_ = at_i \mod 2^l,\] и тогда младшие \(l\) бит тоже образуют линейную конгруэнтную последовательность с меньшим модулем. А поскольку период меньше модуля, то в лучшем случае, младший бит никогда не меняется, второй по старшинству бит меняется каждый раз, и так далее. Таким образом младшие биты оказываются значительно менее “случайными”, чем старшие.
\(c\neq 0\)
При корректном выборе параметров и \(c\neq 0\) , длина периода может достигать \(m\) .
Теорема Халла-Добелла
ЛКГ, задаваемый рекуррентным соотношением \[s_ = a s_i + c \mod m,\] \(c \neq 0\) достигает периода \(m\) iff:
- \(m\) и \(c\) взаимно простые
- \(a-1\) делится на все простые множители \(m\)
- \(a-1\) делится на 4, если \(m\) делится на 4.
Лемма 1
Пусть \(p\) – простое число, а \(e\) – положительное целое число, такое, что \(p^e > 2\) . Если \[x = 1 \mod p^e,\] \[x\neq 1 \mod p^
\(\Delta\) : По условию теоремы, \(x = 1 + qp^e\) для некого целого \(q\) . Тогда \[x^p = (1+qp^e)^p = 1 + C_p^1 qp^e + \ldots + C_p^
Последний член \(q^
Таким образом, все члены в скобках кроме \(1\) делятся на \(p\) и тогда \[x^p = 1+qp^
Пусть число \(m\) раскладывается на простые множители \[m=p_1^
\(\Delta\) : Используя индукцию по \(t\) , достаточно доказать лемму для \((s_0, a, c, m_1m_2)\) , где \(m_1\) и \(m_2\) – взаимно простые. Тогда \(\lambda\) будет НОК \(\lambda_1\) последовательности \((s_0 \mod m_1, a \mod m_1, c \mod m_1, m_1)\) и \(\lambda_2\) последовательности \((s_0 \mod m_2, a \mod m_2, c \mod m_2, m_2)\) . Отметим, что если элементы этих последовательностей обозначены соответственно \(x_i\) , \(y_i\) , \(z_i\) , то справедливы равенства \[y_n = x_n \mod m_1,\] \[z_n = x_n \mod m_2.\] Действительно, \[y_0 = x_0 \mod m_1,\] \[y_1 = (a y_0 + c) \mod m_1 = (a x_0 + c) \mod m_1,\] по индукции, \[y_n = (a x_
Аналогично для \(z_n\) .
Тогда \(\forall k\) , \[x_n = x_k \mod m_1m_2 \iff\] \[y_n = y_k \mod m_1,\] \[z_n = z_k \mod m_2.\]
Пусть теперь \(\lambda’\) – НОК( \(\lambda_1\) , \(\lambda_2\) ). Докажем, что \(\lambda’ = \lambda\) . Так как \(x_n = x_
Из леммы 2 следует, что теорему достаточно доказать для случая, когда \(m\) – степень простого числа, поскольку \[m = p_1^
Поэтому положим \(m=p^e\) , где \(p\) – простое, а \(e\) – натуральное. Для \(a=1\) утверждение теоремы достаточно очевидно, поэтому рассмотрим случай \(a>1\) . Период имеет длину \(m\) iff каждое целое \(0\le x встречается в последовательности ровно один раз. Действительно, никакое значение не может встретиться более одного раза и значений \(\mod m\) ровно \(m\) . Тогда без ограничения общности можем положить \(s_0 = 0\) . Рассмотрим общую формулу для \(s_n\) : \[\begin
Пусть \(1 , где \(p\) – простое число. Если \(\lambda\) есть наименьшее натуральное число, для которого \[\frac
Положим \(\lambda=p^e\) и при этом \(a\neq 1 \mod p\) . Тогда \[\frac = 1 \mod p^e,\] но по теореме Ферма \(a^n = a \mod n\) , что приводит к противоречию. Если \(p=2\) и \(a\neq 1 \mod 4\) , то \(a=3\mod 4 \neq 1 \mod 2^e\) для \(e>1\) и \[a^ <2^e>= 1 \mod 2^e.\] Следовательно, чтобы \(\lambda=m\) , условия (2,3) теоремы необходимы. То есть, необходимо, чтобы \(a=1+qp^f\) , где \(p^f>2\) и \(q\) не кратно \(p\) . Остаётся доказать, что это условие достаточно. Применяя лемму 1, находим, что \(\forall g\ge 0\) : \[a^ = 1 \mod p^ \neq 1 \mod p^ Из доказательства теоремы Халла-Добелла прямо следуют так же выражения максимального периода для случая \(c=0\) . Действительно, если \(c=0\) , то \(x_n = a^n x_0 \mod m\) . По лемме 2, период любой ЛКП зависит от длин последовательностей при \(m=p^e\) . Если \(x_n = a^n x_0 \mod p^e,\) и \(a\) кратно \(p\) , период будет иметь длину не более \(e\) . Поэтому рассмотрим случай, когда \(a\) и \(p\) – взаимно простые. Тогда период \(\lambda \in \mathbb N\) – это наименьшее число, такое, что \[x_0 = a^\lambda x_0 \mod p^e.\] Если теперь \(p^f = НОД(x_0, p^e)\) , то это требование эквивалентно \[a^\lambda = 1 \mod p^ Это позволяет определить \(\lambda(m)\) : \[\lambda(m) = \left\lbrace\begin Вышеперечисленные рассуждения можно кратко сформулировать следующим образом. Максимальным периодом, возможным при \(c=0\) является \(\lambda\) , определённое в \((\text<*>)\) . Этот период достигается, если: В случае \(m=2^e\) , имеем \[\lambda = p^ Источник Дата создания: 2009-05-03 15:27:38 Очень часто при разработке программ возникает необходимость в последовательности случайных чисел. В играх, последовательность случайных чисел может использоваться в разнообразнейших ситуациях: создание монстров, генерация территории, поведение искусственного интеллекта. Вместо использования в программах последовательности чисел, подчиняющихся какому-либо закону, во многих случаях лучше использовать случайно сгенерированные. Обычно используется генерация псевдослучайных чисел. Т.е. числа не совсем случайные. Мы уже рассматривали функцию rand(). Если Вы напишите программу использующую данную функцию, то при каждом запуске программы, rand() будет генерировать одну и ту же последовательность чисел. Последовательность сгенерированная rand() определяется начальным числом (seed). Сначала задаётся начальное число, затем, по определённой формуле вичисляются все остальные числа последовательности. Зная начальное число и формулу по которой рассчитываются числа, можно вычислить следующее число. Такие алгоритмы называются детерминистическими (предопределёнными). Т.е. в них последовательность чисел создаётся на основе начальных данных. При этом последовательность чисел — псевдослучайная: т.е. зная начальное число, можно узнать и все следующие. Но пользователь навряд ли их узнает, числа будут казаться случайными. Одним из предопределённых (детерминистических) алгоритмов создания случайных чисел является линейно-конгруэнтный. Установка начального значения (для функции rand(), которую мы использовали до сих пор) выглядит примерно так: Функция srnd устанавливает начальное значение i. Существует много методов генерации случайных чисел. Линейно-конгруэнтный лишь один из них. Метод довольно старый — 1950х годов. Разработал его Деррик Лемер. Для реализации алгоритма необходиом задать четыре параметра: Диапазон значений m, при этом m > 0. Вичислим шестой элемент с помощью этой формулы (мы ведь уже знаем пятый): X5+1 = (a*1*X5 + (a*1-1)*c/b) % m = (2*1*5 + (2*1-1)*3/1) % 10 = 13 % 10 = 3 Всё росто, правда? У последовательности созданной с помощью линейного конгруэнтного метода и определённой целыми параметрами m, a, c и X, период равен числу m когда выполняются следующие условия: Период не может быть больше числа m. Следовательно m должно быть довольно большим. Примеры (x — целое число): Очень часто для m выбирают одно из простых чисел Мерсенна [3] . Часто число 2 31 — 1 используется, когда вычисления ведутся с 32-ух битными данными. Множитель нужно выбирать так, чтобы период был как можно длиннее. Существует серьёзная проблема с данным коэффициентом: при малых значениях a, если текущий элемент последовательности достаточно мал, вполне вероятно, что следующий элемент тоже будет маленьким. Данный параметр можно выбирать довольно произвольно. Очень часто его задают в виде нуля, но при этом уменьшается длина периода и X != 0. Итак, мы выяснили, что в последовательности есть период. В нашем примере период равен всего четырём элементам. В других случаях период может быть очень большим, но он всегда есть! То есть при генерации случайных чисел линейным конгруэнтным методом если нам нужна очень большая последовательность, то в ней значения будут повторяться с определённой периодичностью. Вот так вот! Генерация последовательных чисел — это самая настоящая мистификация! Т.е. у нас допустим есть генератор случайных чисел. Мы его используем для разных программ. Всегда, всегда этот генератор создаёт одинаковую последовательность чисел (при одинаковых начальных значениях). Мы можем только установить начальное значение. Вообще говоря, с помощью арифметических методов нельзя построить по настоящему случайную последовательность чисел. Как невесело шутил наш друг — Джон Фон Нейман: «Anyone who uses arithmetic methods to produce random numbers is in a state of sin.» (C) Джон Фон Нейман. Грустно всё это. Вот так живёшь-живёшь. Находясь в состоянии неведения, думаешь: «А вот круто бы было создать генератор случайных чисел на основе линейного когнруэнтного метода!». А оказывается что по настоящему случайную последовательность таким образом создать нельзя. Крах надежд! Депрессия и вот ты уже на пороге первой стадии алкоголизма. Этот груз невозможности реализовать свои желания будет давить на тебя всю оставшуюся жизнь. Что-то я отвлёкся. Продолжим. Теперь, когда мы рассмотрели теоретические вопросы, касающиеся генераторов случайных чисел, давайте посмотрим на реализацию, тем более она довольно проста. Для последовательности рассмотренной выше, генератор будет выглядеть так: X объявляется как глобальная переменная: Это самый примитивный вариант генератора. В реальности такое чудовище использовать нельзя! Но мы будем это делать! В данной реализации мы никак не отслеживаем возможность переполнения переменных. Вместо типа int используйте тип _int64 — это целый восьмибайтный тип. Простые числа — числа которые можно разделить без остатка только на единицу и на само число. Например: 11, 3, 7. [1] Код Грея представляет собой последовательность чисел, где следующее число отличается от предыдущей одним битом. Т.е. чтобы угадать последовательность, нужно смотреть не на десятичные числа, а на их двоичные эквиваленты. Вот возрастающая последовательность чисел от 0 до 7 в двоичном коде: Используя код Грея, получим такую последовательность: [3] На данный момент известно 44 числа Мерсенна. Из названия понятно, что данные числа — простые, т.е. делятся только на единицу и на самих себя. Кроме того данные числа должны подчиняться следующей формуле: Где n также простое число. Для первых девяти простых чисел Мерсенна n следующие: <2, 3, 5, 7, 13, 17, 19, 31, 61>. [2] Мы уже рассматривали логические операции && (И) и || (ИЛИ). Существуют также поразрядные логические операции & (поразрядное И) и | (поразрядное ИЛИ). Работате операция следующим образом: То есть в данной операции сравниваются соответствующие позиции двух чисел и если они равны, то в результируюем числе в данной позиции пишется 1, в противном случае — ноль. Кроме линейного конгруэнтного генератора существует множество других. Например Вихрь Мерсенна, изобретённый двумя японскими учёными (непомню как из зовут) в 1997. У него очень большой (очень большой это мягко сказано) период. Кстати, вихрь Мерсенна использует линейный конгруэнтный генератор для установления начального значения (seed). Генераторы случайных чисел применяются при шифровании. Но здесь используются специальные генераторы — криптографически защищённые генераторы псевдослучайных чисел. Например блочный шифр (block cipher), потоковый шифр (stream cipher), который был разработан на основе шифра Вернама. Реализуйте генераторы случайных чисел на основе линейного когруэнтного метода. В качестве параметров воспользуйтесь следующими: Данный материал поможет вам понять как работают генераторы случайных чисел. К этому материалу будет продолжение: мы напишем нормальный генератор и рассмотрим различные приёмы работы с генераторами псевдослучайных чисел. ИсточникСледствия теоремы
Генерация случайных чисел
Последний раз редактировалось: 2012-02-08 06:53:25Генерация псевдослучайных чисел
Линейный конгруэнтный (linear congruential) метод генерации случайных чисел
Множитель a (0 = 2, b >= 1.
1.Наибольший общий делитель c и m равен 1.
2.b — кратно любому простому числу, являющемуся делителем m.
3.Если m кратно 4, тогда b также кратно 4.Выбор m
Множитель a
Инкремент c
Начальное значение (seed)
Реализация генерации случайных чисел
Простые числа
Код Грея
Простые числа Мерсенна
Поразрядные логические операции
Степень числа
Другие генераторы случайных чисел
Упражнения: