miércoles, 15 de octubre de 2008

Numeros aleatorios sin repeticion (una clase)

image006

Supongamos que tenemos que rellenar una lotería primitiva: tenemos que hallar 6 números aleatorios de entre 69 números pero sin que se repitan.
¿Como lo hacemos?, pues nada, aplicando la clase contenida en este truco.
En concreto acabo de hacer esto pues estoy haciendo una página web y necesito mostrar en la misma página 3 artículos aleatorios de una base de datos... y claro, que se me repitan en la misma página web 3 articulos no es muy estético que digamos...
Como en el asunto este se mezclan datos y funciones y procedures, he decidido meterlo todo en una clase para facilitar su reutilización.
He colocado todo en una unit, (llamada uTAlea) para que con sólo añadirla en la clausula uses de nuestra aplicación pueda ser utillizada.
Aqui está el código de la unit uTAlea que contiene la clase TAlea:

 unit uTAlea;
{Radikal, Q3 para Trucomania.}

interface

uses Windows,SysUtils;

type
{Array de booleanos de longitud variable}
TArrayBool = array[0..0] of boolean;
PArrayBool = ^TArrayBool;

{Clase para manejar numeros aleatorios}
TAlea = class
Maximo : integer; //Valor maximo de los numeros obtenidos
Lista : PArrayBool;
FaltanDeSacar : integer;
constructor Create(Rango:integer);
destructor Destroy; override;
procedure Reset; //Resetea la lista de numeros
function PillaNumero:integer; //Devuelve un numero aletorio sin repeticion
end;



implementation


constructor TAlea.Create(Rango:integer);
begin
{
Si los números a extraer no pueden ser repetidos
hemos de crear un array para almacenar cual ha salido y cual no
Como el array ha de ser variable, usamos el GetMem y
los punteros para que sirva tambien para versiones
anteriores a Delphi 4, que no incorporan los arrays
de longitud variable...
}

inherited Create;
Maximo:=Rango;
FaltanDeSacar:=Rango;
{Reservamos memoria para el array de longitud variable}
GetMem(Lista, 1+Maximo * SizeOf(Boolean));
ZeroMemory(Lista,1+Maximo * SizeOf(Boolean));
end;

destructor TAlea.Destroy;
begin
if Assigned(Lista) then FreeMem(Lista, 1+Maximo * SizeOf(Boolean));
inherited Destroy;
end;

function TAlea.PillaNumero:integer;
var
Numero: integer;
begin
if FaltanDeSacar=0 then raise exception.create( 'Error. No se pude sacar otro numero sin repetir'+#13+#10+
'Error. No more numbers are available');

{Buscamos un número que no haya salido ya}
repeat Numero:=Random(Maximo) until NOT Lista^[Numero];
{Lo apuntamos en la lista de numeros ya usados}
Lista^[Numero]:=TRUE;
{Decrementamos la cantidad de numeros que faltan por salir}
Dec(FaltanDeSacar);
Result:=Numero;
end;

procedure TAlea.Reset;
begin
ZeroMemory(Lista,1+Maximo * SizeOf(Boolean));
FaltanDeSacar:=Maximo;
end;

end.

Ya sabes, la grabas como uTAlea.PAS para poder usarla en tus proyectos.
Vamos con una aplicación de ejemplo para probar la clase:

  • Crea una aplicacion nueva, con una form que contenga un TMemo (Memo1) y un TButton (Button1)

  • Copia el uTAlea.PAS en el directorio de esa aplicacion

  • Añade TAlea en el uses de tu form

  • Mete el siguiente código en el OnCLick del Button1:
     procedure TForm1.Button1Click(Sender: TObject);
    var
    Ristra : TAlea;
    n : integer;
    begin
    Memo1.Lines.Clear;

    Ristra:=TAlea.Create(10);

    {Sacamos 10 numero sin repetir}
    for n:=1 to 10 do begin
    memo1.Lines.Add( IntToStr(Ristra.PillaNumero) );
    end;

    Memo1.lines.Add('Otros 5 numeros de entre 10...');

    Ristra.Reset;
    for n:=1 to 5 do begin
    memo1.Lines.Add( IntToStr(Ristra.PillaNumero) );
    end;

    Ristra.Free;
    end;
  • 1 comentario:

    Unknown dijo...

    Muy buena aportacion, me sirvio excelente para un aaplicacion que tengo que hacer.