Random — Функция Delphi


Random — Функция Delphi

Здравствуйте, Уважаемые.
Меня интересует опыт использования функции Random в многопоточных приложениях при интенсивном её использовании.
Краткое описание проблемы.
Имеется статистическая модель, использующая метод Монте-Карло и реализованная на языке Delphi. При некоторых конфигурациях модели требуется (для получения хорошей статистики) колоссальное количество экспериментов. Речь идёт о миллиардах испытаний, что, разумеется, не обходится без солидных временных затрат. 3 млд. испытаний занимает в среднем 1 час 25 минут. Таких конфигураций нужно насчитать много. Однако, итоговые статистические матрицы обладают свойством аддитивности, что позволяет накапливать статистику частями. Т.е., если имеется двухпроцессорная машина с Xeon по 4 ядра, то запускаем 8 потоков по 375 млн. испытаний, а потом складываем результат и нормируем на общее число испытаний.
Эксперимент показал, что при таком подходе 3 млд. испытаний накапливается всего за полчаса. Однако расчёт, по полученным таким образом статданным, поплыл. Для одного объекта дал завышенный результат, для другого заниженный. И в общем физически ничем не обоснованный. Считая всё это в одном, главном, потоке приложения результат великолепный.
Тут я вспомним свою битву, когда переписывал модель на С++ для Linux. Копаясь в дебрях документации gcc обнаружил, что генератор случайных чисел в Linux очень навороченный. Там есть функции для получения случайных чисел в разных потоках отдельно (суффикс _r) и в «однопоточных» приложениях отдельно.

Всё тщательно проверено и грешить остаётся только на «проседание» Random при её интенсивном использовании из нескольких потоков.

Randomize вызывается 1 раз при старте программы.

Отсюда и возникают вопросы.
1. Есть ли подводные камни при использовании Random из нескольких потоков одновременно?
2. Нет ли в Windows API «потокобезопастного» Random?

Random можно считать генератором случайности с большой натяжкой. Для серъезных опытов используют внешние генераторы.

1. Конечно есть. Обычно используется конгруэнтный метод, а он предполагает хранение предыдущего выработанного случайного числа. Если вам нужен потокобезопасный метод, то храните это число для каждого потока свое. То есть напишите свою функцию с использованием слова threadvar


> 2. Нет ли в Windows API «потокобезопастного» Random?

GetTickCount + QueryPerfomanceCounter + Загрузка ЦП + свободная память + Количество потоков + Трафик + Движения мыши + Нажатые клавиши + Количество окон и т.д. продолдать можно бесконечно. Берешь все вместе, к этому применяешь что-то вроде синуса и оборачиваешь критической секцией.


> KilkennyCat © (09.10.09 21:39) [1]

Сейчас экспериментирую с этим.

> palva © (09.10.09 21:40) [2]

threadvar тут не причём. У меня описан класс потока статистики и использую я не переменные, а поля класса.
Вопрос в другом. Если я использую Random во множестве потоков и каждый из потоков интенсивно использует это функцию, то не происходит ли того, то Random начинает выдавать в разных потоках одно и то же число. Именно так это описано в документации Linux.
Как я понял (если всё правильно понял), из своего маленького исследования, Random в Delphi использует CoCreateGUID. B если кванты времени между двумя вызовами Random очень малы, то он генерит число близкое к предыдущему.
Пытаюсь разобраться.


> Random в Delphi использует CoCreateGUID. B если кванты времени
> между двумя вызовами Random очень малы, то он генерит число
> близкое к предыдущему.

CoCreateGUID использует время потому что

С двумя потоками получилось 42 минуты на 3 млд. экспериментов.
8 потоков давали полчаса. Интересный результат.


> не происходит ли того, то Random начинает выдавать в разных потоках одно и то же число

Теоретически это вполне возможно.

> threadvar тут не причём

threadvar один из способов решить проблему. У вас генератор внутри класса? Как я понимаю это в разы снижает производительность. Если для такой пустяковой функции, как выработка очередного, числа требуется обратиться к свойству, то это ухудшит производительность раза в два. Но это так, замечание в сторону. Вопрос в том как хранить предыдущее выработанное число свое для каждого потока. Если treadvar не подходит храните его не внутри функции, а вместе с переменными потока. У вас же есть такие переменные (всякие счетчики), которые свои для каждого потока. Вот вместе с ними и храните. И передавайте как дополнительный параметр функции random. Хотя на вашем месте я и функцию такую не писал бы, а прямо вставил формулу в код. Время бы сэкономил.


> Random в Delphi использует CoCreateGUID

Если так (с удивлением об этом узнал), то откажитесь от Random Delphi, — сэкономите время. Есть много других отличных и простых генераторов.


> Random в Delphi использует CoCreateGUID

Может, Randomize использует? Тогда другое дело.


> palva © (09.10.09 22:11) [8]

Скорее я чего-то неправильно понял.

Вообще, сомневаюсь, что проблема в рандоме. ОС переключает потоки, как ей взумеатся, то етсь если ситуация заывисит от порядка работы потоков, то результата должен быть б.м. случаен.

К каким общим ресурсам еще обращаются потоки?


> palva © (09.10.09 22:07) [7]

В внутри класса у меня нет генератора случайных чисел. Я использую Random.
В том-то всё и дело. Сейчас изучаю генераторы случайных чисел, которые смог найти в сети. Но все они так или иначе в итоге используют Random.
Или такой вот эксклюзив:

procedure TMaster.DoGenerate(Sender:TObject);
var i:Integer;
g:array[0..4] of TGUID;
b:array[0..63] of Byte absolute g;
AList:TStrings;

