Генератор псевдослучайных чисел с равномерным, нормальным и логнормальным распределением своими руками

17.03.19

Разработка - Математика и алгоритмы

Рассматриваем распределение случайных величин для генератора

Генератор псевдослучайных чисел

 

Введение в проблему

 

Проблема случайных чисел начинается с того, что пока не существует ни одного генератора случайных чисел. Быть может, некоторые читатели возразят, и будут настаивать на том, что непосредственно человек является генератором случайных чисел. Но, к сожалению, это не так. Если попросить респондента загадать случайное число, то с высокой долей вероятности это будет число из промежутка от 0 до 20. Из этого интервала, скорее всего, будет загадано число «3», «7», «10», «12». Если же «случайное» число не будет принадлежать данному отрезку, то оно будет либо из повторяющихся цифр, например «111», «666» и так далее, либо будет кратно 10 («100», «1000»). Еще респонденты называют какие-то культовые числа, как то «42», «23», «13».

Таким образом, даже человек является своего рода генератором псевдослучайных чисел, и «программа», заложенная в нём – еще проще и более предсказуемо, чем машинная.

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

Кроме всего вышеперечисленного (данные вещи имеют явный развлекательный характер), псевдослучайные числа также применяются в криптографии, современном шифровании, создании «одноразовых» паролей, при передаче информации между пользователями.

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

 

Способы решения

 

Генератор псевдослучайных чисел с равномерным распределением

 

Встроенный в Pascal генератор случайных чисел является эталоном для наших последующих экспериментов. Данное изображение было создано при использовании графических средств. Были созданы 5000 случайных чисел для x, 5000 – для y, а после по полученным значениям были построены точки с координатами (x, y).

 

 

Это похоже на равномерное распределение.

Возникла идея проанализировать распределение у цифр десятичной записи деления единицы на большое простое число.

Была создана простая программа в Pascal:

 

program pseudorandomnumbersgeneratorthree;

begin

writeln(1/9941);

end.

 

В результате: 0.000100593501659793

Очевидно, что для нашего эксперимента такой точности недостаточно. Самый большой тип данных в Pascalreal (действительные числа) – ограничен 1.7*10^38, поэтому возникла идея обратиться к Java.

  

«Я же просил 400 капель, а тут 402!»

 

Будем запрашивать текущее время в миллисекундах, проверять, является ли оно простым числом (если нет, то будем увеличивать его на 1 до тех пор, пока оно не станет простым), а дальше – находить величину, обратную ему и брать значения с 1 цифры, не равной 0, по 1001 цифру после запятой.

 

import java.math.BigDecimal;

import java.math.BigInteger;

 

public class testing {

 

public static void main(String[] args) {

BigDecimal input = BigDecimal.valueOf(1.0);

BigInteger helper = BigInteger.valueOf(1);

BigInteger input1 = BigInteger.valueOf((int)System.currentTimeMillis());

 

 

boolean primenumber = input1.isProbablePrime(100);

 

while (primenumber != true) {

       input1 = input1.add(helper);

       primenumber = input1.isProbablePrime(100);

}

 

if (primenumber) {

       BigDecimal input3 = new BigDecimal(input1.toString());

       BigDecimal input4 = input.divide(input3, 1001, BigDecimal.ROUND_DOWN);

       System.out.println(input4);

}

}

}

Код реализации представлен выше.

 

 
 Для времени = 2103543487 мс.  получил последовательность:

4.7538831794067873244780700842245055046014363666920473795748060044741066957557031955004218080137080617

43531713352213668782593606537595673675749399898191883684123712152189036251666519504666651086923785569

44946106550351530716892661986597665237619116547410840382592955635910749298428932360835951664830022123

52170877653930812222851849223500269856793314774956207026111269550378446728066145275750127622628987275

12734325515752933896916389254566430553665088075262596175650159021409382443634739080628286530878836069

