Генератор случайных чисел в 1С
Перем глСлучайноеЧисло Экспорт;
Функция глРанд(От=0,До=1,Шаг=0,Парам=0) Экспорт
Если Парам<>0 Тогда
глСлучайноеЧисло=Парам;
КонецЕсли;
Если Число(глСлучайноеЧисло)=0 Тогда
глСлучайноеЧисло=Число(СтрЗаменить(""+ТекущееВремя(),":",""));
глСлучайноеЧисло=(16807*глСлучайноеЧисло)%2147483647;
КонецЕсли;
глСлучайноеЧисло=(16807*глСлучайноеЧисло)%2147483647;
глСлучайноеЧисло=макс(глСлучайноеЧисло,-глСлучайноеЧисло);
СлЕд=глСлучайноеЧисло/2147483647;
СлВыб=СлЕд*(До-От)+От;
Если Шаг>0 Тогда
СлВыб=От+Окр((СлВыб-От)/Шаг)*Шаг;
КонецЕсли;
Возврат(СлВыб);
КонецФункции
http://www.sinor.ru/~my1c/knowhow/rand.html
randSeed=ranseed%100000000001;
randSeed=randSeed*1103515245+12345;
и проблемы со скоростью снимаются...
randSeed=randseed%100000000001;
а так? ;-)
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
В 8.0 для получения случайных чисел можно использовать встроенный генератор GUID.
Вот пример простенькой функции:
//только для целых чисел
Функция ПолучитьСлучайноеЧисло(Мин,Макс)
//вместо Randomize
Для н = 1 По 100 Цикл
Уник = Новый УникальныйИдентификатор;
КонецЦикла;
//генерируем GUID
Уник = СокрЛП(Новый УникальныйИдентификатор);
//оставляем только цифры
Уник = СтрЗаменить(Уник,"-","");
Уник = СтрЗаменить(Уник,"a","");
Уник = СтрЗаменить(Уник,"b","");
Уник = СтрЗаменить(Уник,"c","");
Уник = СтрЗаменить(Уник,"d","");
Уник = СтрЗаменить(Уник,"e","");
Уник = СтрЗаменить(Уник,"f","");
//знаменатель должен иметь такое же количество нулей + 1
Знаменатель = 10;
Для н = 2 По (СтрДлина(СтрЗаменить(Уник,Символы.НПП,""))) Цикл
Знаменатель = Знаменатель * 10;
КонецЦикла;
Случ = Число(Уник) / Знаменатель; //здесь получается дробное случайное число от 0 до 1
//преобразуем его в случайное число из заданного интервала, округляем до целого
ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс);
Возврат ЧислоИзИнтервала;
КонецФункции
|