procedure FillRandBools;
var j,k:Integer;
begin
with lbBool do
begin
Clear;
AList:=Items;
end;

for j:=Low(b) to High(b) do
for k:=0 to 7 do AList.Add(TrueFalse[b[j] and (1 shl k) > 0]);
end;

procedure FillRandInts;
var j:Integer;
begin
with lbInt do
begin
Clear;
AList:=Items;
end;

for j:=Low(b) to High(b) do AList.Add(IntToStr(b[j]));
end;

begin
for i:=Low(g) to High(g) do CreateGUID(g[i]);
FillRandBools;
FillRandInts;
end;


> TUser © (09.10.09 23:01) [11]

Потоки абсолютно независимы.
> то етсь если ситуация заывисит от порядка работы потоков

Никак не зависит. Это просто n независимых задач. По окончании работы потоков матрицы суммируются.
На двух потоках отклонение существенно меньше, чем на восьми.

Идеально только в одном потоке.


> результата должен быть б.м. случаен.

Результат совершенно не должен быть случаен, поскольку оцениваются математические ожидания физических величин.
Вспомни дифракцию электронов на решётке.

Я посмотрел в System.pas, как всё это устроено.
Промежуточное значение хранится в переменной
RandSeed: longint
и хранится не потокобезопасно.
Вы можете хранить ее в своих переменных потока и перед обращением к Random войти в критическую секцию, переменную RandSeed записать из своей переменной, а после обращения снова сохранить у себя и выйти из критической секции. Так будет потокобезопасно. Но насколько это скажется на производительности — не знаю.

> Но все они так или иначе в итоге используют Random.

Это вы зря. Ищите сишную реализацию и переделывайте на паскале. А зачем вам это нужно? Вас что, не устраивает линейный конгруэнтный метод?

Вот здесь интересные примеры:
http://www.gnu.org/software/gsl/manual/html_node/Random-number-generator-algorithms.html
Но найдете еще, если не будете ограничиваться паскалем.


> palva © (09.10.09 23:25) [14]

Ок. Вот что я сделал. Я перенёс всю логику в поток. Загнал туда же генератор случайных чисел из ALGLIB. В принципе это дало больший квант времени между обращениями к Random.
Результат: теперь 3 млд. на восьми ядрах считается не за полчаса, а за 14 минут.
Сходимость результатов:

1.45337418476940E+0002 (8 потоков)/1.45362162532453E+0002 (0 потоков) = 99.982977650385824621397117929928%

Твой подход спытаю непременно чуть позже.


> Твой подход спытаю непременно чуть позже.

И не надо. Наверняка медленнее будет.


> palva © (09.10.09 23:54)
Это будет не наверняка, а точно медленнее, поскольку каждому потоку придётся ждать, когда любой другой потока не «освободит Random». Поэкспериментировать, однако, тоже полезно будет.


Насколько надежна функция Random в Delphi

Я пишу программу, которая пишет статистические тесты в Delphi (должен быть Delphi), и я слышал, что функциональность Random несколько странная. Вы должны вызвать рандомизацию, чтобы рандомизировать семя случайной функции при запуске программы.

Мне интересно, является ли случайная функция (после рандомизации) случайным образом для статистических тестов или нужен Mersenne twister? Кто-нибудь имеет представление о случайной фактической реализации, которая может сказать мне, насколько это важно?

Является ли Random достаточно надежным для ваших статистических тестов, будет зависеть от контекста, в котором вы собираетесь его использовать.

Сказав это, я написал несколько фрагментов кода Delphi, которые должны выполнять правильную статистику, и использовали Random , например. для получения различных нулевых распределений, псевдорепликаций данных и повторных попыток. До сих пор я не встречал ни одного случая в своем собственном коде, где Random приводило бы к предвзятым или ненадежным результатам или результатам, которые исключали бы его использование для предполагаемого статистического теста. Но то, что имеет место для моего кода, необязательно для вас.

Если вы сомневаетесь, вы можете, конечно, статистически проанализировать результаты вызовов Random (например, в R, SPSS и т.д.) и проверить, нарушает ли распределение результатов требования распределения для вашего конкретного статистического теста (ов), [Если вы хороший ученый, это то, что вы должны делать в любом случае.]

Если вам нужны другие PRNG — например, библиотека TPMath содержит некоторые. (Для более вовлекаемых вещей есть также возможность вызывать сложные статистические функции из R через Delphi.)

Delphi 7 функция Random

Безработынй

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;

var
Form1: TForm1;

procedure TForm1.Button1Click(Sender: TObject);
var a:byte;
begin
a:=random(3);
case a of
0: Caption:=’Иванов’;
1: Caption:=’Медведев’;
2: Caption:=’Сидоров’;
end;
end;

begin
Randomize;
end.

//это мой последний бред))))

var
Form1: TForm1;
Edit1<.Text>:array [1..4] of String = (‘Иванов’, ‘Петров’, ‘Сидоров’, ‘Яковлев’);
Edit2<.Text>:array [1..4] of String = (‘Иван’, ‘Сергей’, ‘Петр’, ‘Павел’);
Edit3<.Text>:array [1..4] of String = (‘Петрович’, ‘Иванович’, ‘Александрович’, ‘Сергеевич’);

procedure TForm1.Button5Click(Sender: TObject);

begin
for i:=1 to 4 do
Edit1(i) := Random(4);
Edit2(i) := Random(4);
Edit3(i) := Random(4);

Безработынй

Рандом возвращает число, а в эдите строка.

//вот..и.после объявления массива надо вставить правильно функцию Random

Илон Маск рекомендует:  Сокращённое свойство font

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, StdCtrls;