43467957883962681300156073264959318618545868930733448630624863330909592932984132787743028009955279807

34348374324813756512560274918623538777356400809221777690778968906574356929374952351531775107057722548

52366643751729578576570687269086156049599178074895677210214004955344191560324039072266633867788443776

53695732699630183590304829291128412977753665997778347807458472571144900699645008099611491416721065391

50431169574389692662436505169336677468936015393153600156591390687042164296363795591E-10

Подсчитаем, сколько раз встречается та или иная цифра («отбросим» нули после запятой до первой цифры, не равной «0», отбросим последнюю, 1001 цифру последовательности (округление)):

 

0

1

2

3

4

5

6

7

8

9

97

84

88

105

83

115

125

106

86

101

 

Получилось распределение — последовательность цифр [0-9], близкое к равномерному. Величина выборки 1000, повторение цифр из-за периодичности дроби внутри выборки отсутствуют. Поставленная задача достигнута, генератор создан.

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

 

package education1;

import java.math.BigDecimal;

import java.math.BigInteger;

import java.util.Scanner;

 

public class testing {

 

public static void main(String[] args) {

BigDecimal input = BigDecimal.valueOf(1.0);

BigInteger helper = BigInteger.valueOf(1);

 

Scanner in = new Scanner(System.in);

 

System.out.println("Введите число от 0 до 9");

BigInteger input1 = BigInteger.valueOf((int)System.currentTimeMillis());

int onlyforlools = in.nextInt();

 

System.out.println("Введите число от 0 до 9");

int onlyforlools1 = in.nextInt();

 

System.out.println("Введите число от 0 до 9");

int onlyforlools2 = in.nextInt();

BigInteger input9 = BigInteger.valueOf((int)System.currentTimeMillis());

 

BigInteger firstcontrolnumber = input9.subtract(input1);

 

boolean primenumber = input9.isProbablePrime(100);

 

while (primenumber != true) {

       input9 = input9.add(helper);

       primenumber = input9.isProbablePrime(200);

      

}

 

if (primenumber) {

       BigDecimal input11 = new BigDecimal(input9.toString());

       BigDecimal input12 = input.divide(input11, 1001, BigDecimal.ROUND_DOWN);

       System.out.println(input12);

}

}

}

 

Каждое рациональное число представимо в виде бесконечной периодической дроби. Ниже вспомогательная программа, которая ищет длину возможного периода для бесконечной дроби и показывает распределение цифр 0-9.

 

import java.math.BigInteger;

 

public class testing {

       public static void main (String [] args) {

       BigInteger ten = new BigInteger("10");

       BigInteger helper = BigInteger.valueOf(9947); /*здесь вводите число, далее      Java преобразует его в бесконечную десятичную периодическую дробь вида     1/число, а  потомнаходит количество чисел в периоде*/

       BigInteger lools = new BigInteger("1");

      

       boolean primenumber = helper.isProbablePrime(100);

      

       while (primenumber != true) {

       helper = helper.add(lools);

       primenumber = helper.isProbablePrime(100);

       }

      

       BigInteger three = helper;

      

       BigInteger end123 = new BigInteger ("1");

       String period = "";

      

       do {

       end123 = end123.multiply(ten);

       period = period + end123.divide(three).toString();

       end123 = end123.mod(three);

       }

       while (end123.compareTo(lools) != 0);

      

       int resultat = period.length();

 

      int[][]amountofnumbers = {{0, 0}, {1, 0}, {2, 0}, {3,0}, {4, 0}, {5,0}, {6, 0}, {7,0}, {8,0}, {9,0}};

      

       for (char element: period.toCharArray()) {

       if (element == '0') amountofnumbers[0][1] = amountofnumbers[0][1] + 1;

       if (element == '1') amountofnumbers[1][1] = amountofnumbers[1][1] + 1;

       if (element == '2') amountofnumbers[2][1] = amountofnumbers[2][1] + 1;

       if (element == '3') amountofnumbers[3][1] = amountofnumbers[3][1] + 1;

       if (element == '4') amountofnumbers[4][1] = amountofnumbers[4][1] + 1;

       if (element == '5') amountofnumbers[5][1] = amountofnumbers[5][1] + 1;

       if (element == '6') amountofnumbers[6][1] = amountofnumbers[6][1] + 1;

       if (element == '7') amountofnumbers[7][1] = amountofnumbers[7][1] + 1;

       if (element == '8') amountofnumbers[8][1] = amountofnumbers[8][1] + 1;

       if (element == '9') amountofnumbers[9][1] = amountofnumbers[9][1] + 1;

       }

      

       System.out.println(resultat);

       System.out.println(period);

      

       for (int i = 0; i < amountofnumbers.length; i++) {

             for (int j = 0; j < amountofnumbers[i].length; j++) {

                    System.out.print(amountofnumbers[i][j] + "\t");

             }

             System.out.println();

       }

}

}

