Как стать автором
Поиск
Написать публикацию
Обновить

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

Время на прочтение7 мин
Количество просмотров4.4K

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


Проблема создания таких программ широко обсуждалась в математической литературе в 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
Теги:
Хабы:
Всего голосов 18: ↑3 и ↓15-12
Комментарии8

Публикации

Ближайшие события