почему GetRandom выдает одну и туже последовательность?

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

Ответить
MLT-Coder
Сообщения: 10
Зарегистрирован: 04 сен 2006, 12:44

почему GetRandom выдает одну и туже последовательность?

Сообщение MLT-Coder » 05 сен 2006, 12:14

я наверно уже тут всех достал, но все же...

есть код php

Код: Выделить всё

<pre><?php

$link = ibase_connect('localhost:F:\1.FDB', 'SYSDBA', 'masterkey');

$query = "SELECT InitRandom() from qwe";
$res = ibase_query($link, $query);
ibase_free_result($res);

$query = "SELECT GetRandom(100000), qwe.* from qwe ORDER BY 1";
$res = ibase_query($link, $query);
while ($row = ibase_fetch_assoc($res)) print_r($row);
ibase_free_result($res);

ibase_close($link);

?>
на этот раз использую Firebird-1.5.3.4870-0-Win32

вот результат выполнения кода

Код: Выделить всё

Array
(
    [GETRANDOM] => 67165
    [ID] => 1
    [NAME] => Ivan
)
Array
(
    [GETRANDOM] => 31869
    [ID] => 2
    [NAME] => Masha
)
Array
(
    [GETRANDOM] => 16179
    [ID] => 4
    [NAME] => Dasha
)
Array
(
    [GETRANDOM] => 37223
    [ID] => 5
    [NAME] => Ivan'qwe
)
Array
(
    [GETRANDOM] => 42567
    [ID] => 3
    [NAME] => Pasha
)
сколько бы раз я его не выполнял он не меняетя
последовательность одна и таже и всегда начинается заново

хотя через Firebird ISQL Tool
все работает прекрасно

в чем может быть дело?

возможно потому что php каждый раз заново подсоединяется и отсоединяется, но ведь я в начале вызвал InitRandom()

НО самое главное когда Firebird ISQL Tool находится в подключенном состояние вся проблема исчезает и php код начинает выдавать разные последовательности случайных чисел, стоит мне набрать команду EXIT; в Firebird ISQL Tool php код опять забывает о случайных числах и выдает одно и тоже

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Сообщение Dimitry Sibiryakov » 05 сен 2006, 12:42

Может быть стоит вызывать InitRandom пореже или с разными аргументами?..

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 05 сен 2006, 14:33

я не помню, вызываю ли я randomize в инициализации random_udf. возможно что и нет.
тогда да, надо после коннекта звать initrandom примерно так:
select initrandom(0) from rdb$database.

собственно, я мог бы перекомпилировать, но не уверен, что остальным надо случайный рандом сразу. Хотя, вызовут randseed, если надо повторять последовательность.

Или сам сможешь скомпилировать? В общем, если надо, к вечеру выложу, или пришлю на email.

Chemist
Сообщения: 88
Зарегистрирован: 27 окт 2004, 09:39

Re: почему GetRandom выдает одну и туже последовательность?

Сообщение Chemist » 05 сен 2006, 15:29

MLT-Coder писал(а):я наверно уже тут всех достал, но все же...
Я пока сам пытаюсь разобраться с Random'ом.
Вот этот код UDF на FB2.0 SS - Win2003 выдает на разных коннектах одну у туже последовательность, на FB 2.0 CS - ASPLinux выдает разные последовательности. После перезагрузки сервиса FB2.0 SS - Win2003 может начать работать правильно до какого-то времени. На локальной машине FB1.5 SS - WinXP генерация случайной последовательности идет нормально.

Код: Выделить всё

long EXPORT fn_initRandom(ARG(long *, num))
ARGLIST(long *num)
{
#if defined __WIN32__
  srand((unsigned) *num);
#else
  srandom((unsigned int) *num);
#endif
  return 1;
}

long EXPORT fn_getRandom(ARG(long *, num))
ARGLIST(long *num)
{
#if defined __WIN32__
  return (rand() % *num);
#else
  div_t x;
  x = div(random(), *num);
  return x.rem;
#endif
}
Такое ощущение, что инициализация проходит только один раз либо все обращения к функции в конечном итоге замыкаются на один Seed :roll:, хотя при коннекте к БД сразу выполняется инициализация случайным числом от генератора (шаг можно выбрать) каждому клиенту. Может DLL в SS для всех коннектов загружается одна? Если это так, то тогда все понятно, надо переходит на random-генератор, который использует альтернативный алгоритм. Благо их достаточно.

Код: Выделить всё

EXECUTE PROCEDURE db_initrandom

CREATE PROCEDURE DB_INITRANDOM
AS
  DECLARE VARIABLE valrandom INTEGER;
BEGIN
  valrandom = initrandom(GEN_ID(db_random, 1));
END
Покопал немного, но т.к. БД на SS не используется, то пока вопрос задвинут.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 05 сен 2006, 18:15

Может DLL в SS для всех коннектов загружается одна?
разумеется. ss это один процесс. один процесс может загрузить одну dll только один раз.

Chemist
Сообщения: 88
Зарегистрирован: 27 окт 2004, 09:39

Сообщение Chemist » 05 сен 2006, 18:54

kdv писал(а):
Может DLL в SS для всех коннектов загружается одна?
разумеется. ss это один процесс. один процесс может загрузить одну dll только один раз.
Это понятно, хотя была маленькая надежда, что отдельный поток как-то по другому это делает - не силен в тредах. Тогда почему для одного соединения как сервером так и с локальной машиной поведение разное. На локальной машине генерация случайной последовательности нормальная, на Win2003 постоянно одинаковая :shock:. DLL одна и та же, загадка.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 05 сен 2006, 19:34

фиг знает. может randomize по другому действует, или еще что.
Например, GUID на XP и 2003 другой, нежели под NT.

Chemist
Сообщения: 88
Зарегистрирован: 27 окт 2004, 09:39

Сообщение Chemist » 07 сен 2006, 16:55

Вот этот код дает нормальную псевдослучайную последовательность в случае SS для раных коннектов. Для SS установку seed желательно делать один раз в момент первого вызова, иначе seed просто сбивается, т.к. изначально инициализируется rand'ом (вызов srand), где RND_MAX 0x7FFF, что очень мало и повышает вероятность возвратить seed к начальному или близкому к нему значению.

Код: Выделить всё

long EXPORT fn_initRandom(ARG(long *, num))
ARGLIST(long *num)
{
#if defined __WIN32__
  static long seed = 0;
  if (!seed) 
  {
    seed = *num;
    srand(seed);
  }
#else
  srandom((unsigned int) *num);
#endif
  return 1;
}

long EXPORT fn_getRandom(ARG(long *, num))
ARGLIST(long *num)
{
#if defined __WIN32__
  return random(*num);
#else
  div_t x;
  x = div(random(), *num);
  return x.rem;
#endif
}
PS. Может кому поможет :D

Ответить