Чем больше простое число, тем больше получившийся период. Например, для числа 3 – в периоде всего одно число, для числа 101 – 4 числа, для  7369 – 1842 числа.

 

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

 

Кроме генераторов с равномерным распределением также существуют генераторы с нормальным распределением (имеющим большое значение в физике, биологии, социологии). Воспользуемся центральной предельной теоремой (Сумма достаточно большого количества слабо зависимых случайных величин, имеющих примерно одинаковые масштабы (ни одно из слагаемых не доминирует, не вносит в сумму определяющего вклада), имеет распределение, близкое к нормальному.) ссылка: https://ru.wikipedia.org/wiki/Центральная_предельная_теорема для небольшого видоизменения лучшей программы по генерации псевдослучайной последовательности с равномерным распределением, чтобы получить последовательность случайных чисел с нормальным распределением.

Код представлен ниже:

 

import java.math.BigDecimal;

import java.math.BigInteger;

 

public class testing {

 

public static void main(String[] args) {

BigDecimal input = BigDecimal.valueOf(1.0);

BigInteger helper = BigInteger.valueOf(1);

BigDecimal divider = new BigDecimal("100.0");

BigInteger input1 = BigInteger.valueOf((int)System.currentTimeMillis());

BigDecimal summary = new BigDecimal("0");

int counter;

boolean primenumber = input1.isProbablePrime(100);

if (primenumber == true) {

       counter = 1;

}

else {

       counter = 0;

}

while (counter < 101) {

while (primenumber != true) {

       input1 = input1.add(helper);

       primenumber = input1.isProbablePrime(200);

}

if (primenumber) {

       BigDecimal input3 = new BigDecimal(input1.toString());

       BigDecimal input4 = input.divide(input3, 1001, BigDecimal.ROUND_DOWN);

       summary = summary.add(input4);

}

counter = counter + 1;

primenumber = false;

}

System.out.println(summary.divide(divider, 1001, BigDecimal.ROUND_DOWN));

}

}

 

 

 Нормальное распределение, изображенное с помощью графических ресурсов Pascal

 

Генератор псевдослучайных чисел с логнормальным распределением

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Логнормальное распределение, изображенное с помощью графических ресурсов Pascal

Белые полосы наблюдаются из-за округления значения функции e^(случайное число с нормальным распределением)

 

 

В жизни логнормальное распределение имеет размер заработной платы, сумма чеков и другие случайные величины. Код, позволяющий получить псевдослучайную величину с логнормальным распределением представлен:

 

import java.math.BigDecimal;

import java.math.BigInteger;

import org.nevec.rjm.*;

 