type
TForm1 = class(TForm)
GroupBox1: TGroupBox;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button5: TButton;
MainMenu1: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
Memo1: TMenuItem;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Memo2: TMemo;
procedure Button5Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure N7Click(Sender: TObject);
procedure N6Click(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;

var
Form1: TForm1;
Edit1<.Text>:array [1..4] of String = (‘. ‘, ‘. ‘, ‘. ‘, ‘. ‘);
Edit2<.Text>:array [1..4] of String = (‘. ‘, ‘. ‘, ‘. ‘, ‘. ‘);
Edit3<.Text>:array [1..4] of String = (‘. ‘, ‘. ‘, ‘. ‘, ‘. ‘);

procedure TForm1.Button5Click(Sender: TObject);

end;
writeln(FIO);
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := »;
Edit2.Text := »;
Edit3.Text := »;

procedure TForm1.Button3Click(Sender: TObject);
begin
Close;
end;

procedure TForm1.N7Click(Sender: TObject);
begin
AboutBox1.ShowModal;
end;

procedure TForm1.N6Click(Sender: TObject);
begin
close;
end;

Ненавижу 1С Ненавижу 1С

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, StdCtrls;

type
TForm1 = class(TForm)
GroupBox1: TGroupBox;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button5: TButton;
MainMenu1: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
Memo1: TMenuItem;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Memo2: TMemo;
procedure Button5Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure N7Click(Sender: TObject);
procedure N6Click(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;


var
Form1: TForm1;
Edit1:array [1..4] of String = (‘Иванов’, ‘Петров’, ‘Сидоров’, ‘Яковлев’);
Edit2:array [1..4] of String = (‘Иван’, ‘Сергей’, ‘Петр’, ‘Павел’);
Edit3:array [1..4] of String = (‘Петрович’, ‘Иванович’, ‘Александрович’, ‘Сергеевич’);

procedure TForm1.Button5Click(Sender: TObject);
var n:byte;
begin
try n=random(10); except Randomize;end;
self.Edit1.Text:=Unit1.Edit1[round(random(high(Unit1.Edit1)))]
self.Edit2.Text:=Unit1.Edit2[round(random(high(Unit1.Edit2)))]
self.Edit3.Text:=Unit1.Edit3[round(random(high(Unit1.Edit3)))]
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := »;
Edit2.Text := »;
Edit3.Text := »;

procedure TForm1.Button3Click(Sender: TObject);
begin
Close;
end;

procedure TForm1.N7Click(Sender: TObject);
begin
AboutBox1.ShowModal;
Randomize;
end;

procedure TForm1.N6Click(Sender: TObject);
begin
close;
end;

Функция Random() в Delphi

Привет народ!
Кто поможет с маленькой прогой?
Прога проста, она формирует сроку с цифрами, через запятую. Цифры берутся из заданного диапазона от 1 до 100, функцией RandomRange(1,100)
Прогу в принципе написал, но не устраивает только один момент — это то что очень часто Random выдает одни и те же значения, а мне нужно уникальные в строке получить.
К примеру иногда выдается 12,2,44,99,75,12,34,67. (здесь видно что 12 выдается 2 раза).
Как отучить Random от повторения в пределах одного цикла?
Мой алгоритм самый просто:

for I := 1 to 5 do Begin
K:=RandomRange(1,100);
Comb:=Comb+’|’+IntToStr(k);

Программирую на Delphi 2010

Во-вторых: заведи множество и полученные числа проверяй на вхождение в множество, если − нет, то бери число и выводи, и добавляй во множество.

Клуб программистов

Delphi programming

Подписаться на рассылку:

Random

генерирует случайное целое число или число с плавающей точкой

|| 1 function Random : Extended;

|| 2 function Random ( LimitPlusOne : Integer ) : Integer;

Описание:

Delphi функция Random генерирует произвольные числа. Результатом выполнения функция Random могут быть числа с плавающей точкой диапазоне :

0 var
float : single;
int : Integer;
i : Integer;

begin
// Get floating point random numbers in the range 0

Результат выполнения:

float = 2.3283064365387E-10
float = 0.031379981256104
float = 0.861048460006714
float = 0.202580958604813
float = 0.2729212641716

int = 68
int = 32
int = 17
int = 38
int = 43

Random и Randomize

Функция Random генерирует и возвращает случайное число. Синтаксис функции следующий:

Функция возвращает случайное число большее или равное 0 и строго меньше L (то есть L не входит в диапазон возвращаемых значений). Если параметр опущен (последний вариант из трёх приведённых выше), то возвращается вещественное число между 0 и 1 (0 включительно, 1 не входит в диапазон возвращаемых значений).

Математически это можно записать так:

ПРИМЕЧАНИЕ
FreePascal использует в процедуре Random имитацию случайностей Мерсенна Твистера. Эта реализация имеет большее статистическое распределение, чем, например, алгоритм Линейного Конгруэнтного генератора, но работает значительно медленнее, чем последний. Если скорость выполнения программы критична, то должны быть рассмотрены альтернативные генераторы случайных чисел.

Процедура Randomize в Паскале запускает генератор случайных чисел. Синтаксис:

Процедура Randomize инициализирует генератор случайных чисел FreePascal, задавая значение переменной Randseed, вычисленное с помощью системных часов.

Как запустить генератор случайных чисел в Паскале

Пример программы, где используются разные варианты получения случайных чисел, приведён ниже:


ВАЖНО!
Перед использованием функции Random надо обязательно вызвать процедуру Randomize, чтобы запустить генератор случайных чисел. Иначе функция Random будет возвращать НЕ случайное число.

Процедура Randomize вызывается ТОЛЬКО ОДИН РАЗ в начале программы. Типичная ошибка новичков заключается в том, что они вызывают Randomize перед каждым вызовом Random. Этого делать не надо.

Что такое Randseed

Давайте сначала попробуем не использовать процедуру Randomize. Например, так:

В этом примере функция Random будет возвращать какое-то значение, но оно НЕ будет случайным числом. Сколько бы раз вы не запускали программу, она всегда будет выводить одинаковые числа.

В моём случае это были числа 54 и 59. В вашем случае это могут быть другие числа, но суть не в этом, а в том, что они будут всегда одинаковыми.

Теперь попробуем сделать так:

Теперь числа будут другими. В моём случае 69 и 7. Правда, они тоже не будут изменяться, то есть не будут случайными.

Однако эти числа изменились. Потому что мы изменили значение глобальной переменной Randseed.

Переменная Randseed — это глобальная предопределённая переменная в FreePascal. Она также есть и в Делфи. Так что всё сказанное будет справедливо и для Делфи.

Эта переменная задаёт начальное значение для генератора случайных чисел. Опираясь на это значение функция Random генерирует случайное число. Но, если значение переменной Randseed будет всегда одинаковым, то никаких случайных чисел мы не получим.

Поэтому при запуске программы надо хотя бы один раз вызвать процедуру Randomize, которая устанавливает начальное значение переменной Randseed, получая системное время и из этих данных формируя значение для переменной Randseed.

Так как программа запускается в какое-то случайное время, то и значение переменной Randseed будет случайным. А, следовательно, и функция Random будет возвращать случайные значения.

Давайте попробуем написать свою процедуру Randomize. Она может быть, например, такой:

Я не буду в подробностях разъяснять работу процедуры DecodeTime. Скажу только, то она возвращает, час, минуту, секунду и миллисекунду текущего времени компьютера. Мы используем миллисекунду, значение которой и присваиваем переменной Randseed.

Можете попробовать использовать эту процедуру вместо стандартной процедуры Randomize и убедиться, что функция Random возвращает случайные значения. Не забудьте подключить к программе модуль SysUtils, в котором объявлена процедура DecodeTime, иначе программа не откомпилируется.

ПРИМЕЧАНИЕ
Исходя из того, что процедура Randomize задаёт начальное значение переменной Randseed на основе текущего времени компьютера, можно предположить, что, например, если запустить программу сегодня в 12:00:00 и завтра в это же время, то функция Random вернёт одинаковые числа.

К сожалению, проверить это очень трудно, так как запустить программу в одно и то же время с точностью до миллисекунды будет практически невозможно.

random — Насколько надежна функция Random в Delphi

Я пишу программу, которая пишет статистические тесты в Delphi (должен быть Delphi), и я слышал, что функциональность Random несколько странная. Вы должны вызвать рандомизацию, чтобы рандомизировать семя случайной функции при запуске программы.

Мне интересно, является ли случайная функция (после рандомизации) случайным образом для статистических тестов или нужен Mersenne twister? Кто-нибудь имеет представление о случайной фактической реализации, которая может сказать мне, насколько это важно?

    2 9
  • 7 апр 2020 2020-04-07 00:27:58
  • Daisetsu

9 ответов

Возврат случайного значения между 0..9

Примечание. Проверяйте длину FloatToStr (Random) перед использованием или используйте любую другую цифру из десятичной части.

  • 7 апр 2020 2020-04-07 00:28:00
  • DejanR


С веб-сайта Embarcadero:

_lrand — функция генератора длинных случайных чисел. _rand использует мультипликативный конгруэнтный генератор случайных чисел с периодом 2 ^ 64 для возврата последовательных псевдослучайных чисел в диапазоне от 0 до 2 ^ 31 — 1.

Генератор повторно инициализируется вызовом srand со значением аргумента 1. Его можно установить в новую начальную точку, вызвав srand с заданным количеством семян.

  • 7 апр 2020 2020-04-07 00:28:00
  • dbasnett

Если они не изменили реализацию, так как я ее проанализировал (Delphi 4 IIRC), Delphi PRNG реализуется следующим образом:

(Псевдокод/​​предполагаем, что умножения находятся на сколь угодно больших целых числах)

  • 7 апр 2020 2020-04-07 00:28:00
  • CodesInChaos

Просто добавьте в пул возможностей — Windows предлагает ряд встроенных функций криптографии. Вероятно, для них также есть оболочка Delphi, если она по умолчанию не включена.

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

И если этого недостаточно, вы также можете попытаться зарегистрироваться в Quantum Random Bit Generator Service для некоторых ДЕЙСТВИТЕЛЬНОсильные > случайные значения.

  • 7 апр 2020 2020-04-07 00:28:00
  • Vilx-

Если вы не покупаете какое-то относительно эзотерическое оборудование, наилучшим приближением к случайным числам, которые может предоставить компьютер, является полностью детерминированная псевдослучайная последовательность. В общем случае функция рандомизации использует некоторое относительно случайное значение (часто основанное на времени, но иногда на движениях мыши — я не знаю, что делает Delphi) в качестве семени, которое обеспечивает точку входа в псевдослучайную последовательность. Без этого вы в конечном итоге получите один и тот же набор случайных чисел в том же порядке каждый раз, что в первую очередь приведет к победе в использовании случайных чисел.

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

  • 7 апр 2020 2020-04-07 00:28:00
  • James McLeod

Если вы ищете способ гарантировать уникальность случайных чисел с наивысшим временем выполнения, About.com создал вызов Самый быстрый уникальный генератор случайных чисел и реализация Патрика ван Логчем был избран победителем.

  • 7 апр 2020 2020-04-07 00:27:59
  • Vantomex

Я не мог сопротивляться.

  • 7 апр 2020 2020-04-07 00:27:59

  • Mick

Delphi PRNG, как и почти все языки программирования RTL PRNG, является линейным конгруэнтным генератором.

Илон Маск рекомендует:  Что такое код domattribute &#62;name

Это достаточно хорошо для большинства мелких вещей, но есть вещи, на которые нужно следить. В частности, следите за младшими битами: шаблон умножения и добавления означает, что младшие разряды не очень случайны. Но это в целом относится только к большим 32-битным значениям, выведенным, а затем усеченным с mod или аналогичным. Использование Random(10) для вырезания значения между 0 и 9 внутренне использует умножение во всем 32-битном диапазоне, а не на операцию mod .

  • 7 апр 2020 2020-04-07 00:27:59
  • Barry Kelly

Является ли Random достаточно надежным для ваших статистических тестов, будет зависеть от контекста, в котором вы собираетесь его использовать.

Сказав это, я написал несколько фрагментов кода Delphi, которые должны выполнять правильную статистику, и использовали Random , например. для получения различных нулевых распределений, псевдорепликаций данных и повторных попыток. До сих пор я не встречал ни одного случая в своем собственном коде, где Random приводило бы к предвзятым или ненадежным результатам или результатам, которые исключали бы его использование для предполагаемого статистического теста. Но то, что имеет место для моего кода, необязательно для вас.

Если вы сомневаетесь, вы можете, конечно, статистически проанализировать результаты вызовов Random (например, в R, SPSS и т.д.) и проверить, нарушает ли распределение результатов требования распределения для вашего конкретного статистического теста (ов), [Если вы хороший ученый, это то, что вы должны делать в любом случае.]

Если вам нужны другие PRNG — например, библиотека TPMath содержит некоторые. (Для более вовлекаемых вещей есть также возможность вызывать сложные статистические функции из R через Delphi.)

Random — Функция Delphi

Ещё одна оригинальная статья от меня ждёт своих читателей, но на этот раз это будет не о крэкерстве, а о высокоуровневом языке программирования Delphi. Давайте рассмотрим генератор случайных чисел, который частенько мельком называют генератором псевдослучайных чисел, не поясняя всей глубокой сути этой приставки «псевдо». На самом же деле, когда речь заходит о компьютерах, процессорах и вычислительных системах о «случайности» можно забыть, потому как все процессы происходящие в компьютере являются результатом строгих математико-логических операций, происходющих в процессоре и других вспомогательных микросхемах компьютера (контроллер прерываний, таймер, сопроцессор. ).

Есть в Delphi две интересные функции: Randomize и Random, а также переменная RandSeed. Надеюсь, что вы знаете об их использовании. Но, наверняка, редко используете переменную RandSeed (dword), а вот с ней то я и захотел разобраться, а также решил поделиться своим опытом с вами.

Поскольку я всё-таки крэкер, давайте пойдём крэкерским путём — возьмём готовую программу и поглядим как она работает — я взял «Генератор случайных паролей», написанный Цованяном Романом Сейрановичем. Но сначала я посмотрел как же Randomize рандомайзит, на основе чего ? Оказывается, на основе GetSystemTime, которая возвращает текущую дату и точное время (с миллисекундами) но не у нас, а по Гринвичу, для меня это было — на 4 часа меньше, чем на часах. Затем я поглядел алгоритм функции Randomize — он выглядит в Delphi5 следующим образом:

var St: systemtime;
GetSystemTime(St);
RandSeed := ((St.wHour*$3c + St.wMinute)*$3c + St.wSecond)*$3e8+St.wMilliseconds;

Как видите, всё очень просто. А сама функция Random уже использует только переменную RandSeed, берёт на основе неё случайное число, а потом меняет RandSeed, причём строго математически, то есть если при каждом запуске программы заносить в RandSeed одинаковую переменную, то набор «случайных» чисел, полученных с помощью Random при том же диапазоне будет такой же.

Я решил провести небольшой опыт по взлому пароля, сгенерированного с помощью программы «Генератор случайных паролей». Запустил программу, взял длинный не взламываемый перебором пароль и запаковал с этим паролем в ZIP архив какой-то файл. На следующий день я «захотел» взломать этот архив и сделал следующим образом — посмотрел время создания архива и понял, что пароль был сгенерирован где-то в течение 2-3 минут до создания архива, и написал я небольшую программку, которая эмулировала Randomize, но я использовал не GetSystemTime, чтобы заполнить структуру St: Systemtime, а сам заполянял её с шагом одна миллисекунда и начиная от 3 минут до создания архива, не забывая также, что час взят по Гринвичу. Кроме того, я подглядел алгоритм создания пароля в программе «Генератор случайных паролей». То есть, я создал лист паролей. Для каждой миллисекунды я сделал не один пароль, а несколько (я «забыл» длину пароля, но помнил, что она где-то от 4 до 16 символов). То есть у меня получился внушительный лист паролей на почти 30 мегабайт. Затем я взял программку Advanced ZIP Password Recovery и поручил ей мой запароленный архив и дал в помощь свой 30 мегабайтный «словарик», через 3 секунды пароль был найден, надо же.

Теперь давайте прикинем: если мы не знаем время генерации ключа, но знаем, что некто любит генерировать «трудновзламываемые» пароли с помощью «Генератора случайных паролей», то что мы будем делать ? Теперь посчитаем сколько миллисекунд в сутках 1000*60*60*24 = 86’400’000. Допустим, что пароль длинной от 4 до 20 символов, то есть каждую миллисекунду мы получим по 17 паролей, значит за сутки у нас будет 86400000*17 = 1’468’800’000 — всего то полтора миллиарда паролей. А программка Advanced ZIP Password Recovery перебирает, на моём не слишком крутом компьютере, по 520000 паролей в секунду. Значит любой пароль, сгенерированный в любой день, в любое время с помощью программы «Генератор случайных паролей» будет найден за 1468800000/520000 = 2824 секунды, то есть примерно за 50 минут. Единственной проблемой является, то что Advanced ZIP Password Recovery не поддерживает внешний плагин, который бы «давал» пароли для перебора, поэтому придётся создавать гигабайтные словари паролей, что не очень удобно, но весьма реально.

Таким образом создавать «Генератор случайных паролей» на основе стандартных функций Delphi весьма глупо. «А как тогда не глупо ?» — спросите вы. Вообще, сейчас серьёзные организации пароли любят генерировать на основе белого шума. Нам же до этого шума далеко — брать нам его негде. А вот пользователя, сидящего за компьютером мы вполне можем использовать, а именно его замечательный человеческий фактор — аналоговую случайную функцию: запустим программу и заметим время, а потом, когда пользователь нажмёт «Генерировать пароль» — заметим время ещё раз — вот и одна случайность, можно также «последить» за траекторией мыши — вот вам вторая случайность. Теперь ещё можно поиcпользовать всякие хитрые функции GetDiskFreeSpace, также можно использовать хэш функции для замедления процесса создания листа паролей, то есть если ваша функция Randomize будет работать хотя бы 100 миллисекунд — уже потребуется 100 суток, чтобы создать весь лист паролей. Но тут ведь ещё одна проблема RandSeed — dword, то есть 32 бита, а это 2^32 = 4294967296. Поделим ка мы его на количество миллисекунд в сутках 4294967296/86’400’000 = 50, то есть потребуется 50*50 = 2500 минут в нашем случае, чтобы найти пароль от любого RandSeed, значит надо брать в качестве RandSeed переменную длинее 32 бит, мне приходит на ум лишь Int64 — вот тогда ваш пароль уже никто не взломает за разумное время.

Вы скажете: что за фигня — зачем крэкеру это знать ? Может, и не надо, но по-моему уважающему себя крэкеру, а также «продвинутому» программисту полезно знать о вот таком нестандартном подходе к обыденным вещам, понять суть происходящего, познать основы криптоанализа и глубину возможностей крэкерства и недостатков высокоуровневого программирования, а не только снимать аспр, да и то через раз.

Удачи вам в вашей деятельности, чем бы вы ни занимались, Bad_guy.

Обучающий курс. 12. Функции и процедуры в Delphi. Математические вычисления. Случайные числа

Сегодня мы поговорим о процедурах и функциях в Delphi . Что такое процедура? Это маленькая программа, выполняющая операции с указанными данными. Различают собственно процедуры и функции. Их основное отличие — процедура просто совершает какие-либо операции, а функция обязательно выдаёт какой-либо результат в результате своей работы. Существует огромное количество стандартных процедур и функций. Подпрограммы (так называют процедуры и функции) можно писать и самостоятельно, но об этом речь пойдёт позже. Сейчас нам нужно научиться работать с готовыми функциями.

Общие сведения о подпрограммах

Фактически, подпрограмма — это такая же полноценная программа, просто работает она не отдельно, не сама по себе, а включена в другую программу.
У подпрограммы всегда есть имя. Имя строится по тем же правилам, что и идентифмкатор. Как правило, имена даются вполне логичные. Например, если функция находит максимальное из нескольких чисел, то её логично назвать Max .
Подпрограммы могут иметь входные параметры. Входные параметры — это данные, которые сообщаются подпрограмме до начала её работы, а в процессе выполнения эти данные могут использоваться. Тем не менее, подпрограммы могут и не иметь входных параметров. Входные параметры также называют аргументами. Например, функции, которая узнаёт текущее время, никакие дополнительные параметры не нужны, а вот если функция считает факториал, то обязательно должно быть число, для которого он считается.
Как было сказано выше, функция выдаёт какое-то значение в результате своей работы. Процедура в общем случае значения не выдаёт.

Вызов подпрограмм

Вызываются подпрограммы по имени. Если подпрограмме требуется передать какие-либо параметры, то они указываются в скобках после имени подпрограммы через запятую. Если входные параметры отсутствуют, достаточно просто написать имя подпрограммы, либо оставить скобки пустыми. В случае, если работа происходит с функцией, результат можно «сохранить» в какой-то переменной, просто «присвоив» этой переменной функцию.

Обратите внимание: работа с функциями происходит как с обычными переменными, просто их значения вычисляются «на лету».

Функции математических вычислений

Эти функции работают с числовыми данными. Как правило, входным параметром является какое-то число, а выходным — результат вычисления. Практически везде аргумент является либо целым числом ( Integer ), либо вещественным ( Real ). Возвращаемое значение — тоже число. Рассмотрим некоторые из этих функций:

Abs(x) — модуль (абсолютное значение) указанного числа x . Пример: Abs(-5) = 5 .


Sin(x) — синус числа x . Здесь x — угол в радианах (не в градусах!). Пример: Sin(Pi/2) = 1 .

Cos(x) — косинус числа x . Аналогично, x — радианы. Пример: Cos(Pi) = -1 .

Exp(x) — экспонента, e x ( e в степени x ).

Ln(x) — натуральный логарифм числа x . Пример: Ln(Exp(2)) = 2 .

Sqr(x) — квадрат числа x ( x 2 ). Пример: Sqr(5) = 25 .

Sqrt(x) — квадратный корень числа x . Пример: Sqrt(64) = 8 .

Int(x) — целая часть числа x . Пример: Int(1.234) = 1 .

Frac(x) — дробная часть числа x . Пример: Frac(1.234) = 0.234 .

Round(x) — округление аргумента до ближайшего целого числа. Пример: Round(1.234) = 1 .

Trunc(x) — целая часть вещественного числа x. Пример: Trunc(1.234) = 1 .

Илон Маск рекомендует:  Что такое код ctype_alpha

Pred(x) — предыдущее значение x (например, для x = 2 это 1 ).

Succ(x) — следующее значение x (для x = 2 это 3 ).

Odd(x) — проверка аргумента на нечётность. Функция возвращает значение True , если аргумент является нечётным числом и False — если чётным. Пример: Odd(5) = True .

Предсказываю вопрос: в чём отличие Int() от Trunc() ? А отличие в том, что Int() возвращает число вещественного типа, а Trunc() — целочисленного .

Это лишь часть всех доступных функций. На самом деле их гораздо больше. Но помимо функций есть ещё процедуры.

Процедуры работы с числами

Поскольку процедуры в результате работы не выдают никакого значения, процедуры работы с числами просто изменяют переданные им параметры-переменные.

Inc(x) — увеличение аргумента на единицу. Фактически, это то же самое, что x:=x+1 . Тем не менее, рекомендуется использовать именно эту функцию, так как работает она быстрее.
Примечание: под понятием «быстрее» подразумевается, конечно, быстрота «компьютерная». Компьютер выполняет миллионы операций в секунду и для человека такие вещи незаметны.

Inc(x,n) — увеличение аргумента на число n . Эквивалентно записи x:=x+n .

На самом деле, это не две разные процедуры — просто параметр n является необязательным. Да, бывают необязательные параметры, которые можно указать, а можно и не указывать. Если они отсутствуют, то просто берётся какое-то значение по умолчанию. В данном случае n по умолчанию имеет значение 1 .

Dec(x,n) — уменьшение аргумента на n единиц. Точно также, как и в Inc , параметр n является необязательным. Эквивалентно записи x:=x-n .

В документации необязательные параметры обычно заключают в квадратные скобки, т.е. обычно пишут Inc(x , [n]) . Обратите внимание: это лишь условное обозначение, которое создано с целью узнавания, что параметр необязательный. В программном коде никаких скобок нет и быть не может.

Не хватает стандартных математических функций?

Существует дополнительный модуль с именем Math , в котором содержится большое число математических функций. Например, если нужно посчитать гиперболический арксеканс числа, то мучаться и описывать способ его вычисления вручную не придётся — есть готовая функция ArcSecH() .
Чтобы подключить модуль Math , откройте исходный код модуля. Для этого, когда открыта форма, следует нажать F12 , либо выбрать пункт меню View » Toggle Form/Unit . Далее нужно переместиться в самое начала модуля в раздел uses . В этом разделе через запятую описываются имена подключённых модулей. Как можно заметить, даже при наличии пустой формы несколько модулей уже подключены. В этот список и следует добавить Math :

Всё, теперь в Вашем распоряжении большое количество математических функций.

Пример комбинирования функций

Раз уж речь пошла о математических функциях, пусть пример будет на них и основан. Допустим, у нас есть такая сравнительно сложная функция:

Нам нужно создать программу, которая бы вычисляла значение этой функции по заданным числам x и y . Рассмотрим поэтапно элементы функции:
1) Возведение числа e в степень, модуль — функции Exp() и Abs() соответственно.
2) Натуральный логарифм — функция Ln() .
3) Число e . Часто спрашивают — как получить число e ? Ведь это, по сути, такая же константа, как и число пи . Но она не объявлена. А ответ прост: e = e 1 , поэтому e — это exp(1) .
4) Тангенс — функция Tan() .
Всё необходимое у нас есть, поэтому можно приступить к записи. Главное — не забывать заключать в скобки отдельные элементы формулы, чтобы порядок действий сохранился (в нашем примере это не потребуется).

