Ver Mensaje Individual
  #19  
Antiguo 18-06-2008
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Reputación: 25
Delphius Va camino a la fama
Yo también considero de que el uso de TStrinList es una opción recomendable. Cuenta con los métodos adecuados para resolverte el problema de buscar entre los repetidos. Ni que decir de que con emplear el TStringList te evitas tener que estar buscando contra la base de datos y luego guardando.

Por otro lado, si es demasiado preocupante el tema de los repetidos y te resulta un tanto "molesto" el emplear un TStringList, te recomendaría que emplearas algún generador de números pseudoaletorios que te grantize de que no habrá ningún repetido en una serie de m elementos (ese m es número bastante enorme por cierto).

Existen. Si que existen. Los generadores que cumplen con estas condiciones son los generadores congrenciales mixtos.

Para que exista esta característica en dichos generadores se debe cumplir estos requisitos:
1. b es primo con m
2. a-1 es múltiplo de p para todo primo p que divida a m
3. a-1 es múltiplo de 4 siempre que m sea múltiplo de 4

Para comprender mejor el tema te hago llegar este documento.
Mis apuntes de Modelos y Simulacones están el armario, perdona que no pueda explayarme demasiado.

Internamente, la función Random es un generador congruencial (o multiplicativo) mixto. Y no me extraña que sus valores a,b y m sean los adecuados según dicho axiomas.
Aunque no está demás hacer algunas comprobaciones. Y no es de extrañar que ante un período m corto como es el de 1503 existan colisiones.

Si buscas sobre el tema en los foros llegarás a hilos en donde he expuesto el tema. De hecho, ha quedado disponible aqui en los foros mis códigos que pueden ser estudiados y adaptados según tus necesidades.

Si necesitas un riguroso control sobre los repetidos puede que debas consideras que la inversión del esfuerzo sobre el de emplear un generador propio (tal vez) te es más viable que emplear un TStringList y el simple Random.

En fin, es una alternativa más que te ofrezco.

Por otro lado, me parece que es más efectivo emplear un repeat-until que un while para iterar hasta encontrar un número que no esté repetido.

El algoritmo debe venir así:

Si empleamos el StrinList
1. Desde 1 hasta CANTIDAD_DE_SORTEOS hacer:
1.1. repetir
1.1.1 Numero = Random(....)
1.1* hasta Numero no esté en lista (si empleamos el uso de un TStringList)
1.2. Añadir a lista
2. Guardar el contenido de lista en DB

Si se opta por emplear el uso de un generador que nos garantize que en el período no exista un repetido entonces lo hacemos más simple así:
1. Desde 1 hasta CANTIDAD_DE_SORTEOS hacer:
1.1. Numero = Random(....)
1.2. Guardar en DB

En fin. Cada cosa que te ofrece sus ventajas y desventajas.

Ahora pienso que, se puede emplear Random() y no random(1503) por ejemplo. Creo (no aseguro) que el uso de Random (sin indicar rango) fuerza al generador a trabajar con el valor máximo de m. Si es así, y si se cumplen estos axiomas mencionados podría bastar con obtener la parte decimal de dicho número y obtener el resto de la divsión respecto a 1503 (tal vez 1504).

Es decir que si obtenemos 0,48971 realizamos estos pasos:
1. Nro = 48971
2. Nro = Nro mod 1503 = 875

¿Porque el mod? Porque de este modo garantizamos que el máximo valor a obtenemos sea menor a 1503.

Ummm.... ¿Y no es eso volver a amplicar un generador sobre otro? Pues de hecho si... volvemos a lo mismo. Ya que de por sí, el generador realizar el mod de un número astronómicamente grande respecto a un valor m (también astronómicamente grande).

En definitiva.... es volvemos a lo mismo. Acotar un rango amplio a uno más bajo. De cualquier manera la distribución de probabilidad que siguen es, un principio, uniforme: 1/m.

Analicemos el tema numéricamente,

Si m es 1503, obtendremos = 0.000665335

Un valor bajo, al menos en un principio. Analizarlo al extremo en el contexto de los 300 Pues.... 300/1503 = 0.1996007984

¿Porqué hice ese 300/1503? Pues para demostrar la probabilidad de que un número se repita 300 veces dentro de los 1503.

Si nos basamos en que el rango de 1503 es corto... yo diría que las probabilidades no son alentadoras. Trabajar con dicho rango es posiblemente, inviable.

Apliqué este algoritmo:

Código Delphi [-]
resourcestring
  msgSorteado = 'Nro ganador: %d, sorteo nº: %d';

procedure TForm1.RealizarSorteo(Sorteos: TStrings);
const
  CANTIDAD_SORTEOS = 300;
var
    i, Nro: integer;
begin
  Sorteos.Clear;

  for i := 1 to CANTIDAD_SORTEOS do
    begin
      Nro := Random(1503) + 1;
      Sorteos.Add(Format(msgSorteado,[Nro,i]))
    end;
end;


En un TListBox con la propiedad Sorted en true.

He hecho una prueba con D6 y obtenido repetidos, en distintas iteraciones. De hecho varios números se repiten dos veces.

La prueba no tiene sentido si uno aplica un Randomize obviamente.

La misma prueba, aplicando este algoritmo:

Código Delphi [-]
procedure TForm1.RealizarSorteo(Sorteos: TStrings);
const
  CANTIDAD_SORTEOS = 300;
var
    i, Nro: integer;
begin
  Sorteos.Clear;
  for i := 1 to CANTIDAD_SORTEOS do
    begin
      Nro := Trunc(Random * 100000000000) mod 1503;
      Sorteos.Add(Format(msgSorteado,[Nro,i]))
    end;
end;

Si bien no obtengo toda la parte decimal convertida en entero, para un experimento puede servir.

Y vuelvo a obtener repetidos...
¿Que pasa aquí?
Pues simplemente que estamos acotando el rango grande a uno pequeño.

Realizando la prueba con un Random solo, y un format a 0.8f. No he conseguido ningún repetido.

He aquí la eterna pregunta ¿Nos conformamos con el uso de una "lista" para buscar repetidos e iteramos mientras haya repetidos o nos esforzamos a buscar aquellas propiedades a,b que garantizen junto a m que en la serie no nos vamos a topar con repetidos y empleamos nuestro propio generador?

Puedes probar bibliotecas científicas para ver si alguna ofrece una mejor alternativa.

Y antes yo sacaba valores respecto a 1503.... en realidad amigo, deberíamos hacerlo en 300. Que son en realidad esos 300 números que saldrán. De hecho, si sabemos que en realidad sorteamos 300 numéros, tenemos 1/300 de salir ¿No es cierto? 1/300 no da un valor de 0,003333..

Aun así, necesitamos generar números en el rango buscado y por tanto nuestro "m" son y serán esos 1503. Si tomamos todos, o sólo los primeros 300 ya es otra cosa.

Yo me pregunto, si al acotar el rango a 1503 obtenemos muchos repetidos... que podría esperararse si lo hacemos en 300. (No es necesario responderla, no viene al caso... es sólo una reflexión mia).

Yo hice esos cálculos para mentalizar el porque y cuando el uso de un Random es útil.

Se que no aporto mucho, pero al menos creo que con esto doy un panorama del uso de random.

Recuerden que las implementaciones de Random pueden variar de una versión a otra del compilador.

Saludos
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita