Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   Orden aleatorio con Rand() Ineficaz (https://www.clubdelphi.com/foros/showthread.php?t=55301)

DarkDudae 13-04-2008 12:38:34

Orden aleatorio con Rand() Ineficaz
 
Hola a todos,

estoy intentando sacar un listado aleatorio de filas con un SELECT en firebird.

He estado haciendo pruebas con la función Rand():

Código SQL [-]
SELECT CAMPO1, CAMPO2, CAMPO3 FROM MITABLA
ORDER BY RAND()


Aparentemente, funciona, pero nada más lejos de la realidad. Cada vez que ejecutas el query sin cerrar la aplicación, parece que la función RAND hace su cometido, y ordena las filas aleatoriamente. Pero es falso. Si cierras la aplicación, y la vuelves a abrir, se obtiene exactamente la misma ordenación.

Ejemplo: Ejecuto mi aplicación y uso el SELECT anterior y me da este orden:

3-4-2-1

Si vuelvo a ejecutar el Query nuevamente me da un orden diferente:

1-3-4-2


Y así sucesivamente...

Si cierro la aplicación y vuelvo a ejecutar el SELECT, obtendré nuevamente la misma ordenación: 3-4-2-1 , luego 1-3-4-2 ...

:confused:

Si podeis arrojar algo de luz a este asunto, os estaría muy agradecido.

Saludos

xEsk 13-04-2008 15:10:51

Eso es porque debes inicializar el rand().

Es como en Delphi, antes de usar el random, debes llamar al "randomize()".

El problema, es que ahora mismo no se si existe alguna función en Firebird que haga esto, es cuestión de buscarlo por internet.

Saludos.

DarkDudae 13-04-2008 17:04:10

Cita:

Empezado por xEsk (Mensaje 279658)
Eso es porque debes inicializar el rand().

Es como en Delphi, antes de usar el random, debes llamar al "randomize()".

El problema, es que ahora mismo no se si existe alguna función en Firebird que haga esto, es cuestión de buscarlo por internet.

Saludos.

Eso fue lo primero que pensé, pero parece ser que no existe nada por el estilo (al menos yo no lo he sabido encontrar)

Como solución provisional, antes de ejecutar el select normal, ejecuto otros selects limitados a dos únicos registros con un único campo en su parámetro de búsqueda. Esta consulta, la ejecuto entre 1 y 100 veces (con la función random propia de delphi). Así al menos me aseguro de que el random maneje 100 posibilidades de inicio distintas.

Es una solución bastante cutre, ya que es ejecutar a veces hasta 100 veces una consulta inútil, pero al menos me sirve para inicializar la consulta cuasi-aleatoriamente.

Si conocéis alguna otra solución mejor...

Saludos

xEsk 13-04-2008 17:56:57

Una solución que se me ocurrió fue reemplazar la función rand() del Firebird por otra creada por mi (importarla en forma de UDF), pero después pensé, que es muy raro que lleve un random y no un randomize, así que mire manualmente en el fichero ib_udf2.sql que lleva el Firebird, y efectivamente, he encontrado el srand().

Te copio su descripción:
Código SQL [-]
/*****************************************
 *
 *  s r a n d
 *
 *****************************************
 *
 * Functional description:
 *  Seeds the random number generator using
 *  the current time and returns the first
 *  pseudo-random number (between 0 and 1)
 *  in the new sequence.
 *
 * Note:
 *  Two srand() calls performed within a second
 *  will return the same value.
 * 
 *****************************************/
DECLARE EXTERNAL FUNCTION srand 
  RETURNS DOUBLE PRECISION BY VALUE
  ENTRY_POINT 'IB_UDF_srand' MODULE_NAME 'ib_udf';

Saludos.

DarkDudae 15-04-2008 09:13:25

Ante todo, muchas gracias. Funciona perfectamente.

Cuando buscaba una solución, también indagué en la documentación del firebird, pero cuando leí que el srand() era la antigua función rand() que ha sido sustituida recientemente por la rand() actual para evitar que la semilla arrojase valores aleatorios iguales cuando se ejecutaban diversas llamadas en el mismo segundo, la dejé en el olvido:

Cita:

UDFs Added and Changed
IB_UDF_rand() vs IB_UDF_srand()
IB_UDF_lower
UDFs added or enhanced in Firebird 2.0's supplied libraries are:
IB_UDF_rand() vs IB_UDF_srand()
F. Schlottmann-Goedde
In previous versions, the external function rand() sets the random number generator's starting point based on the current time and then generates the pseudo-random value.
srand((unsigned) time(NULL));
return ((float) rand() / (float) RAND_MAX);

The problem with this algorithm is that it will return the same value for two calls done within a second.
To work around this issue, rand() was changed in Firebird 2.0 so that the starting point is not set explicitly. This ensures that different values will always be returned.
In order to keep the legacy behaviour available in case somebody needs it, srand() has been introduced. It does exactly the same as the old rand() did.
No obstante, para mis necesidades, las limitaciones del antiguo rand() no son tales. En fin, cada función tiene sus pros y sus contras.

Un saludo!


La franja horaria es GMT +2. Ahora son las 19:05:56.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi