ФГОБУ ВПО "СибГУТИ" Кафедра вычислительных систем ОСНОВЫ ПРОГРАММИРОВАНИЯ Случайные числа и генерация тестовых данных Преподаватель: Доцент Кафедры ВС, к.т.н. Поляков Артем Юрьевич © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» План лекции • Случайные числа и их применение в вычислительной технике. • Подходы к генерации случайных чисел. • Псевдослучайные числа и алгоритмы их формирования. • Статистическая проверка случайной природы чисел. • Тестирование программного обеспечения • Генерация входных данных © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 2 Применение случайных чисел Азартные игры и лотереи. Компьютерное моделирование Криптография Компьютерные игры © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 3 Генерация случайных чисел 1. Ранние способы генерации случайных чисел. Их большим недостатком является крайне низкая скорость генерации. 2. Более эффективными являются физические генераторы случайных чисел, которые используют природные шумы. HotBits Random.org © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 4 Генерация случайных чисел (2) 3. Генерация псевдослучайных чисел (ГСПЧ). Генератор псевдослучайных чисел, англ. Pseudo-random number generator (PRNG), это алгоритм, позволяющий генерировать длинные последовательности чисел, свойства которых близки к свойствам случайных чисел. Однако такие последовательности имеют период (повторяются с каким-то расстоянием). Middle-square method seed ← значение for i ← 0 to n do rnd[i] = (seed / 1000) % 1000000; seed = rnd[i]2; 4. Генерация согласно заданному распределению вероятностей. Такие методы в основном используют числа с равномерным распределением и известную функцию плотности некоторого распределения (например нормального). В зависимости от источника равномерного распределения результат будет либо псевдослучайным либо случайным. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 5 Линейный конгруэнтный генератор Генератор псевдослучайных чисел, англ. Pseudo-random number generator (PRNG), это алгоритм, позволяющий генерировать длинные последовательности чисел, свойства которых близки к свойствам случайных чисел. Однако такие последовательности имеют период (повторяются с каким-то расстоянием). Конкретная последовательность чисел обычно определяется "зерном" из которого "прорастает" вся остальная последовательность. Алгоритм PRNG для одинакового значения зерна будет всегда порождать одинаковую последовательность. Наиболее простым вариантом PRNG является линейный конгруэнтный генератор (Linear Congruential Generator – LCG), который описывается следующим рекуррентным соотношением: x0 = seed xn+1 = (a∙xn + b) mod m Период такого генератора (длина неповторяющейся последовательности) не превышает m. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 6 Линейный конгруэнтный генератор (2) Наиболее простым вариантом PRNG является линейный конгруэнтный генератор (linear congruential generator – LCG), который описывается следующим рекуррентным соотношением: x0 = seed xn+1 = (a∙xn + b) mod m Source m m, m > 0 – модуль (modulus); a, m > a > 0 – мультипликатор (multiplier); c, m > c ≥ 0 – инкремент (increment) x0 – случайное зерно. a c Borland C/C++ 232 glibc (GCC) Borland Delphi, Virtual Pascal 231 1103515245 12345 232 134775813 22695477 1 Биты xn, формирующие случайноге число rand(): 30..16 lrand(): 30..0 30..0 m = 2147413647 1 © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 63..32 7 Период ГСПЧ (PRNG) http://ru.wikipedia.org/wiki/Генератор_псевдослучайных_чисел Никакой детерминированный алгоритм не может генерировать полностью случайные числа, он может только аппроксимировать некоторые их свойства. Джон фон Нейман: « … всякий, кто питает слабость к арифметическим методам получения случайных чисел, грешен вне всяких сомнений … ». Любой ГПСЧ с ограниченными ресурсами рано или поздно зацикливается — начинает повторять одну и ту же последовательность чисел. Длина циклов ГПСЧ зависит от самого генератора и часто составляет около 2n / 2, где n — размер внутреннего состояния в битах. Линейные конгруэнтные и LFSRгенераторы (Регистр сдвига с линейной обратной связью (РСЛОС, англ. Linear feedback shift register) обладают максимальными циклами порядка 2n. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 8 Период ГСПЧ (PRNG) (2) http://ru.wikipedia.org/wiki/Генератор_псевдослучайных_чисел Большинство простых арифметических генераторов хотя и обладают большой скоростью, но страдают от многих серьёзных недостатков: • Слишком короткий период/периоды. • Последовательные значения не являются независимыми. • Некоторые биты «менее случайны», чем другие. • Неравномерное одномерное распределение. • Обратимость. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 9 С11. Pseudo-random sequence generation functions В стандарте языка Си (ISO/IEC 9899:2011, сокр. С11) предусмотрены следующие функций генерации псевдослучайных чисел: #include <stdlib.h> int rand(void); void srand(unsigned int seed); http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf C11 …The rand function computes a sequence of pseudo-random integers in the range 0 to RAND_MAX … The value of the RAND_MAX macro shall be at least 32767… GLibC: RAND_MAX = 2147483647 … The srand function uses the argument as a seed for a new sequence of pseudo-random numbers to be returned by subsequent calls to rand. If srand is then called with the same seed value, the sequence of pseudo-random numbers shall be repeated. If rand is called before any calls to srand have been made, the same sequence shall be generated as when srand is first called with a seed value of 1. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 10 С11. Pseudo-random sequence generation functions (2) В стандарте также приведен пример линейного конгруэнтного генератора, который может быть использован для генерации случайных чисел. C11 … EXAMPLE The following functions define a portable implementation of rand and srand. static unsigned long int next = 1; int rand(void) // RAND_MAX assumed to be 32767 { next = next * 1103515245 + 12345; return (unsigned int)(next/65536) % 32768; } void srand(unsigned int seed) { next = seed; } © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 11 GNU LibC http://www.gnu.org/software/libc/manual/html_node/Pseudo_002dRandom-Numbers.html#Pseudo_002dRandom-Numbers В реализации GNU LibC (GLibC) стандарта Си предлагается более широкий выбор функций генерации случайных чисел: • ISO Random: PRNG, предусмотренный в С11 • BSD Random: функции, заимствованные из BSD (Berkley Software Distribution). В настоящее время BSD называют семейство Unixподобных операционных систем. • SVID Random: функции, стандартизированные в системах SVID (System V Interface Definition) компании AT&T. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 12 BSD random В BSD системах предусмотрен более сложный и гибкий генератор случайных чисел, который можно настраивать. Помимо линейного конгруэнтного генератора, аналогичного предложенному в стандарте языка СИ доступно еще 4 алгоритма генерации псевдослучайных чисел. Кроме стандартных функций, аналогичных функциям стандарта: BSD функция Аналог С11 long int random(void); int rand(void); void srandom(unsigned int seed); void srand(unsigned int seed); Доступны две дополнительные выбирать тип используемого PRNG: функции, которые позволяют // Выбор и инициализация начального состояния генератора char *initstate(unsigned int seed, char *state, size_t n); // Установка начального состояния генератора char *setstate(char *state); © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 13 GNU C Library (2) По умолчанию glibc-2.18/stdlib/random_r.c /* Linear congruential. #define>TYPE_0 #define>BREAK_0 #define>DEG_0 #define>SEP_0 0 8 0 0 /* x[7] + x[3] + 1. #define>TYPE_1 #define>BREAK_1 #define>DEG_1 #define>SEP_1 /* x[15] + x + 1. #define>TYPE_2 #define>BREAK_2 #define>DEG_2 #define>SEP_2 */ /* x[31] + x[3] + 1. #define>TYPE_3 #define>BREAK_3 #define>DEG_3 #define>SEP_3 */ 1 32 7 3 /* x[63] + x + 1. #define>TYPE_4 #define>BREAK_4 #define>DEG_4 #define>SEP_4 */ 3 128 31 3 */ 4 256 63 1 */ 2 64 15 1 © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 14 Выбор ГСПЧ(PRNG) в GLibC glibc-2.18/stdlib/random_r.c int __initstate_r (unsigned int seed, char *arg_state, size_t n, struct random_data *buf) { /* Linear congruential. */ if (buf == NULL) #define>TYPE_0 0 goto fail; #define>BREAK_0 8 … int type; if (n >= BREAK_3) type = n < BREAK_4 ? TYPE_3 : TYPE_4; else if (n < BREAK_1) { if (n < BREAK_0) goto fail; type = TYPE_0; } else type = n < BREAK_2 ? TYPE_1 : TYPE_2; #define>DEG_0 #define>SEP_0 0 0 /* x7 + x3 + 1. */ #define>TYPE_1 #define>BREAK_1 #define>DEG_1 #define>SEP_1 1 32 7 3 /* x15 + x + 1. */ #define>TYPE_2 #define>BREAK_2 #define>DEG_2 #define>SEP_2 2 64 15 1 © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 15 GNU C Library (glibc) glibc-2.18/stdlib/random_r.c int __random_r (struct random_data *buf, { ... int32_t *result) if (buf->rand_type == TYPE_0) { int32_t val = state[0]; val = ((state[0] * 1103515245) + 12345) & 0x7fffffff; state[0] = val; *result = val; } ... Source m glibc (GCC) 231 a c 1103515245 12345 Биты xn, формирующие случайноге число 30..0 (m = 0x7FFFFFFF) © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 16 Алгоритм GLibC PRNG, TYPE0 #include <stdio.h> #include <stdlib.h> Вывод программы: 1103527590, next = 377401575 377401575, next = 662824084 int main() 662824084, next = 1147902781 { 1147902781, next = 2035015474 int rval, i; 2035015474, next = 368800899 int tmp[8]; 368800899, next = 1508029952 // BREAK0 = 8 ≤ 8 < BREAK1 = 32 1508029952, next = 486256185 initstate(1,(char*)tmp, 8); 486256185, next = 1062517886 1062517886, next = 267834847 for(i=0;i<10;i++){ 267834847, next = 180171308 rval = random(); int next = (((unsigned int)rval * 1103515245) + 12345) & 0x7fffffff; printf("%d, next = %d\n", rval, next); } return 0; } © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 17 Алгоритм вычисления периода PRNG input n for i ← 1 to n do rnd[i] = rand() // запомнить первые n псевдослучайных чисел found ← false // Флаг – признак обнаружения rnd i←1 // Проверяемый элемент rnd count ← n // Количество сгенерированных чисел while ( не found ) do if rnd[i] = rand() then // rnd[i] совпал, далее проверяем rnd[i+1] i←i+1 if i = n then // если совпали все n элементов - выход found ← true; else // если очередной элемент не совпал – проверяем сначала i ← 1; count ← count + 1 output count © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 18 Период GLibC ГСПЧ(PRNG), TYPE0 Source m glibc (GCC) 231 a c 1103515245 12345 Биты xn, формирующие случайноге число 30..0 Экспериментально полученное значение периода линейного конгруэнтного ГСПЧ, вычисленное согласно приведенному алгоритму, составляет 2147483774. Это означает, что последовательности чисел с номерами 1 – 2147483774, 2147483775 – 4294967550, 4294967551 – 6442451325 будут совпадать. Период такого генератора составляет примерно 231. Такой период слишком мал, поэтому применение ГСПЧ типа 0 на практике не желательно. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 19 Алгоритм GLibC PRNG, TYPE1 http://www.mathstat.dal.ca/~selinger/random/ #include <stdio.h> #define MAX 1000 #define seed 1 main() { int r[MAX], i, start = 0, tmp[32]; initstate(1,(char*)tmp,32); r[0] = seed; for (i=1; i<DEG; i++) { r[i] = (16807LL * r[i-1]) % 2147483647; if (r[i] < 0) { r[i] += 2147483647; } } start = i; for (i=start; i < start + SEP; i++) { r[i] = r[i-DEG]; } start = i; for (i=start; i < start + DEG*10; i++) { r[i] = r[i-DEG] + r[i-SEP]; } start = i; for (i=start; i<MAX; i++) { r[i] = r[i-DEG] + r[i-SEP]; int r1 = rand(); printf("%d <-> %d\n", ((unsigned int)r[i]) >> 1, r1); } } #define #define #define #define TYPE BREAK DEG SEP © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 1 32 7 3 20 Период GLibC ГСПЧ(PRNG), TYPE1 Количество совпавших элементов Расстояние между совпадениями 1 3∙107 – 9∙109 2 16∙109 3 69∙109 4 138∙109 n = 128 272∙109 Таким образом, период ГСПЧ типа 1 в (272∙109 / 2147483774) = 126 раз превышает период ГСПЧ типа 0. ГСПЧ типа 1 – 4 относятся к Аддитивным ГСПЧ и характеризуются существенно большим периодом по сравнению с линейным. © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 21 Принцип работы аддитивного ГСПЧ #define #define #define #define TYPE BREAK DEG SEP 1 32 7 3 + + 1 2 3 4 DEG 5 6 7 1 2 3 SEP 1. Начальное заполнение массива производится при помощи линейного конгруэнтного генератора 2 4 6 6 9 12 13 10 14 19 r[0] = seed; for (i=1; i<DEG; i++) { r[i] = (16807LL * r[i-1]) % 2147483647; if (r[i] < 0) { r[i] += 2147483647; } } © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 22 Принцип работы аддитивного ГСПЧ #define #define #define #define TYPE BREAK DEG SEP 1 32 7 3 + + 1 2 3 4 5 6 7 DEG 2. Часть (SEP) элементов дублируется 1 2 3 2 4 6 6 9 12 13 10 14 19 SEP start = i; for (i=start; i < start + SEP; i++) { r[i] = r[i-DEG]; } © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 23 Принцип работы аддитивного ГСПЧ (2) #define #define #define #define TYPE BREAK DEG SEP 1 32 7 3 + + 1 2 3 4 5 6 7 1 DEG 3. Первые k чисел обычно отбрасываются. В GLibC отбрасываются 10*DEG чисел. 2 3 2 4 6 6 9 12 13 10 14 19 SEP start = i; for (i=start; i < start + DEG*10; i++) { r[i] = r[i-DEG] + r[i-SEP]; } © Кафедра вычислительных систем ФГОБУ ВПО «СибГУТИ» 24