Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Lazarus, FreePascal, Kylix, etc. (https://www.clubdelphi.com/foros/forumdisplay.php?f=14)
-   -   Revisión de código (https://www.clubdelphi.com/foros/showthread.php?t=93072)

danielmj 09-04-2018 20:23:07

Revisión de código
 
Hola, tengo el siguiente código:

Código Delphi [-]
for i:= 0 to lista3.Items.Count -1 do
    for j:= 0 to per.Items.Count -1 do
    begin
      stB.Panels.Items[2].Text:= '[C. '+IntToStr(i+1)+'],'+'[P. '+IntToStr(j+1)+']';
      barra.Max:= lista3.Items.Count;
      barra.Position:= i+1;
      barra2.Max:= per.Items.Count;
      barra2.Position:= j;
      lista3.Selected:= lista3.Items[i];
      stB.Panels.Items[4].Text:= FormatFloat('0.00',(i*100)/lista3.items.Count)+'%';

      cad1:= lista3.Items.Item[i].SubItems[0] +
        ' '+ lista3.Items.Item[i].SubItems[1]+' '+lista3.Items.Item[i].SubItems[2]+
        ' '+ lista3.Items.Item[i].SubItems[3] +' '+lista3.Items.Item[i].SubItems[4];

      cad2:= per.Items.Strings[j];

      if cad2 = cad1 then

        with listaResumen2.Items.Add do
          begin
            inc(cinco);
            caption:= lista3.Selected.Caption;
            subitems[0]:= lista3.Items.Item[i].SubItems[0];
            subitems[1]:= lista3.Items.Item[i].SubItems[1];
            subitems[2]:= lista3.Items.Item[i].SubItems[2];
            subitems[3]:= lista3.Items.Item[i].SubItems[3];
            subitems[4]:= lista3.Items.Item[i].SubItems[4];
            subitems[5]:= lista3.Items.Item[i].SubItems[5];
            subitems[5]:= IntToStr(5);
            subitems[6]:= IntToStr(cinco);
          end;
    end;

Desde mi punto de vista, si se da la condición y cad2 = cad1, en la lista 3 se almacernará x valores. El problema es que es un proceso muy largo y que puede que no siempre se de esa condicion por lo que no puedo saber si realmente hará lo que debe. Por eso, si no es mucho pedir ¿según vosotros ese código es correcto?

Un saludo y gracias.

duilioisola 10-04-2018 08:42:58

  • Agregué comentarios
  • Las barras de estado solo necesitan inicializarse una vez, ya que al principio ya sabes los datos.
  • Moví el cálculo de cad1 fuera del bucle que recorre per, ya que siempre calcula lo mismo (depende del indice del bucle i)
  • Moví el refresco de información de barra fuera del bucle que recorre per ya que solo depende del i.
  • Agregue algún begin..end porque me gusta mas como se ve el código.

Código Delphi [-]
// Inicializo barras de estado
barra.Max := lista3.Items.Count;
barra2.Max := per.Items.Count;

// Recorro lista3
for i := 0 to lista3.Items.Count -1 do
begin
  // Calculo CAD1 cada vez que cambia el indice de lista3
  with lista3 do
  begin
    Selected := Items[i];
    cad1 := Items.Item[i].SubItems[0] +
    ' '+ Items.Item[i].SubItems[1] +' '+ Items.Item[i].SubItems[2]+
    ' '+ Items.Item[i].SubItems[3] +' '+ Items.Item[i].SubItems[4];
  end;

  // Actualizo panel de informacion relacionada con recorrido de lista3
  barra.Position := i + 1;
  stB.Panels.Items[4].Text := FormatFloat('0.00',(i * 100) / lista3.items.Count)+'%';

    // Recorro per
  for j := 0 to per.Items.Count -1 do
  begin
    // Actualizo panel de informacion general
    stB.Panels.Items[2].Text := '[C. '+IntToStr(i+1)+'],'+'[P. '+IntToStr(j+1)+']';

    // Actualizo panel de informacion relacionada con recorrido de per
    barra2.Position := j;

    // Calculo CAD2 cada vez que cambia el indice de per
    cad2 := per.Items.Strings[j];

    // Si lista3 y per son "iguales" agrego un registro a listaResumen2
    if cad2 = cad1 then
    begin
      with listaResumen2.Items.Add do
      begin
        inc(cinco);
        caption:= lista3.Selected.Caption;
        subitems[0]:= lista3.Items.Item[i].SubItems[0];
        subitems[1]:= lista3.Items.Item[i].SubItems[1];
        subitems[2]:= lista3.Items.Item[i].SubItems[2];
        subitems[3]:= lista3.Items.Item[i].SubItems[3];
        subitems[4]:= lista3.Items.Item[i].SubItems[4];
        subitems[5]:= lista3.Items.Item[i].SubItems[5];
        subitems[5]:= IntToStr(5);
        subitems[6]:= IntToStr(cinco);
      end;
    end;
  end;
end;

danielmj 10-04-2018 09:02:00

Gracias duilioisola, voy a probar.

danielmj 12-04-2018 12:19:48

Hola, he probado estos dias el código y si bien "funciona" realmente no va a encontrar nunca nada en la lista 3. Me explico, supongamos que el día x del mes y del año z apareció la siguiente combinación: 23, 26, 28, 37, 40, 47 y ahora supongamos que el programa genera una combinación de otros seis numeros de la cual se hace las permutaciones de 5 de esos números. Pues bien, aunque entre la combinacion ganadora del dia x tenga 5 coincidencias con la combinación generada por el programa, puede ser que al generar las permutaciones en "per": 23,32,28,37,40 nunca la encuentre pues el segundo numero de la combinacion y el segundo de per son diferentes o el tercero de la combinación y el segundo de per... trato de buscar la lógica y no la veo. Ejemplo real..

Un saludo.

duilioisola 12-04-2018 21:34:36

Es tan simple como tener ordenados los números.
Si es una permutación:
Los valores no se repiten
El orden no importa.

danielmj 12-04-2018 21:47:23

Hola, lo sé. El problema es que el programa no fue capaz de encontrar cinco de los seis números de una combinación que si tuvo en su día cinco aciertos. De ahí mis dudas, la única forma de mostrarlo es con un vídeo del proceso pero es engorroso grabar un vídeo tras otro hasta conseguir uno válido. Un saludo.

danielmj 13-04-2018 10:09:04

Hola, cuando escribí mi último mensaje, estaba en el trabajo y no podía verificarlo, pero pensando pensando caí en la cuenta de que en cad1 solo metía 5 números de la lista y no 6 por eso nunca encontraría cinco aciertos en caso de que uno de ellos fuera diferente. Ya lo solucioné y paso a probar. Un saludo.

danielmj 13-04-2018 12:02:49

Hola de nuevo, he revisado ese error que comentaba antes, y sigue sin encontrar nada, el día 9-05-15 salío una combinación que coincide en 5 números con las generadas por el programa, pero este no la encuentra (y el listado de sorteos es real, se carga de un CSV). ¿Alguna idea?

Combinación de ese dia: 16 27 29 34 38 43

danielmj 13-04-2018 14:00:30

No hay forma, puedo intentarlo hasta el infinito que no va a funcionar y la razon es que si en cad1 tengo 12345 y en cad2 tengo 12346 jamás encontrará 5 coincidencias por que uno de los números es distinto por muy ordenados y por muy permutacion que sea. Me tiene ya frustrado por que no sé como solucionarlo.

Si per muestra las permutaciones a partir de 12345 y en lista3 tengo 12346 las permutaciones nunca coincidiran con lista 3.

danielmj 13-04-2018 14:35:35

He pensado en una posible solución, calcular las permutaciones de cada fila de la lista3

Código Delphi [-]
Combinatoria2([(Items.Item[i].SubItems[0]), Items.Item[i].SubItems[1], Items.Item[i].SubItems[2],
    Items.Item[i].SubItems[3], Items.Item[i].SubItems[4], listBox2);

El problema es que no acepta enteros y cadenas y cuando pongo IntToStr da error.

bucanero 13-04-2018 16:32:48

hola danielmj,

Una opción es representar la combinación con un entero de 32 bits si los elementos a combinar son menos de 32 o de 64 bits si son mas de 32 elementos y menos de 64 elementos, (para mas elementos tendrías que crear array de enteros), de tal forma que cada posición de un bit representa ese numero de elemento.

por ejemplo: si tienes una combinación de 3 elementos de una lista del 1 al 8
combinación 1: elementos 1, 5, 6
combinación 2: elementos 2, 5, 6

Código:

bit|7|6|5|4|3|2|1|0|        ->        DEC
C1=|0|0|1|1|0|0|0|1|        ->        49
C2=|0|0|1|1|0|0|1|0|        ->        50

IMPORTANTE: como el indice de los bits se inicia en 0 y los elementos en 1 hay que ajustar el indice restando una unidad

Al ejecutar el siguiente código ya puedes saber si hay coincidencias o no
Código Delphi [-]
var
  c1, c2: Integer;
begin
  ...
  if (c1 = c2) then begin
   // las dos combinaciones son iguales
  end
  else if (c1 and c2) <> 0 then begin
   // coinciden algunos elementos y en particular los elementos que coinciden
   // son aquellos cuya posicion de bit esta puesta a 1
  end
  else begin
    // las dos combinaciones son totalmente distintas
  end;
  ...
end;

Para obtener el entero de la combinación a partir de una lista con los elementos selecionados puede hacer los siguiente
Código Delphi [-]
function ValorCombinacion(list:TStrings):Int64;
var
  elemento:Byte;
  i: Integer;
begin
  result := 0;
  with list do
    for i := 0 to Count - 1 do begin
      //se obtiene el valor numerico de la lista
      elemento := StrToInt(strings[i])-1;
      //se inserta en el resultado
      result := result or (Int64(1) shl elemento);
    end;
end;

Espero que esto te pueda ayudar
Un saludo

TOPX 13-04-2018 16:53:19

Hola,

Puede ser que le esté entendiendo mal, pero sospecho que le puede servir el típico "set intersection". A saber:
Código Delphi [-]
var
  a, b, c: set of char;
  x: char;
begin
  a := ['A', 'B', 'C', 'D'];
  b := ['C', 'D', 'E', 'F'];
  c := a*b; // "c" es la intersección de "a" y "b", c = ['C', 'D']

  Memo1.Lines.Clear;
  for x in c do
  begin
    Memo1.Lines.Add(x);
  end;
end;
Fuente: Intersection of two strings/sets - Stack Overflow
-

danielmj 13-04-2018 16:54:32

Hola, mi código es este:

Código Delphi [-]
for i := 0 to lista3.Items.Count -1 do
begin
    // Calculo CAD1 cada vez que cambia el indice de lista3
  with lista3 do
    begin
        Selected := Items[i];
    fila.Caption:= 'Fila: '+IntToStr(i)+' /'+items.item[i].caption;
    n.Caption:= items.Item[i].SubItems[0] + ' '+ items.Item[i].SubItems[1]+
    ' '+ items.Item[i].SubItems[2] + ' '+ items.Item[i].SubItems[3]+
    ' '+items.Item[i].SubItems[4];

    {Items.Item[i].SubItems[0] +
        ' '+ Items.Item[i].SubItems[1] +' '+ Items.Item[i].SubItems[2]+
        ' '+ Items.Item[i].SubItems[3] +' '+ Items.Item[i].SubItems[4];{+
    ' '+ Items.Item[i].SubItems[5];}
    end;

    // Recorro per
    for j := 0 to per.Items.Count -1 do
    begin
        // Actualizo panel de informacion
        stB.Panels.Items[2].Text:= '[C. '+IntToStr(i+1)+'],'+'[P. '+IntToStr(j+1)+']';
        barra.Max:= lista3.Items.Count;
        barra.Position:= i+1;
        barra2.Max:= per.Items.Count;
        barra2.Position:= j;
        stB.Panels.Items[4].Text:= FormatFloat('0.00',(i * 100) / lista3.items.Count)+'%';

        // Calculo CAD2 cada vez que cambia el indice de per
        cad2 := per.Items.Strings[j];

        // Si lista3 y per son "iguales" agrego un registro a listaResumen2
    // Si alguno de los 5 numeros de lista 3 es distinto a alguno de los
    //numeros de per, YA NO VA A FUNCIONAR.

      if cad2 = n.Caption then
        begin
            with listaResumen2.Items.Add do
            begin
                inc(cinco);
                caption:= lista3.Selected.Caption;
                subitems[0]:= lista3.Items.Item[i].SubItems[0];
                subitems[1]:= lista3.Items.Item[i].SubItems[1];
                subitems[2]:= lista3.Items.Item[i].SubItems[2];
                subitems[3]:= lista3.Items.Item[i].SubItems[3];
                subitems[4]:= lista3.Items.Item[i].SubItems[4];
                subitems[5]:= lista3.Items.Item[i].SubItems[5];
                subitems[5]:= IntToStr(5);
                subitems[6]:= IntToStr(cinco);
            end;
        end;
    end;

Comparo cada item de per (cad2) con la etiqueta "n" ¿no debería funciionar? loo pregunto por que no lo hace.
Voy a probar tu código y te comento. Gracias.

TOPX 13-04-2018 17:12:15

Cita:

Empezado por danielmj (Mensaje 525661)
Voy a probar tu código y te comento. Gracias.

¿El código de bucanero ó el código de TOPX? :rolleyes:
-

bucanero 13-04-2018 17:58:28

Cita:

Empezado por danielmj (Mensaje 525661)
Comparo cada item de per (cad2) con la etiqueta "n" ¿no debería funciionar? loo pregunto por que no lo hace.
Voy a probar tu código y te comento. Gracias.

El problema que veo en tu código es que las variables que estas comparando son de tipo string, y en cuanto no sean totalmente idénticas ya no te va a devolver ninguna coincidencia.
Tomando el ejemplo mio anterior y aplicado a tu técnica obtendrías:

Código Delphi [-]
  // combinación 1: elementos 1, 5, 6  
  cad1 := '1 5 6';
  // combinación 2: elementos 2, 5, 6 
  n.caption := '2 5 6';

  if cad2 = n.Caption then begin
    //solo ocurrira cuando las dos combinaciones sean identicas
    ... 
  end;

Por eso salvo que sea la misma combinación no obtienes resultados. Debes hacer otro tipo de comparación que no sea de tipo string.

Lo interesante del método que te puse arriba es que al hacer el AND de dos combinaciones representadas con dos integer de tipo INT64, solamente se tarda un único ciclo de CPU para saber si la combinación tiene o no tiene alguna coincidencia, y dado que la mayoría de las veces no vas a tener coincidencias, es un proceso muy rápido.

Y para procesos con tantísimas combinaciones a comparar, cualquier ganancia en calculo se agradece, pues se nota en el tiempo final.

danielmj 13-04-2018 18:39:19

Hola TOPX creo que no es lo que busco, o no entendí bien tu propuesta, lo que trato de hacer (básicamente) es comparar cada fila de un listview de 5 elementos con cada item de un listbox que almacena todas las permutaciones (120).

Bucanero, miro tu versión, pero hay cosas que no veo, por ejemplo, declaro tu funcion, pero no la implementación a la hora de comparar las permutaciones con cada subitem del listview. Se me escapa.

Un saludo.

bucanero 13-04-2018 19:04:02

aplicandolo a tu código deberías de tener algo así:

Código Delphi [-]

function ValorCombinacion(list:TStrings):Int64;
var
  elemento:Byte;
  i: Integer;
begin
  result := 0;
  with list do
    for i := 0 to Count - 1 do begin
      //se obtiene el valor numerico de la lista
      elemento := StrToInt(strings[i])-1;
      //se inserta en el resultado
      result := result or (Int64(1) shl elemento);
    end;
end;

function obtenerComunes(res:int64):string;
var
  i:integer;
begin
  result:='';
  for i :=0 to 49 do
    if ((res and (int64(1) shl i))<>0) then 
      result:=result+IntToStr(i+1)+' ';
end;

...

var
  res, cad1, cad2:Int64;
  comunes:String;

...

for i := 0 to lista3.Items.Count -1 do
begin
    // Calculo CAD1 cada vez que cambia el indice de lista3
  with lista3 do
    begin
        Selected := Items[i];
        cad1:= ValorCombinacion(lista3.items[i].SubItems);
    end;

    // Recorro per
    for j := 0 to per.Items.Count -1 do
    begin
        ....

        // Calculo CAD2 cada vez que cambia el indice de per
        cad2 := ValorCombinacion(per.Items[j].SubItems);

        res:=cad1 and cad2;
        if (cad1 = cad2) then begin
            // las dos combinaciones son iguales
        end
        else if (res <> 0) then begin
           // coinciden algunos elementos y en particular los elementos que coinciden
           // son aquellos cuya posición de bit esta puesta a 1
           //para saber los elementos que son comunes recorres bit a bit la variable RES 
           obtenerComunes(res);
        end
        else begin
          // las dos combinaciones son totalmente distintas
        end;


    end;


Un saludo

danielmj 13-04-2018 19:17:31

Hola Bucanero, estoy con tu código pero en
Código Delphi [-]
cad2 := ValorCombinacion(per.Items[j].SubItems);
da error, no reconoce "subitems". <<Per>> es un listBox, quizás sea por eso., así que por ahora no puedo ver que tal funciona.
Un saludo y gracias.

bucanero 13-04-2018 19:24:51

debes de adaptar el parámetro LIST de la funcion ValorCombinacion para el tipo de componente que estes utilizando en tu caso particular, pensé que eran TLISTVIEW. Si en un TLISTBOX entonces puedes poner:

Código Delphi [-]
cad2 := ValorCombinacion(per.items);

danielmj 13-04-2018 19:32:48

Hola, no va Bucanero, da error... básicamente que el valor de per, no es un valor entero valido.
Un saludo.

danielmj 13-04-2018 20:07:44

No, no funciona, no acepta el valor de cad2 como entero.

bucanero 13-04-2018 21:53:03

¿Que contenido tienen los elementos de per.items? si como muestras en la imagen el contenido es "4 20 21 23 25" entonces debes convertir antes esa cadena a una lista de valores valida para la función ValorCombinacion() donde cada elemento contenga un único numero. O ajustar los parámetros de entrada a los que tu necesites.

prueba con

Código Delphi [-]

 var
    list:TStringList;

...
begin
    //inicio del proceso
    // poner esto al principio del proceso fuera de todos los bucles     
    list:=TStringList.create;
    list.delimiter:=' ';

 ...

     // Calculo CAD2 cada vez que cambia el indice de per
    list.delimitedText:=per.Items[j].SubItems;
    cad2 := ValorCombinacion(list);

...

     // al final del proceso 
     list.free; //liberar el objeto
 end;

danielmj 13-04-2018 23:46:42

Gracias, no he contestado antes por que estoy en trabajo. Llegaré mañana por la mañana a casa y ya pruebo. Un saludo.

danielmj 14-04-2018 03:51:31

Hola, el código ya no da error, pero sigue sin encontrar coincidencias habiendolas (en concreto con el sorteo del dia L-14-01-13). No hay forma de hacerlo funcionar joer.
Saludos.

Edito: He hecho una prueba, he creado una etiqueta y en ella almaceno el valor de cad1 así..
Código:

label21.caption:= IntToStr(cad1)
y lo que devuelve es un entero largo que no tiene nada que ver con la combinacion de lista3. Por eso no encuentra nada.

mamcx 14-04-2018 04:05:06

En vez de mostrar codigo debes mostrar datos.

Pon en una tabla y/o lista los datos iniciales, y otra como deben quedar.

danielmj 14-04-2018 04:27:11

Muestra en tiempo real. Video
Hola Mamcx, no he entendido bien lo que dices. Te explico el proceso a groso modo.. en un listview (lista3) cargo los sorteos hasta la fecha (7 columnas, la primera que sería el caption muestra la fecha del sorteo y se desprecia. El resto muestra la combinacion). En un listbox (per) tengo la permutacion de 5 numeros aleatorios (a partir de los 6 que mas se repiten en un listview (lista) y a partir de ahí debe encontrar 5 coincidencias entre las permutaciones del listbox (per) y cinco de los seis números de la lista3. Pero no encuentra nada y ya he probado varias formas.

Un saludo.

danielmj 14-04-2018 09:22:45

Algo que no entiendo es.. en cad1 que hace mencion a la lista3, se guardan 5 o 6 subitems? por que yo solo quiero comparar 5 igual por eso no encuentra nada. Saludos.

danielmj 14-04-2018 10:50:04

Hola, hee creado una imagen general a ver si asi me explico mejor. Saludos.

bucanero 14-04-2018 11:45:17

Hola,

este ejemplo esta completo y funcional, contiene dos listas con combinaciones:
Código Delphi [-]
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type
  { TForm1 }
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    ListBox2: TListBox;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    list:TStringList;
    function GetComb(listBox:TListBox):String;
    procedure buscarComunes(const Cad1:Int64);
    function  extraerComunes(const res:int64):string;
    procedure CompararCombinaciones(const Cad1, cad2:Int64);
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  //inicio del proceso
  // poner esto al principio del proceso fuera de todos los bucles
  list:=TStringList.create;
  list.delimiter:=' ';

  With listbox1, items do begin
    add('10 12 21 33 45');
    add('10 13 23 34 45');
    add('16 33 14 27 10'); //<-- da lo mismo el orden de los elementos
  end;

  With listbox2, items do begin
    add('10 11 20 33 45');
    add('2 20 22 34 44');
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // al final del proceso
  list.free; //liberar el objeto
end;

function ValorCombinacion(list:TStrings):Int64;
var
  elemento:Byte;
  i: Integer;
begin
  result := 0;
  with list do
    for i := 0 to Count - 1 do begin
      //se obtiene el valor numerico de la lista
      elemento := StrToInt(strings[i])-1;
      //se inserta en el resultado
      result := result or (Int64(1) shl elemento);
    end;
end;
function  TForm1.GetComb(listBox:TListBox):String;
begin
  result:=listBox.items[listBox.itemIndex];
end;

function  TForm1.extraerComunes(const res:int64):string;
var
  i:integer;
begin
  result:='';
  for i :=0 to 49 do
    if ((res and (int64(1) shl i))<>0) then
      result:=result+IntToStr(i+1)+' ';
end;
procedure TForm1.CompararCombinaciones(const Cad1, cad2:Int64);
var
  res:Int64;
  comunes:string;
begin
  res:=cad1 and cad2;
  if (cad1 = cad2) then begin
    // las dos combinaciones son iguales
  end
  else if (res <> 0) then begin
    // coinciden algunos elementos y en particular los elementos que coinciden
    // son aquellos cuya posición de bit esta puesta a 1
    //para saber los elementos que son comunes recorres bit a bit la variable RES
    comunes:=extraerComunes(res);
    // mostrar resultados
    Memo1.lines.add(GetComb(listbox1)+#9+GetComb(listbox2)+#9+'->'+#9+comunes);
  end
  else begin
    // las dos combinaciones son totalmente distintas
  end;
end;
procedure TForm1.buscarComunes(const Cad1:Int64);
var
  cad2:Int64;
  j:integer;
begin
  // Recorro la lista2
   with listbox2 do
   for j := 0 to Count -1 do
   begin
     ItemIndex := j;
     //convierto el string separado por espacios a una lista
     list.delimitedText:=Items[j];
     // Calculo CAD2 cada vez que cambia el indice de per
     cad2 := ValorCombinacion(list);
     CompararCombinaciones(cad1, cad2);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  cad1:Int64;
  i:integer;
begin
  //Recorro la lista1
  with ListBox1 do
    for i := 0 to Count -1 do
    begin
      // Calculo CAD1 cada vez que cambia el indice de lista3
      ItemIndex := i;
      //convierto el string separado por espacios a una lista
      list.delimitedText:=Items[i];
      cad1:= ValorCombinacion(list);
      buscarComunes(cad1);
    end;
end;

end.

mamcx 14-04-2018 17:58:31

Cita:

Empezado por danielmj (Mensaje 525680)
Muestra en tiempo real. Video

Ni el video ni la imagen es clara. Lo que necesitas es un ejemplo minimo de los datos (la imagen habla de los numeros mas repetidos pero eso no se ve).

Me huele que el problema es facil pero esta oscurecido por el codigo que tienes y que no esta simplificado a un minimo para poder entenderlo.

Reduce el problema, pon una lista de unas 10 o 20 filas con ejemplos representativos. Luego un ejemplo concreto que COINCIDA con los datos de ejemplo de como es la cosa.

danielmj 15-04-2018 13:36:25

Hola bucanero,
He estado mirando tu código e implementandolo para mi caso, en compilación no da error, pero como las barras de progreso no se mueven ni un par de etiquetas que muestran el recorrido de lista3 y el listbox per, no sé si realmente funciona o no.

El código resumido seria este:

Código Delphi [-]
unit primi;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ImgList, Menus, StdCtrls, ComCtrls, Gauges, uTAlea, ListViewExt,
  ToolWin, Buttons, VirtualListData, System.ImageList, Vcl.Imaging.pngimage;

type
  ...
  private
    { Private declarations }
    list:TStringList;
    function GetComb(listBox:TListBox):String;
    function extraerComunes(const res:int64):string;
    procedure CompararCombinaciones(const Cad1, cad2:Int64);
    procedure buscarComunes(const Cad1:Int64);
    FVirtualData:  TVirtualData;
  public
    { Public declarations }
  end;

TModa = record
     Number : Integer;
     Count : Integer;
  end;

  ...

const
  ...

var
  ...

implementation

{$R *.dfm}
{$M  16384,1999999}

function ValorCombinacion(list:TStrings):Int64;
var
  elemento:Byte;
  i: Integer;
begin
  result := 0;
  with list do
    for i := 0 to Count - 1 do
    begin
      //se obtiene el valor numerico de la lista
      elemento := StrToInt(strings[i])-1;
      //se inserta en el resultado
      result := result or (Int64(1) shl elemento);
    end;
end;

function  TForm1.GetComb(listBox:TListBox):String;
begin
  result:=form1.per.items[form1.per.itemIndex];
end;


function obtenerComunes(res:int64):string;
var
  i:integer;
begin
  result:='';
  for i :=0 to 49 do
    if ((res and (int64(1) shl i))<>0) then
      result:=result+IntToStr(i+1)+' ';
end;

//... creacion y ejecucion de hilos de procesamiento

function  TForm1.extraerComunes(const res:int64):string;
var
  i:integer;
begin
  result:='';
  for i :=0 to 49 do
    if ((res and (int64(1) shl i))<>0) then
      result:=result+IntToStr(i+1)+' ';
end;

procedure TForm1.CompararCombinaciones(const Cad1, cad2:Int64);
var
  res:Int64;
  comunes:string;
begin
  res:=cad1 and cad2;
  if (cad1 = cad2) then begin
    // las dos combinaciones son iguales
    ShowMessage('Iguales');
  end
  else
    showMessage('No hay coincidencias');
    form1.timer1.Enabled:= false;
    form1.Refresh;
    exit
end;


procedure TForm1.buscarComunes(const Cad1:Int64);
var
  cad2:Int64;
  j:integer;
  list:TStringList;

begin
   // Recorro la lista2
   for j := 0 to form1.per.Items.Count -1 do
   begin
     label32.Caption:= IntToStr(j+1);
     form1.barra2.Max:= form1.per.Items.Count;
     form1.barra2.Position:= j;
     form1.label22.Caption:= IntToStr(cad2);
     form1.per.ItemIndex := j;
     //convierto el string separado por espacios a una lista
     list.delimitedText:=form1.per.items[j];
     // Calculo CAD2 cada vez que cambia el indice de per
     cad2 := ValorCombinacion(list);
     CompararCombinaciones(cad1, cad2);
     //form1.button10.Click
   end;
end;

//cargo en lista3 el archivo de sorteos
procedure LoadFromCSV(LV: TListView; const aFileName: string);
var
  LI : TListItem;
  TS1, TS2: TStrings;
  i, j: Integer;
begin
  TS1 := TStringList.Create;
  TS2 := TStringList.Create;
  try
    TS1.LoadFromFile(aFileName);
    // Columnas
    TS2.CommaText := TS1[0];
    for i := 0 to TS2.Count-1 do
    begin
      LV.Columns.Add;
      LV.Columns.Items[i].Caption := TS2[i];
      LV.Columns[i].Width:= 100; // esta línea es opcional
    end;
    // Items, Subitems
    for i := 1 to TS1.Count-1 do
    begin
      TS2.Clear;
      TS2.CommaText := TS1[i];
      LI := LV.Items.Add;
      LI.Caption := TS2[0];
      for j:= 1 to TS2.Count - 1 do
        LI.SubItems.Add(TS2[j]);
    end;
  finally
    TS1.Free;
    TS2.Free;
  end;
end;

//Crea las permutaciones y las agrega al listbox "per" a partir de un listado general
procedure combinatoria2(v: array of Integer; Serie: TStrings);
var
  a,b,c,d,e: Integer;
begin
  if Length(v) <> 5 then
    raise Exception.Create('Error: Deben ser 5 elementos');
  for a := Low(v) to High(v) do
    for b := Low(v) to High(v) do
      for c := Low(v) to High(v) do
        for d := Low(v) to High(v) do
         for e := Low(v) to High(v) do
           if not(
  (v[a]=v[b])or(v[a]=v[c])or(v[a]=v[d])or(v[a]=v[e])or
  (v[b]=v[c])or(v[b]=v[d])or(v[b]=v[e])or
  (v[c]=v[d])or(v[c]=v[e])or
  (v[d]=v[e])) then
  Serie.Add(Format('%d %d %d %d %d',[v[a], v[b], v[c], v[d], v[e]]));
end;

//Carga en lista3 el contenido del CSV
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  if Open.Execute then
       begin
            LoadFromCSV(lista3, open.FileName);
            lista3.Column[0].Width:= 85;
            lista3.Column[1].Width:=30;
            lista3.Column[2].Width:=30;
            lista3.Column[3].Width:=30;
            lista3.Column[4].Width:=30;
            lista3.Column[5].Width:=30;
            lista3.Column[6].Width:=30;
            stB.Panels.Items[0].Text:= 'Núm. Filas: '+IntToStr(lista.Items.Count);
            button9.Enabled:=true;
       end;
end;

//*** Aquí comienza el proceso de buscar coincidencias ***
procedure TForm1.Button10Click(Sender: TObject);
var
  i, j, cinco: integer;
  res, cad1, cad2:Int64;
  comunes:String;
  list:TStringList;

begin
  Application.ProcessMessages;
  stB.Panels.Items[0].Text:= 'Combinaciones: '+IntToStr(lista3.Items.Count);
  stB.Panels.Items[1].Text:= 'Permutaciones: '+IntToStr(per.Items.Count);
  timer1.Enabled:= true;
  cinco:= 0;

  // Recorro lista3
  for i := 0 to lista3.Items.Count -1 do
    begin
      barra.Max:= lista3.Items.Count;
      barra.Position:= i+1;
      with lista3 do
        begin
        selected := Items[i];
        label31.Caption:= IntToStr(i+1); //--> esta etiqueta solo muestra el primer valor (1)
        // Calculo CAD1 cada vez que cambia el indice de lista3
        list.delimitedText:= items.Item[i].SubItems[0]+' '+items.Item[i].SubItems[1]+
                             ' '+items.Item[i].SubItems[2]+' '+items.Item[i].SubItems[3]+
                             ' '+items.Item[i].SubItems[4];
        cad1:= ValorCombinacion(list);
        label21.Caption:= IntToStr(cad1);
        buscarcomunes(cad1);
    stB.Panels.Items[4].Text:= FormatFloat('0.00',(i * 100) / lista3.items.Count)+'%';
      end;
    end;
end;

//... Procesos para calcular numeros mas repetidos de lista y carga del combobox
// con el numero de combinaciones aleatorias a generar.

end.

Y aquí puedes ver el programa en funcionamiento...
Como puedes ver ni las barras de progreso se mueven ni las etiquetas F y P muestran los valores de i y j que serían los elementos seleccionados de lista3 y per.

Un saludo.

bucanero 16-04-2018 11:22:59

Hola danielmj

En tu código en cuanto a la parte estrictamente de programación hay algunos errores:

- Asignas el valor MAX a los progressBar dentro de los bucles, eso hace que esta operación se realice en cada ciclo del propio bucle sin necesidad. Esta inicialización hay que hacerla al principio fuera del bucle y solo una vez. Y dentro del bucle solo actualizar el campo Position del progressBar

Cita:

Empezado por danielmj (Mensaje 525689)
Código Delphi [-]
  // Recorro lista3
  for i := 0 to lista3.Items.Count -1 do
    begin
      barra.Max:= lista3.Items.Count;
      barra.Position:= i+1;
    end;

La forma correcta es esta:
Código Delphi [-]
  // Recorro lista3
  barra.Max:= lista3.Items.Count;
  for i := 0 to lista3.Items.Count -1 do
    begin
      barra.Position:= i+1;
    end;

-La variable list:TStringList la declaras en la sección private del form y luego a nivel local dentro del var del procedure TForm1.Button10Click, por lo que al llamarlo desde dentro del procedure, vas a usar la variable local ademas parece que no esta inicializada, por lo que obtendrás un error de access violation


- En esta parte del código, primero muestras el contenido de una variable que aun no has asignado, y mas adelante es cuando le asignas el valor.
Cita:

Empezado por danielmj (Mensaje 525689)
Código Delphi [-]
procedure TForm1.buscarComunes(const Cad1:Int64);
     ...
     form1.label22.Caption:= IntToStr(cad2);
     ...
     cad2 := ValorCombinacion(list);
     ...
end;

En general te recomiendo que separes la parte del calculo de la parte visual y de muestreo de datos. Te quedara un código mucho mas limpio.

-En el procedure CompararCombinaciones has eliminado toda la parte correspondiente a la búsqueda de los números que coinciden cuando las dos combinaciones no son exactamente iguales (variable res)
Cita:

Empezado por danielmj (Mensaje 525689)
Código Delphi [-]
procedure TForm1.CompararCombinaciones(const Cad1, cad2:Int64);
var
  res:Int64;
  comunes:string;
begin
  res:=cad1 and cad2;
  if (cad1 = cad2) then begin
    // las dos combinaciones son iguales
    ShowMessage('Iguales');
  end
  else
    showMessage('No hay coincidencias');
    form1.timer1.Enabled:= false;
    form1.Refresh;
    exit
end;



- El procedure combinatoria2(v: array of Integer; Serie: TStrings); no tengo claro lo que quieres hacer con el, lo único que hace es cambiar el orden en que se obtiene los elementos de esa determinada combinación en particular, pero en realidad siempre obtienes la misma combinación en distinto orden de sus elementos. Y si lo que buscas es un sistema tipo LOTO, el orden de los elementos no influye. Aquí tienes los datos que muestras en tu vídeo, correspondientes todos ellos a la misma combinación:
Código:

1 4 5 12 24
1 4 5 24 12
1 4 12 5 24
1 4 12 24 5
1 4 24 5 12
1 4 24 12 5

Si mi ejemplo, que lo único que hace es buscar los numero que coinciden dentro de dos combinaciones,
te ha servido y es lo que estas intentando implementar, entonces intenta entender su funcionamiento, y en base a ese ejemplo haz tus propias modificaciones. Si no es eso exactamente lo que necesitas, entonces como te comento Mamcx el problema es que no esta muy claro lo que pretendes obtener
Cita:

Empezado por mamcx (Mensaje 525687)
Ni el video ni la imagen es clara. Lo que necesitas es un ejemplo minimo de los datos (la imagen habla de los numeros mas repetidos pero eso no se ve).

Un saludo

danielmj 17-04-2018 13:03:04

Hola, a ver me explico otra vez espero que esta vez mejor. Comenzaré por explicar el funcionamiento del programa, lo que espero que haga.

1º En el combo box, tengo una serie de valores que van de 5000 a 10.000.000 (aunque realmente como máximo uso 2.000.000).
2º En función de este valor, el programa genera x numero de combinaciones aleatorias y las almacena en un listview.
3º De entre todas esas combinaciones generadas, que pueden ser 5000 o 5 millones, busca los 6 números que mas veces han salido y calcula sus permutaciones (de ahí que combinatoria ordene esos 6 números). En 2 listbox genera las permutaciones de 6 y 5 numeros respectivamente
4º Las permutaciones almacenadas en per, que son de 5 números, las compara con lista3.
5º En otro listview (lista3), carga un archivo csv con sorteos ya jugados y a partir de las permutaciones del listbox "per" va comparando 1 a 1 con cada fila de ese listview
6º El programa tiene 3 métodos de busqueda: fuerza bruta, busqueda del 5 y selectiva por sorteos ya jugados.
6.1. El metodo "fuerza bruta" compara cada permutacion en base a 6 números con cada una las filas del listview que muestra las combinaciones aleatorias, es decir busca cada permutacion entre 5 mil.... 5mill de combinaciones, comparando. Si encuentra los 6 números de la permutacion me lanza un messagebox y me indica en que fila del listview aparece. Este método es muy tedioso y lento por que encontrar 6 numeros exactos es casi imposible por lo que realmente apenas lo uso.
6.2 Método "búsqueda del 5" básicamente hace lo mismo que el método anterior pero en lugar de 6 números con sus coincidencias, busca 5 con lo que las posibilidades de encontrarlos es mayor.
6.3 Método de "busqueda selectiva", realmente este método no tiene mayor importancia por que al igual que el método anterior, busca 5 coincidencias sobre los sorteos ya jugados. Tanto este método como busqueda del 5, usan las permtuaciones del listbox "per" de 5 números. De encontrarlos, tanto para el punto 6.3 y 6.2 me indica donde se encuentra esas coincidencias en los listview (lista [combinaciones aleatorias] o lista3 [combinaciones de sorteos previos]).

Ahora la teoría, mi teoría que obviamente no tiene que ser válida pero es la que quiero explotar:
Si el método de búsqueda del 5 encuentra 5 coincidencias en alguna de las filas entre el millon de combinaciones aleatorias, la aceptaría como válida estadisticamente y es la que jugaría.
Esto es válido para "Búsqueda selectiva", que es a la que pertenece todo el código aquí expuesto. Para la busqueda del 5 es lo mismo salvando las diferencias en cuanto a nombre de controles y variables.

En cuanto a lo que dices bucanero, es cierto lo del progress bar y lo de la variable, paso a corregirlo. En cuanto a combinatoria, más arriba expliqué su función.
Sobre la parte del código que no muestro. Esa parte la he modificado para que simplemente muestre un mensaje diciendo que no encontró esas 5 coincidencias búscadas. Sino son 5, el programa las ignora.

Saludos.

danielmj 17-04-2018 14:38:22

He cambiado el código a esto:
Código Delphi [-]
procedure TForm1.Button10Click(Sender: TObject);
var
  cad1:Int64;
  i:integer;
begin
  //Recorro la lista1
  with per do
    for i := 0 to Count -1 do
    begin
      // Calculo CAD1 cada vez que cambia el indice de lista3
      ItemIndex := i;
      //convierto el string separado por espacios a una lista
      list.delimitedText:=Items[i];
      cad1:= ValorCombinacion(list);
      buscarComunes(cad1);
    end;

Buscar Comunes
Código Delphi [-]
procedure TForm1.buscarComunes(const Cad1:Int64);
var
  cad2:Int64;
  j:integer;

begin
  // Recorro la lista2
   with lista3 do
   for j := 0 to Items.Count -1 do
   begin
     lista3.Selected:= items[j];
     //convierto el string separado por espacios a una lista
     list.delimitedText:= items.Item[j].SubItems[0]+ ' '+
                          items.Item[j].SubItems[1]+ ' '+
                          items.Item[j].SubItems[2]+ ' '+
                          items.Item[j].SubItems[3]+ ' '+
                          items.Item[j].SubItems[4]+ ' '+
                          items.Item[j].SubItems[5];
     // Calculo CAD2 cada vez que cambia el indice de per
     cad2 := ValorCombinacion(list);
     CompararCombinaciones(cad1, cad2);
   end;
end;

Código Delphi [-]
procedure TForm1.CompararCombinaciones(const Cad1, cad2:Int64);
var
  res:Int64;
  comunes:string;
begin
  label21.Caption:= IntToStr(cad1);
  res:=cad1 and cad2;
  if (cad1 = cad2) then begin
    // las dos combinaciones son iguales
    ShowMessage('Iguales');
    exit;
  end

//*** Esto lo tengo desactivado por que me dice "no hay coincidencias" por cada linea que compara.
{  else //if (res <> 0) then
    showMessage('No hay coincidencias');
    form1.timer1.Enabled:= false;
    form1.Refresh;
    exit}
end;

Parece que recorre la lista3 y lo compara con per pero incluso habiendo una combinación real de 5 aciertos sigue sin encontrarla, además el proceso es eterno, tarda como 1 seg. por cada comparacion y eso que solo son 120 permutaciones, cuando sean 720 y 50.000 combinaciones aleatorias... no sería factible creo.
Un saludo.

Casimiro Notevi 17-04-2018 14:55:17

Es que, para empezar, debes olvidarte de todo lo que sea manejo de cadenas de texto, presentación en pantalla, labels, edits, listview, etc.
Tienes que optimizar/afinar muchísimo si quieres que funcione de forma ágil.
Si vas a trabajar con números, entonces usa integer. Si vas a necesitar un grupo de ellos, entonces usa un array de integer. Y mejor aún, punteros a array de integer.
Empieza por hacer un prototipo simple, lo más simple y elemental posible, y a ser posible con papel y lápiz. Este es un tema muy estudiado, y muchas personas por el mundo han propuesto "soluciones" muy diversas, es algo muy complejo (si fuese fácil, todos seríamos millonarios :D), por lo que hay que afinar al máximo. Debes tener una base/estructura muy bien depurada para luego trabajar sobre ella con todo lo nuevo que se te ocurra añadir o probar.
Puedes buscar por internet algunos proyectos similares a los que quieres hacer y verás que no tiene mucho que ver con la forma en que estás atacando el problema. Échales un vistazo, te lo aconsejo.

danielmj 17-04-2018 15:01:31

Hola Casimiro, consejo aceptado, haré algo sencillo y desde ahí ire subiendo. Saludos.

bucanero 17-04-2018 18:14:08

Cita:

Empezado por danielmj (Mensaje 525716)
Parece que recorre la lista3 y lo compara con per pero incluso habiendo una combinación real de 5 aciertos sigue sin encontrarla...

El problema yo lo veo en el proceso que te rellena el listbox llamado PER con la función combinatoria2 que lo único que hace es cambiar el orden de una combinación de numeros entre ellos, pero que al aplicar la función ValorCombinacion te va a devolver siempre el mismo valor de combinación, independientemente del orden de los elementos.

De hecho haz la siguiente prueba, ejecuta este procedimiento y veras como a cada permutación de tu lista PER obtienes el mismo valor en la función ValorCombinacion, y eso solo te hace repetir cálculos innecesarios una y otra vez

Código Delphi [-]
procedure TEST;
var 
  i: longint;
begin
  // llama aqui a la funcion que te rellena el listbox PER 
  // combinatoria2([1, 4, 5, 12, 24], PER.Items);
  Memo1.Clear;
  with Per do begin
    for i := 0 to Count - 1 do begin
      //convierto el string separado por espacios a una lista
      list.Clear;
      list.delimitedText := items[i];
      //mostrar resultado
      Memo1.Lines.Add(items[i] + #9 + IntTostr(ValorCombinacion(list)));
    end;
  end; 
end;

Creo que no necesitas ni el listbox PER ni la función que te lo rellena. Para generar una combinación aleatoria yo usaría algo así:

Código Delphi [-]

const 
  maxNum:integer=49; //<-- Numero maximo de elementos

var
  combinacion:Int64;

 ...
 for N1 := 1 to maxNum - 5 do
    for N2 := N1 + 1 to maxNum - 4 do
      for N3 := N2 + 1 to maxNum - 3 do
        for N4 := N3 + 1 to maxNum - 2 do
          for N5 := N4 + 1 to maxNum - 1 do
            for N6 := N5 + 1 to maxNum do begin
              //codifica la combinacion en un Int64
              combinacion := ValorCombinacion(n1, n2, n3, n4, n5, n6);
              analizarCombinacion(combinacion);
            end;
  ...

Y todo fuera de los componentes visuales.

Yo te recomiendo que para separar toda la parte visual de los cálculos, utilices un datamodule o una simple unidad independiente para meter todos los procesos referentes a calculo y en el formulario solo dejes la parte visual.

Y por tema de velocidad en el tipo de rutinas que hacen calculos de forma masiva, intentan en la medida de todo lo posible no hacer llamadas a elementos visuales ni refresco de la información de los mismos, pues son rutinas que se pueden ejecutar varios millones de veces, y las llamadas a componentes visuales son muy lentas, a parte de que te pueden dejar muy tirado el programa.

Cita:

Empezado por danielmj (Mensaje 525716)
Código Delphi [-]
procedure TForm1.CompararCombinaciones(const Cad1, cad2:Int64);
...
  label21.Caption:= IntToStr(cad1);
...
    ShowMessage('Iguales');
...
end;

Un saludo

danielmj 22-04-2018 17:29:55

Bueno, gracias por vuestro tiempo.

danielmj 08-05-2018 19:49:53

Hola de nuevo,

Sigo con lo mismo porque no lo consigo, me estoy centrando en el método de bucanero y tengo una duda. en los edit1,2,3,4,5 y 6 están los numeros que mas veces salen. En su método, se comparan dos listbox completos, pero y si quiero comparar tan solo 1 item de un listbox con todos los de otro listbox? por que a mi me da error.

saludos

danielmj 09-05-2018 12:52:25

Revisión de código
 
Hola, quiero rellenar un listview con los valores de un lisbox pero no se que hago mal Pero devuelve una excepcion.

Código Delphi [-]
procedure TForm1.Button2Click(Sender: TObject);
var
  i,j: integer;

begin
  for i:= 0 to 5 do
  for j:= 0 to listBox1.Items.Count -1 do; //..> el listbox tiene 6 elementos
    lista.Items[i].SubItems[0]:= listBox1.Items.Strings[j];
    ...
    lista.Items[i].SubItems[5]:= listBox1.Items.Strings[j];
end;

Alguna idea o comentario?


La franja horaria es GMT +2. Ahora son las 01:29:12.

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