public class testing {

 

public static void main(String[] args) {

BigDecimal input = BigDecimal.valueOf(1.0);

BigInteger helper = BigInteger.valueOf(1);

BigInteger input1 = BigInteger.valueOf((int)System.currentTimeMillis());

BigDecimal summary = new BigDecimal("0");

BigDecimal divider = new BigDecimal("100.0");

BigDecimal eulerdigit = new BigDecimal("2.7182818284590455");

 

int counter;

boolean primenumber = input1.isProbablePrime(100);

if (primenumber == true) {

       counter = 1;

}

else {

       counter = 0;

}

while (counter < 101) {

while (primenumber != true) {

       input1 = input1.add(helper);

       primenumber = input1.isProbablePrime(200);

}

if (primenumber) {

       BigDecimal input3 = new BigDecimal(input1.toString());

       BigDecimal input4 = input.divide(input3, 1001, BigDecimal.ROUND_DOWN);

       summary = summary.add(input4);

}

counter = counter + 1;

primenumber = false;

}

System.out.println(eulerdigit.pow(summary.divide(divider, 1001, BigDecimal.ROUND_DOWN)).toString());

}

}

 

Итог

         В данной статье представлены рабочие алгоритмы разных распределений для создания последовательности псевдослучайных цифр. Надеюсь, эта статья была интересной для вас!

 

Автор: Васильев Алексей. Консультант-математик: Васильев Николай.

См. также

Метод Дугласа-Пойкера для эффективного хранения метрик

Математика и алгоритмы Платформа 1C v8.2 Конфигурации 1cv8 Россия Абонемент ($m)

На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL. Я же покажу как можно сократить объем данных в 49.9 раз при этом: 1. Сохранить значения локальных экстремумов 2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.

1 стартмани

30.01.2024    1925    stopa85    12    

34

Алгоритм симплекс-метода для решения задачи раскроя

Математика и алгоритмы Бесплатно (free)

Разработка алгоритма, построенного на модели симплекс-метода, для нахождения оптимального раскроя.

19.10.2023    4784    user1959478    50    

34

Регулярные выражения на 1С

Математика и алгоритмы Инструментарий разработчика Платформа 1С v8.3 Мобильная платформа Россия Абонемент ($m)

Что ж... лучше поздно, чем никогда. Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.

1 стартмани

09.06.2023    7815    5    SpaceOfMyHead    17    

56

Мини-обзор разных решений задач

Математика и алгоритмы Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Три задачи - три идеи - три решения. Мало кода, много смысла. Мини-статья.

03.04.2023    3152    RustIG    6    

25

Модель распределения суммы по базе

Математика и алгоритмы Платформа 1С v8.3 Россия Абонемент ($m)

Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса

1 стартмани

21.03.2022    7991    7    kalyaka    11    

44

Изменения формата файлов конфигурации (CF) в 8.3.16

Математика и алгоритмы Платформа 1С v8.3 Бесплатно (free)

Дополнение по формату файлов конфигурации (*.cf) в версии 8.3.16.

16.12.2021    4601    fishca    13    

37

Интересная задача на Yandex cup 2021

Математика и алгоритмы Бесплатно (free)

Мое решение задачи на Yandex cup 2021 (frontend). Лабиринт. JavaScript.

12.10.2021    9012    John_d    73    

46
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. vasilev2015 2700 18.03.19 08:27 Сейчас в теме
Все программные модули статьи можно перевести на язык 1С 8.3
Пример деления с неограниченной точностью:


        //Вводите переменные из диалога
	Делимое = 1;
	Делитель = 9971;
	НужнаяРазрядность = 1000;
        //Вводите переменные из диалога
	
	РезультатДеления = ""+Цел(Делимое / Делитель)+",";
	ОстатокДеления = Делимое % Делитель;
	
	Для Разрядность = 1 ПО НужнаяРазрядность Цикл
		ОстатокДеления = ОстатокДеления * 10;
		РезультатДеления = РезультатДеления+Цел(ОстатокДеления / Делитель);
		ОстатокДеления = ОстатокДеления % Делитель;			
	КонецЦикла; 
	
	Сообщить(РезультатДеления);

Показать
Оставьте свое сообщение