Как возвести число в степень?

Почему я останавливаюсь на таких вопросах? Просто они очень часто возникают у новичков и не каждый может догадаться, как выполнить требуемую операцию.

Способ 1. X y можно преобразовать к виду e ln(x)⋅y . Тогда возведение в степень можно записать так:

Способ 2. В модуле Math есть функция для возведения в степень — Power . У функции 2 аргумента — основание и показатель степени. Запись, соответственно, следующая :=Power(x,y);

Случайные числа

Зачем нужны случайные числа? Как правило, чтобы проверить результаты какого-то эксперимента при различных условиях. На основе случайных чисел можно вычислять различные вероятности. Во всех языках программирования есть возможность использовать случайные числа.

В Pascal (и Delphi соответственно) случайные числа генерируются функцией Random . Функция принимает один параметр, да и тот необязательный. Этот параметр позволяет указать границу диапазона, из которого будет выбрано случайное число. Итак: Random([Range: Integer]) . Если Range указан, то число выбирается из диапазона 0 ( X — само случайное число, которое будет получено). Обратите внимание, что сама граница в диапазон не включается, т.е. Random(10) никогда не выдаст число 10 , хотя 0 — запросто. Если диапазон не указан, то он считается равным единице, т.е. 0 .

Пример. Создадим форму с кнопкой, но пусть кнопка каждую секунду изменяет своё положение. Воспользуемся таймером ( TTimer , вкладка System палитры компонент). Interval оставим без изменения ( 1 сек. ), а вот в обработчике запрограммируем произвольное изменение положения кнопки на форме. Разберёмся, что нам нужно:
1) Позиция кнопки на форме. Как Вы уже знаете, за положение отвечают свойства Left и Top , которые указывают положение левого верхнего угла кнопки относительно левого верхнего угла формы. Именно этим свойствам мы будем присваивать произвольные значения.
2) Каков будет диапазон для генерации случайных чисел? Очевидно, что кнопка не должна уйти за границы формы. Значит нам нужно подключить размеры самой формы, т.е. её высоту и ширину. В данном случае будем использовать не Width и Height , а ClientWidth и ClientHeight , так как в первые свойства входят заголовок и границы формы, а это лишние пиксели, за которые кнопка может вылезти. Однако и это ещё не всё — из этих размеров мы должны вычесть соответственно ширину и высоту самой кнопки, иначе она может частично скрыться за границами.
Пишем обработчик:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Запускаем программу и наблюдаем. Кнопка действительно прыгает с места на место. Но есть один крайне неприятный момент: запустите программу несколько раз и понаблюдайте за точками, в которые попадает кнопка. Вы сразу заметите, что каждый раз кнопка перемещается по одним и тем же точкам.

