Программный генератор статистически безупречных случайных чисел

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Программный генератор статистически безупречных случайных чисел


Проблема создания таких программ широко обсуждалась в математической литературе в 1965-1975 годах,
тогда же отмечалась сложность этой задачи.
Современная математика имеет значительные достижения в этом вопросе.
Они доступны узким специалистам, но сложны для понимания, и удалились из широкого обсуждения.
Я предлагаю здесь простое решение этой задачи, оно, возможно, недостойно внимания больших математиков, но вполне доступно начинающим.


Мне удалось создать программу, генерирующую последовательность символов '0' и '1', имеющую безупречно хорошие статистические свойства.
Конструкция программы проста и легко доступна для понимания.
Случайная последовательность нулей и единиц должна обладать следующими свойствами:


  • количество нулей и единиц во всех фрагментах этой последовательности примерно одинаково;
  • количество вхождений фрагментов 00, 01, 10 и 11 всегда примерно одинаково;
  • то же касается всевозможных фрагментов длины 3, 4, 5 и так далее.
    Ясно, что программа может подсчитывать количество вхождений таких фрагментов в уже сформированную последовательность и генерировать очередной символ так, чтобы поддерживать перечисленные равенства.
    Если анализировать эти равенства в порядке возрастания длины фрагмента, то программа быстро зацикливается.
    Однако, если их анализировать в обратном порядке, то всё получается замечательно хорошо.

Что же в итоге получилось?
Задаётся некоторая начальная последовательность символов '0' и '1' (начальный отрезок), которую программа будет достраивать до заранее заданной предельной длины, после чего остановится.
Такая организация программы целесообразна в связи с тем, что по мере роста длины сформированной последовательности скорость формирования очередного символа уменьшается.
Для формирования очередного символа программа организует цикл, по различным длинам маски (идентификатор lenmask).
Маска в этом цикле задаётся как фрагмент сформированной последовательности, состоящий из lenmask её последних символов.
Такая маска циклически передвигается вдоль сформированной последовательности.
В ходе этого вложенного цикла передвижений подсчитываются суммы s0 и s1, это количество появлений нуля или соответственно единицы в сформированной последовательности непосредственно после обнаруженного совпадения маски с ней.
Если по окончании подсчёта при очередной заданной длине маски значения s0 и s1 оказываются различными, то при s0>s1 очередной символ формируется как 1, иначе как 0 и цикл по длинам масок прерывается.
Если же при всех длинах маски значения сумм s0 и s1 окажутся одинаковыми, то очередной символ сформируется по значению резервной переменной, принимающей одно из двух значений 0 или 1 и меняющей значение после каждого использования.
Эта ситуация, конечно, маловероятна.
Математически строгое доказательство того, что сформированная описанной программой последовательность нулей и единиц обладает необходимыми статистическими свойствами, остаётся нерешённой трудной задачей, однако непосредственный контроль пробных запусков этой программы принёс очень хорошие результаты.


Детально комментированный текст этой программы на языке JAVA представлен в приложении.
Программа состоит из двух частей.
Это собственно формирование псевдослучайной последовательности и статистический контроль результата.
Для контроля формируется некоторое количество масок различной длины.
Эти маски наполнены нулями и единицами.
Подсчитывается количество вхождений этих масок в сформированную последовательность.
Как и следовало ожидать, результаты контроля демонстрируют, что количество таких вхождений зависит от длины маски и почти не зависит от её наполнения.
Удивительно, что такая вполне элементарная программа позволяет получить результат, казавшийся недостижимым простыми средствами.


Приложение


Текст программы на языке JAVA


