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

19.10.2019

21
//Функция формирует удобное для чтения представление значений. // Примеры форматирования чисел ЗначФормат = Формат(123456.789, " ЧЦ=10; ЧДЦ=2"); // ЗначФормат = " 123 456,79" ЗначФормат = Формат(123456.789, " ЧГ=0; ЧДЦ=2"); // Знач 16
Полнотекстовый поиск - позволит найти текстовую информацию, размещенную практически в любом месте используемой конфигурации. При этом искать нужные данные можно либо по всей конфигурации в целом, либо сузив... 8
" Момент времени" - виртуальное поле, не хранится в базе данных. Содержит объект МоментВремени (который включает в себя дату и ССЫЛКУ НА ДОКУМЕНТ) В 7.7 было понятие ПозицияДокумента, а в 8.x Момент времени Для получения... 6
Для 8.х НайтиПоСсылкам (FindDataByRef) Синтаксис: НайтиПоСсылкам (Список ссылок) Параметры: Список ссылок Обязательный Тип: Массив. Массив со списком ссылок на объекты, ссылки на которые нужно найти. ...

Ключевые слова: генератор, случайный, чисел, число, алгоритм, random, randomize, распределение, равномерное, лотерея

Вот не думал, что в 1С пригодится, а вот на Вам.. клиенты решили акцию провести, типа "собери крышечки", только собирать нужно слова, кто правильное слово из своего набора букв соберет - тот и выиграл. Задача вообщем казалось бы простая: имеется алфавит, имеется некое слово, которое нужно собрать, например "коньяк", в нем 6 букв, как видите.

Нужно: сгенерить некоторое кол-во случайных шестибуквенных сочетаний из любых букв алфвита, к этому примешать некоторые кол-во вариантов, при которых слово все-таки можно сложить, например "ньккоя" - слово складывается, а "кавпры" - явно не подходит.

Дополнительное условие: все эти варианты (правильные и нет) нужно пронумеровать, чтобы при получении "призовой" карточки можно было бы сверить номер (а был ли такой).

Казалось бы, причем здесь 1С? Так вот учет этих карточек и призов желают добавить в учетную прогу, ну и заодно попросили нагенерить случайных комбинаций (ну не вручную же их сочинять).
Для каждой акции комбинации генерятся один раз, потом по ним изготавливают карточки, т.е. в следующий раз слово будет другое и т.д.

В общем задача сводится к следующему:
1. Генерить случайные числа, желательно с большим разбросом.
2. По числу вычислять комбинацию букв (т.е. найти какое-то соответствие между возможныи комбинациями и их номерами).
3. Пункт обратный предыдущему - по слову проверять номер комбинации.

Решение:
1. т.к. генератор от avb и NS давал маленький разброс случайных чисел, пришлось поюзать немного другой алгоритм:

Function Random()
if emptyvalue(randSeed) = 1 then
randSeed = _getperformancecounter();
endif;

RandSeed=(a*randSeed+c)%m;
return randSeed;
endfunction

Здесь:
a=1664525; c=1013904223; m=4294967296;
последняя переменная - 2 в 32-й степени, две другие - рекомендуемые для таких целей коэффициенты

Ограничение по максимальному значению 2^32 выбрано исходя из максимального кол-ва комбинаций (для обрезанного алфавита в 28 букв и слов по 7, т.к. в реальной задаче их именно 7, общее число комбинаций составит 28^7, таким образом выбранное ограничение лежит примерно посередине интервала, что вполне достаточно для выборки 20-30 тыс. вариантов)

Нам также понадобится еще одна вспомогательная функция - возведение в целочисленную положительную степень:

Функция Степень(Знач а,Знач б, Рез=1)
Если б>0 Тогда
Рез=Рез*а;
б=б-1;
Степень(а,б,Рез);
Возврат Рез;
Иначе
Возврат Рез;
КонецЕсли;
КонецФункции

Здесь: а - основание степени, б - показатель степени, Рез - результат

2. Выявить зависимость между последовательно расположенными комбинациями, оказалось на удивление простым:

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

Для нашей системы счисления, основанием будут являться степени шестерки, т.е. для получения первого слева разряда, необходимо разделить номер нашей комбинации на 6 в 5-й степени, затем остаток от деления - на 6 в 4-й и т.д.

Таким образом, получаем набор из шести чисел, которые по сути являются порядковыми номерами букв в нашем алфавите.

Получившийся код:

Функция ПолучитьСимволы(Поз,ТекСимв=1,СимСтр="")
Если ТекСимв Делитель=Степень(СтрДлина(Буквы),к-ТекСимв);
ТекОст=Поз%Делитель;
СимСтр=Строка(СимСтр)+Сред(Буквы,Цел(Поз/Делитель+?(ТекОст>0,1,0)),1);
ПолучитьСимволы(ТекОст,ТекСимв+1,СимСтр);
Возврат СимСтр;
Иначе
СимСтр=СимСтр+Сред(Буквы,(?(Поз=0,СтрДлина(Буквы),Поз)),1);
Возврат СимСтр;
КонецЕсли;
КонецФункции

Здесь:
Поз - номер комбинации (псевдослучайное число)
ТекСимв - текущий обрабатываемый символ
СимСтр - резльтирующая строка символов
Буквы = строка, содержащая буквы алфавита в стандартном порядке ("абв...юя")
к - число символов в искомом слове (в данном случае = 6)

3. Обратное преобразование также тривиально:

Функция ПолучитьКомбинацию(Слово,ТекСимв=0,Поз=0)
НомСимв=Найти(Буквы,Сред(Слово,к-ТекСимв,1));
Если ТекСимв>0 Тогда
Если ТекСимв Поз=Поз+(НомСимв-1)*Степень(СтрДлина(Буквы),ТекСимв);
Иначе
Возврат Поз;
КонецЕсли;
Иначе
Поз=?(НомСимв=СтрДлина(Буквы),0,НомСимв);
ПолучитьКомбинацию(Слово,ТекСимв+1,Поз);
Возврат Поз;
КонецЕсли;
КонецФункции

Здесь:
Слово - комбинация символов, номер которой ищем
ТекСимв - текущий обрабатываемый символ (по сути разряд шестиричного "числа")
Поз - искомый номер комбинации


************************

Премешать N чисел:

Для а=1 по N цикл
массив[а]=а;
Конеццикла;
Для а=1 по N-1 цикл
Сл=Случ(а,N);// Целое случайное число в интервале [а..N]
К=массив[а];
массив[а]=массив[Сл];
массив[Сл]=К;
КонецЦикла;

//********************************************************************************
************************

Sc = CreateObject("MSScriptControl.ScriptControl");
Sc.language = "VBscript";
sc.executeStatement("randomize");
оноВотТутаБудет=Sc.eval("rnd");

Как сделать чтобы числа выбирались произвольно от 1 до 100?

Ранд=_GetPerformanceCounter()%(100+1);

похоже это лучшеее

//********************************************************************************
************************

В 8.0 для получения случайных чисел можно использовать встроенный генератор GUID.
Вот пример простенькой функции:

//только для целых чисел
Функция ПолучитьСлучайноеЧисло(Мин,Макс)

//вместо Randomize
Для н = 1 По 100 Цикл
Уник = Новый УникальныйИдентификатор;
КонецЦикла;

//генерируем GUID
Уник = СокрЛП(Новый УникальныйИдентификатор);

//оставляем только цифры
Уник = СтрЗаменить(Уник,"-","");
Уник = СтрЗаменить(Уник,"a","");
Уник = СтрЗаменить(Уник,"b","");
Уник = СтрЗаменить(Уник,"c","");
Уник = СтрЗаменить(Уник,"d","");
Уник = СтрЗаменить(Уник,"e","");
Уник = СтрЗаменить(Уник,"f","");

//знаменатель должен иметь такое же количество нулей + 1
Знаменатель = 10;
Для н = 2 По (СтрДлина(СтрЗаменить(Уник,Символы.НПП,""))) Цикл
Знаменатель = Знаменатель * 10;
КонецЦикла;