Отчего это происходит? Дело в том, что числа, выдаваемые функцией Random() на самом деле не являются случайными — они псевдослучайны , т.е. наблюдается повторение. К счастью, решение есть — специальная процедура Randomize() инициализирует генератор случайных чисел, который выдаёт действительно случайные числа. Вызвать эту процедуру нужно всего один раз за время работы программы — обычно это делается при запуске (например, в событии OnCreate формы). Процедура не принимает никаких параметров. Вернёмся к нашему примеру:

procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize
end ;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Теперь кнопка будет прыгать совершенно по разным точкам при каждом запуске программы.

Кстати, можно дописать скобки к названию процедуры — от этого работа не изменится: Randomize; = Randomize(); А запись немного красивее (на мой взгляд).

Дополнительные возможности редактора кода

В редакторе кода есть одна очень хорошая вещь — после того, как написано имя процедуры или функции и открыта скобка, появляется подсказка со всеми параметрами, которые подпрограмма принимает. Более того, там же указаны и типы данных всех параметров. Попробуйте, к примеру, набрать Inc( и подождать секунду — появится подсказка:

Как и было отмечено выше, второй параметр является необязательным и он заключен в квадратные скобки.

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

Если функции или процедуре входные параметры не нужны, подсказка всё равно появится и сообщит об этом:

Если после набора имени и скобки подсказка не появилась, то и при компиляции программы скорее всего возникнет ошибка. Причиной, по которой компилятор не смог найти указанную функцию или процедуру, может быть ошибка при наборе имени, либо модуль, в котором описана подпрограмма, не подключен.
Ошибки при компиляции появляются внизу окна редактора кода с указанием номера строки, где обнаружена ошибка и описанием самой ошибки.
Попробуем запросить процедуру Randomiz (например, мы случайно недописали букву » e » на конце):

Строка с ошибкой выделилась, а внизу появился её номер ( 28 ) и описание — Undeclared identifier (неописанный идентификатор).

Заключение

В этом уроке охвачено сразу несколько тем — знакомство с процедурами и функциями, обзор математических функций и процедур, модуль Math, работа со случайными числами, а также удобства редактора кода и выделение ошибок при компиляции.

Насколько надежен Случайная функция в Delphi

Я пишу программу, которая написать статистические тесты в Delphi (должно быть Delphi), и я слышал, что случайная функциональность несколько странно. Вы должны вызвать рандомизации для рандомизации семени случайной функции при запуске программы.

Я интересно, если случайная функция (после вызова рандомизации) является случайной достаточно для статистических испытаний или нужен Мерсенна твистер? Кто-нибудь есть какое-либо представление о фактической реализации случайной, который может сказать мне, насколько это важно?

ПСЧ Delphi, как и почти все языка программирования RTL PRNGs, является линейным конгруэнтным генератором .

Это достаточно для большинства мелких вещей, но есть вещи , чтобы не упустить. В частности, следить за биты низкого порядка: шаблон умножения и добавление означает , что младшие биты являются не очень случайно на всех. Но это касается в основном только для больших 32-битных значений вырванного и затем укорочены с mod или подобными. Используя Random(10) срывать значение между 0 и 9 внутренне использует умножение на весь 32-битный диапазон , а не mod операции.

Я не мог сопротивляться.

Являются ли Random достаточно надежная для ваших статистических тестов будут зависеть от контекста , в котором вы собираетесь его использовать.

Сказав это, я написал несколько частей Delphi кода , который нужно сделать соответствующие статистические данные, и используются , Random например , для получения различных нулевых распределений, данных псевдо-репликаций и resamplings. До сих пор я не встречал ни был в моем собственном коде , где Random бы давали необъективные или недостоверные результаты или результаты , которые бы препятствовали его использованию для предполагаемого статистического теста. Но то , что справедливо для моего кода не обязательно должен держать для ваших.

Если у вас возникли сомнения, вы могли бы, конечно , статистически анализировать результаты звонков Random (например, R, SPSS и т.д.) , а также изучить вопрос о распределении результатов нарушают дистрибутивные требования для вашего конкретного статистического теста (ов). [Если вы надлежащий ученый, это то , что вы должны делать в любом случае.]

В случае , если вам нужна другой PRNGs — например, TPMath библиотека содержит некоторые. (Для более сложных вещей, есть также возможность вызова сложных статистических функций из R с помощью Delphi.)

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL