Книга знаний

Рекламное место пустует
1С:Предприятие / Приемы программирования

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

Подборка алгоритмов для генерации случайных чисел и перемешивания.Автор статьи: Волшебник | Редакторы: acsent
Последняя редакция №15 от 16.11.11 | История
URL: http://kb.mista.ru/article.php?id=25

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


insider:

Вот не думал, что в 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




http://www.sinor.ru/%7Emy1c/knowhow/rand.html



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

//только для целых чисел
Функция ПолучитьСлучайноеЧисло(Мин,Макс)
    
    //вместо Randomize
    Для н = 1 По 100 Цикл
        Уник = Новый УникальныйИдентификатор;
    КонецЦикла; 
    
        //генерируем GUID
    Уник = СокрЛП(Новый УникальныйИдентификатор);

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

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

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


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

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2011 | Mista.ru

Яндекс.Метрика