Случ = Число(Уник) / Знаменатель; //здесь получается дробное случайное число от 0 до 1

//преобразуем его в случайное число из заданного интервала, округляем до целого
ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс);

Возврат ЧислоИзИнтервала;

КонецФункции

Взято [необходимо зарегистрироваться для просмотра ссылки]

//********************************************************************************
************************

ЗЫ. я набрёл на эту статью в поисках генератора случайных чисел. Так вот для себя я выбрал вариант
Ранд=_GetPerformanceCounter()%(100+1);


Ключевые слова: генератор, случайный, чисел, число, алгоритм, random, randomize, распределение, равномерное, лотерея

Вот не думал, что в 1С пригодится, а вот на тебе... клиенты решили акцию провести, типа "собери крышечки", только собирать нужно слова, кто правильное слово из своего набора букв соберет - тот и выиграл. Задача вообщем казалось бы простая: имеется алфавит, имеется некое слово, которое нужно собрать, например "коньяк", в нем 6 букв, как видите.

Нужно: сгенерить некоторое кол-во случайных шестибуквенных сочетаний из любых букв алфвита, к этому примешать некоторые кол-во вариантов, при которых слово все-таки можно сложить, например "ньккоя" - слово складывается, а "кавпры" - явно не подходит.

Дополнительное условие: все эти варианты (правильные и нет) нужно пронумеровать, чтобы при получении "призовой" карточки можно было бы сверить номер (а был ли такой).

Казалось бы, причем здесь 1С? Так вот учет этих карточек и призов желают добавить в учетную прогу, ну и заодно попросили нагенерить случайных комбинаций (ну не вручную же их сочинять).
Для каждой акции комбинации генерятся один раз, потом по ним изготавливают карточки, т.е. в следующий раз слово будет другое и т.д.

В общем задача сводится к следующему:
1. Генерить случайные числа, желательно с большим разбросом.
2. По числу вычислять комбинацию букв (т.е. найти какое-то соответствие между возможныи комбинациями и их номерами).
3. Пункт обратный предыдущему - по слову проверять номер комбинации.

Решение:
1. т.к. генератор от avb и NS давал маленький разброс случайных чисел, пришлось поюзать немного другой алгоритм:

Function Random() if emptyvalue(randSeed) = 1 then randSeed = _getperformancecounter(); endif; randSeed=(a*randSeed+c)%m; return randSeed; endfunction

Здесь:
a=1664525; c=1013904223; m=4294967296;
последняя переменная - 2 в 32-й степени, две другие - рекомендуемые для таких целей коэффициенты

Ограничение по максимальному значению 2^32 выбрано исходя из максимального кол-ва комбинаций (для обрезанного алфавита в 28 букв и слов по 7, т.к. в реальной задаче их именно 7, общее число комбинаций составит 28^7, таким образом выбранное ограничение лежит примерно посередине интервала, что вполне достаточно для выборки 20-30 тыс. вариантов)

Нам также понадобится еще одна вспомогательная функция - возведение в целочисленную положительную степень:

Функция Степень(Знач а,Знач б, Рез=1 ) Если б>0 Тогда Рез=Рез*а; б=б-1 ; Степень(а,б,Рез); Возврат Рез; Иначе Возврат Рез; КонецЕсли; КонецФункции

Здесь: а - основание степени, б - показатель степени, Рез - результат

2. Выявить зависимость между последовательно расположенными комбинациями, оказалось на удивление простым:

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

Для нашей системы счисления, основанием будут являться степени шестерки, т.е. для получения первого слева разряда, необходимо разделить номер нашей комбинации на 6 в 5-й степени, затем остаток от деления - на 6 в 4-й и т.д.

Таким образом, получаем набор из шести чисел, которые по сути являются порядковыми номерами букв в нашем алфавите.

Получившийся код:

