PDA

Ver la Versión Completa : Orden aleatorio con Rand() Ineficaz


DarkDudae
13-04-2008, 12:38:34
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():

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
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:/*****************************************
*
* 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:

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!