//gerase = generator random sequence
//генератор статистически безупречной 
//случайной последовательности нулей и единиц
package ikojar;
public class gerase {
public static void main(String[] args) {
//управляющие параметры программы
//число, кодирующее начальный отрезок формируемой последовательности
//двоичное представление числа beg станет 
//началом формируемой последовательности
int beg=5,
//длина формируемой последовательности
lenrez=2048,
//maxpower это максимальная длина контрольной маски 
//в процессе статистического контроля сформированной последовательности
//контрольная маска скользящим образом 
//накладывается на сформированную последовательность
//подсчитывается количество вхождений 
//различных вариантов наполнения маски
//для разных вариантов наполнения маски 
//количество её вхождений в сформированную последовательность 
//должно быть приблизительно одинаковым
maxpower=10;

//декларация рабочих переменных программы
//rs это random symbol, может быть использован, 
//если очередной символ не был определён алгоритмом формирования
//алгоритм не сможет сформировать очередной символ, если 
//в результате расчёта окажется, что и нуль и единица одинаково допустимы
byte rs=0;
//массив result будет содержать сформированную последовательность 
//нулей и единиц
byte[] result=new byte[lenrez];
//массив mask будет вместилищем контрольной маски, используемой 
//как при формировании последовательности, 
//так и при её статистическом контроле
//длина маски будет изменяться внутри программы, 
//здесь просто резервируется максимально необходимое место для неё
byte[] mask=new byte[lenrez];

//собственно формирование псевдослучайной последовательности
//распаковка управляющего числа beg
//двоичное представление управляющего числа beg будет помещено 
//в начало формируемой псевдослучайной последовательности result
int p=beg,bp=0;
for(;;)
{//cir raspak
result[bp]=(byte)(p%2);
bp++;
if(p<2)break;
p/=2;
}//cir raspak
//печать начального отрезка
String so="начальный фрагмент ";
for(int i=0;i<bp;i++)so+=String.valueOf(result[i]);
System.out.println(so);
//подготовка печати результата
System.out.println("сформирована последовательность");

//цикл формирования выходной последовательности
for(int pos=bp;pos<lenrez;pos++)
{//circlepos
//признак hs(hard symbol) это 
//признак того, что очередной символ формируемой выходной 
//последовательности определился в результате расчёта
//если расчёт не сможет определить очередной символ, 
//то он будет задан из переменной rs
int hs=0;
//цикл по убывающему размеру маски
//lenmask это длина маски
for(int lenmask=pos-2;lenmask>=0;lenmask--)
{//lenmask
//заполнение маски последними символами 
//ранее сформированной последовательности
for(int i=0;i<lenmask;i++)mask[i]=result[pos+i-lenmask];
//s0 и s1 станут результатами подсчёта 
//количества вхождений нулей и единиц соответственно
//непосредственно после очередного вхождения образа маски 
//в уже сформированную последовательность
//при скользящем наложении маски на эту последовательность
int s0=0,s1=0;
//цикл скользящего продвижения маски вдоль 
//сформированной последовательности псевдослучайных символов
for(int i=lenmask;i<pos;i++)
{//calcS01
//eqm это признак совпадения фрагмента 
//сформированной последовательности с наложенной маской
int eqm=1;
//вычисление eqm
for(int i1=0;i1<lenmask;i1++)if(mask[i1]!=result[i+i1-lenmask]){eqm=0;break;}
//собственно акт подсчёта количества нулей или единиц, следующих 
//непосредственно после вхождения образа маски
if(eqm==1)if(result[i]==0)s0++;else s1++;
}//calcS01

//формирование очередного символа выходной последовательности 
//в зависимости от соотношения между s0 и s1
//признак hs станет равным 1, если 
//таким образом будет сформирован очередной символ
if(s0<s1){result[pos]=0;hs=1;break;}
if(s1<s0){result[pos]=1;hs=1;break;}
}//lenmask
if(hs==1)continue;
//если расчёт не определил очередной символ, 
//то он формируется из переменной rs
result[pos]=rs;rs=(byte)(1-rs);
}//circlepos

//выходная последовательность готова
//осталось выдать её на печать
so="";
for(int i=0,c=0;i<lenrez;i++)
{//out rez
so+=String.valueOf(result[i]);
c++;
if(c==64){c=0;System.out.println(so);so="";}
}//out rez
System.out.println(so);

//а теперь статистический анализ сформированной последовательности
System.out.println
("статистический анализ сформированной последовательности");

//цикл по различным размерам контрольных масок
//pw это длина контрольной маски, 
//накладываемой на сформированную последовательность
for(int pw=1;pw<maxpower;pw++)
{//pw
//контрольные маски будут наполняться 
//всевозможными комбинациями нулей и единиц
//эти комбинации можно интерпретировать как двоичные представления 
//того или иного неотрицательного целого числа
//будут перебираться все 
//такие представляющие числа в пределах от 0 до pm-1
//верхняя граница pm для такого перебора сейчас будет определена
int pm=1;for(int i=0;i<pw;i++)pm*=2;
//печатаем заголовок серии чисел, равных количеству вхождений 
//очередной маски в сформированную последовательность
System.out.println("для маски длиной "+String.valueOf(pw));
int mincount=lenrez,maxcount=0;
//цикл последовательного перебора целых чисел от 0 до pm-1, 
//двоичное представление которых станет содержимым очередной маски
for(int im=0;im<pm;im++)
{//im
//заполнение маски двоичным представлением числа im
p=im;
for(int i=pw-1;i>=0;i--)
{mask[i]=(byte)(p%2);p/=2;}

//цикл скользящего наложения контрольной маски 
//на сформированную последовательность 
//переменная s станет равной количеству совпадений 
//контрольной маски с сформированной последовательностью
int s=0;
for(int i0=pw;i0<=lenrez;i0++)
{//i0
//проверка совпадения маски с содержимым под маской
int eqm=1;
for(int i1=0;i1<pw;i1++)if(result[i0+i1-pw]!=mask[i1]){eqm=0;break;}
if(eqm==1)s++;
}//i0
//печатаем очередное число вхождений 
//маски в сформированную последовательность
//этот громоздкий вариант печати здесь дезактивирован
//System.out.println(String.valueOf(s));

//подготовка укороченных оценок качества результата
if(s<mincount)mincount=s;
if(s>maxcount)maxcount=s;
}//im
System.out.println("минимум вхождений="+String.valueOf(mincount));
System.out.println("максимум вхождений="+String.valueOf(maxcount));
}//pw

return;
}//main

}//class
Источник: https://habr.com/ru/post/541956/


Интересные статьи

Интересные статьи

В прошлой статье я рассказал об итераторах и генераторах в javascript, но не привел никаких практичных примеров. Этой статьей я хочу заполнить этот пробел и, на примере п...
Полгода назад мне захотелось создать лучший генератор псевдослучайных чисел (ГПСЧ) с какой-нибудь необычной архитектурой. Я думал, что начало будет лёгким, а по мере работы задача станет медлен...
Несколько месяцев назад меня вдруг поразила мысль, что я понятия не имею о принципах работы компьютерного железа. Я до сих пор не знаю, как работают современные компьютеры. Я прочитал книгу ...
Если Вы используете в своих проектах инфоблоки 2.0 и таблицы InnoDB, то есть шанс в один прекрасный момент столкнуться с ошибкой MySQL «SQL Error (1118): Row size too large. The maximum row si...
В «1С-Битрикс» считают: современный интернет-магазин должен быть визуально привлекательным, адаптированным для просмотра с мобильных устройств и максимально персонализированным с помощью технологии Бо...