Функция ПолучитьСимволы(Поз,ТекСимв=1 ,СимСтр="") Если ТекСимв<к Тогда Делитель=Степень(СтрДлина(Буквы),к-ТекСимв); ТекОст=Поз%Делитель; СимСтр=Строка(СимСтр)+Сред(Буквы,Цел(Поз/Делитель+?(ТекОст>0 ,1 ,0 )),1 ); ПолучитьСимволы(ТекОст,ТекСимв+1 ,СимСтр); Возврат СимСтр; Иначе СимСтр=СимСтр+Сред(Буквы,(?(Поз=0 ,СтрДлина(Буквы),Поз)),1 ); Возврат СимСтр; КонецЕсли; КонецФункции

Здесь:
Поз - номер комбинации (псевдослучайное число)
ТекСимв - текущий обрабатываемый символ
СимСтр - резльтирующая строка символов
Буквы = строка, содержащая буквы алфавита в стандартном порядке ("абв...юя")
к - число символов в искомом слове (в данном случае = 6)

3. Обратное преобразование также тривиально:

Функция ПолучитьКомбинацию(Слово,ТекСимв=0 ,Поз=0 ) НомСимв=Найти(Буквы,Сред(Слово,к-ТекСимв,1 )); Если ТекСимв>0 Тогда Если ТекСимв<к Тогда Поз=Поз+(НомСимв-1 )*Степень(СтрДлина(Буквы),ТекСимв); ПолучитьКомбинацию(Слово,ТекСимв+1 ,Поз); Иначе Возврат Поз; КонецЕсли; Иначе Поз=?(НомСимв=СтрДлина(Буквы),0 ,НомСимв); ПолучитьКомбинацию(Слово,ТекСимв+1 ,Поз); Возврат Поз; КонецЕсли; КонецФункции

Здесь:
Слово - комбинация символов, номер которой ищем
ТекСимв - текущий обрабатываемый символ (по сути разряд шестиричного "числа")
Поз - искомый номер комбинации

Премешать N чисел:

Для а=1 по N цикл массив[а]=а; Конеццикла; Для а=1 по N-1 цикл Сл=Случ(а,N);// Целое случайное число в интервале [а..N] К=массив[а]; массив[а]=массив[Сл]; массив[Сл]=К; КонецЦикла;

Sc = CreateObject("MSScriptControl.ScriptControl "); Sc.language = "VBscript "; sc.executeStatement("randomize "); оноВотТутаБудет=Sc.eval("rnd ");

Как сделать чтобы числа выбирались произвольно от 1 до 100?

Ранд=_GetPerformanceCounter()%(100 +1 );
похоже это лучшеее

Библиотека мат. функций, где есть генератор сл. чисел:
http://1c.proclub.ru/modules/mydownloads/personal.php?cid=92&lid=2688

В 8.0 для получения случайных чисел можно использовать встроенный генератор GUID.
Вот пример простенькой функции:

//только для целых чисел Функция ПолучитьСлучайноеЧисло(Мин,Макс) //вместо Randomize Для н = 1 По 100 Цикл Уник = Новый УникальныйИдентификатор; КонецЦикла; //генерируем GUID Уник = СокрЛП(Новый УникальныйИдентификатор); //оставляем только цифры Уник = СтрЗаменить(Уник,"- ",""); Уник = СтрЗаменить(Уник,"a ",""); Уник = СтрЗаменить(Уник,"b ",""); Уник = СтрЗаменить(Уник,"c ",""); Уник = СтрЗаменить(Уник,"d ",""); Уник = СтрЗаменить(Уник,"e ",""); Уник = СтрЗаменить(Уник,"f ",""); //знаменатель должен иметь такое же количество нулей + 1 Знаменатель = 10 ; Для н = 2 По (СтрДлина(СтрЗаменить(Уник,Символы.НПП,""))) Цикл Знаменатель = Знаменатель * 10 ; КонецЦикла; Случ = Число(Уник) / Знаменатель; //здесь получается дробное случайное число от 0 до 1 //преобразуем его в случайное число из заданного интервала, округляем до целого ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс); Возврат ЧислоИзИнтервала; КонецФункции

Еще один системный вариант:
Rnd = СоздатьОбъект("System.Random "); Сообщить(Rnd.Next());

© sblogg.ru, 2024
Сонник. Восточный календарь. Интересные факты