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 21: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 09: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 10:02:00

Gracias duilioisola, voy a probar.

danielmj 12-04-2018 13: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 22: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 22: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 11: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 13: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 15: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 15: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 17: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 17: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 17: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 18: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 18: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 19: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 20: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 20: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 20: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 20:32:48

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


La franja horaria es GMT +2. Ahora son las 14:16:42.

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