IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 Delphi Discussion :

Suppression des doublons d'un ListBox très long !


Sujet :

Delphi

  1. #1
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut Suppression des doublons d'un ListBox très long !
    Bonjour à tous,

    Je suis actuellement à la recherche d'un moyen de supprimer les Items en double d'un ListBox assez lourd.
    Pour cela j'ai une fonction très simple mais qui prend un temps incroyable (19 minutes exactement...).

    Je ne suis pas un expert en Delphi, mais j'ai pas mal cherché et apparemment il serait plus approprié d'utiliser un TFileStream... (Mais je ne sais pas comment)

    Voici ma fonction actuelle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    procedure NoDup(AListBox : TListBox);
    var
      i : integer;
    begin
      with AListBox do
      for i := Items.Count - 1 downto 0 do
      begin
        if Items.IndexOf(Items[i]) < i then
        Items.Delete(i);
        Application.ProcessMessages;
      end;
    end;
    Quelqu'un aurait-il une idée afin d'optimiser ce temps de traitement ?

    Merci par avance aux réponses !

    Beny

  2. #2
    Expert éminent
    Avatar de qi130
    Homme Profil pro
    Expert Processus IT
    Inscrit en
    Mars 2003
    Messages
    3 925
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Expert Processus IT
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2003
    Messages : 3 925
    Points : 6 040
    Points
    6 040
    Par défaut
    Exporte le contenu dans un TStringList en positionnant son Duplicate à False; puis écrase le contenu de la TListBox avec ce TStringList.

  3. #3
    Membre éclairé Avatar de DOLPat®
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Février 2003
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2003
    Messages : 426
    Points : 790
    Points
    790
    Par défaut
    Bonjour

    Tu peux déjà accélérer de façon notoire ton traitement en utilisant les fonctions BeginUpdate et EndUpdate pour bloquer les événements:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      AListBox.Items.BeginUpdate;
      // Traitement des Items de la liste
      // ...
      AListBox.Items.EndUpdate;

  4. #4
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Bonjour,

    Autre possibilité : voir ici : http://www.developpez.net/forums/d44...te-rapidement/
    tu pourras y trouver la procedure SupprDoublonsAOS(const nomFiSource, NomFiDest : string; IgnoreCase : boolean);
    ... celle-ci supprime rapidement les lignes formant des doublons d'un fichier de texte qu'on charge dans une StringList "L" avec L.LoadFromFile(nomFiSource);
    ... il suffit donc d'une légère modification pour charger les Items de la ListBox dans cette StringList "L".

    A+.

  5. #5
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Merci à tous pour vos réponses !
    Effectivement j'ai adopté le TStringList.
    Je ne connaissais pas les BeginUpdate et EndUpdate, intéressant !

    Si ça peut en aider certains, voici ma fonction finale :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    procedure NoDup(AListBox: TListBox);
    var
      lStringList: TStringList;
    begin
      lStringList := TStringList.Create;
      try
        lStringList.Duplicates := dupIgnore;
        lStringList.Sorted := true;
        lStringList.Assign(AListBox.Items);
        AListBox.Items.Assign(lStringList);
      finally
        lStringList.free
      end;
    end;
    Résultat ? Avant, la procédure prenait 19 minutes ; maintenant, au alentours de 1 à 2 minutes.
    Merci de votre aide

    Beny

  6. #6
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Bonjour,

    Benymypony :Avant, la procédure prenait 19 minutes ; maintenant, au alentours de 1 à 2 minutes.
    ... Le gain de vitesse semble excellent.
    ... Par contre cela m'intéresserait de savoir à quoi correspondent ces durées d'exécution :
    - Nombre d'Items présents dans la ListBox d'origine ?
    - Nombre moyen de caractères des Items ?
    - Nombre d'Items présents dans la ListBox expurgée, ou pourcentage de doublons ?

    Il y a peut être encore moyen de gagner en vitesse.

    A+. :D

  7. #7
    Membre expérimenté
    Homme Profil pro
    Ingenieur de recherche - Ecologue
    Inscrit en
    Juin 2003
    Messages
    1 157
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingenieur de recherche - Ecologue

    Informations forums :
    Inscription : Juin 2003
    Messages : 1 157
    Points : 1 414
    Points
    1 414
    Par défaut
    Je ne connaissais pas les BeginUpdate et EndUpdate, intéressant !
    Mais tu les as oubliés dans ta procédure
    et comme l'a écrit DolPat, c'est très avantageux en temps



    Citation Envoyé par benymypony Voir le message
    Merci à tous pour vos réponses !
    Effectivement j'ai adopté le TStringList.
    Je ne connaissais pas les BeginUpdate et EndUpdate, intéressant !

    Si ça peut en aider certains, voici ma fonction finale :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    procedure NoDup(AListBox: TListBox);
    var
      lStringList: TStringList;
    begin
      lStringList := TStringList.Create;
      try
        lStringList.Duplicates := dupIgnore;
        lStringList.Sorted := true;
        lStringList.Assign(AListBox.Items);
        AListBox.Items.Assign(lStringList);
      finally
        lStringList.free
      end;
    end;
    Résultat ? Avant, la procédure prenait 19 minutes ; maintenant, au alentours de 1 à 2 minutes.
    Merci de votre aide

    Beny

  8. #8
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Citation Envoyé par Gilbert Geyer Voir le message
    Bonjour,

    ... Le gain de vitesse semble excellent.
    ... Par contre cela m'intéresserait de savoir à quoi correspondent ces durées d'exécution :
    - Nombre d'Items présents dans la ListBox d'origine ?
    - Nombre moyen de caractères des Items ?
    - Nombre d'Items présents dans la ListBox expurgée, ou pourcentage de doublons ?

    Il y a peut être encore moyen de gagner en vitesse.

    A+.
    Bonjour,

    Alors voilà quelques infos :
    - Nombre d'Items avant l'opération : 120 000 env.
    - Nombre de caractères : entre 10 et 20.
    - Nombre d'Items après 'déboublonnage' : 14 000 env.



    Beny

  9. #9
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Citation Envoyé par dehorter olivier Voir le message
    Mais tu les as oubliés dans ta procédure
    et comme l'a écrit DolPat, c'est très avantageux en temps
    La durée d’exécution me convenait, je ne me suis plus posé de questions...
    Bien vue, je vais ajouter cela !

    Beny

  10. #10
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    Benymypony : merci pour les infos complémentaires.

    Entre-temps j'ai adapté le code de SupprDoublonsAOS au cas de la ListBox comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    Type ArrChaines   = Array Of String;
         ArrInteger   = Array Of Integer;
     
    PROCEDURE SupprDoublonsLB( L: tListBox; IgnoreCase : boolean);
    var       lgMaxChaines, i, DernierL : integer;
              PisteClef : ArrInteger;
              AOS : ArrChaines;
     
              Function AlphaSortAOS( var A : ArrChaines; var PisteurClefs : ArrInteger;
                                     IgnoreCase : boolean) : boolean; Far;
                    {-}
                    type     PlageDeChar    = array[0..255] of Integer;
                             TableauDeChar  = array of PlageDeChar;
                    var
                             I, J           : Integer;
                             LLimit, HLimit : Integer;
                             CharEnCours    : Integer;
                             PtrTmp         : Integer;
                             PisteurDeChar  : TableauDeChar;
     
                        procedure SecondePasse(var PtrSuiv : Integer; PosCharCourant, Dernier : Integer);
                        {-}
                        var    PtrSurVide      : Integer;
                               PtrTmp          : Integer;
                               CharEnCours     : Integer;
                               LLimit, HLimit  : Integer;
                               PtrPlageDeChar  : PlageDeChar;
     
                        begin
                          PtrPlageDeChar := PisteurDeChar[PosCharCourant];
                          PtrSurVide := 0;
                          LLimit   := MaxInt;
                          HLimit   := 0;
                          repeat
                            PtrTmp := PtrSuiv;
                            PtrSuiv := PisteurClefs[PtrTmp];
                            if PosCharCourant <= Length(A[PtrTmp])
                            then begin
                                 if IgnoreCase
                                 then CharEnCours := Ord(UpCase(A[PtrTmp][PosCharCourant]))
                                 else CharEnCours := Ord(A[PtrTmp][PosCharCourant]);
     
                                 if CharEnCours < LLimit then LLimit := CharEnCours;
                                 if CharEnCours > HLimit then HLimit := CharEnCours;
                                 PisteurClefs[PtrTmp] := PtrPlageDeChar[CharEnCours];
                                 PtrPlageDeChar[CharEnCours] := PtrTmp;
                            end
                            else begin
                                 PisteurClefs[PtrTmp] := PtrSurVide;
                                 PtrSurVide := PtrTmp;
                            end;
                          until PtrSuiv = 0;
     
                          Inc(HLimit);
                          repeat
                            Dec(HLimit);
                            PtrSuiv := PtrPlageDeChar[HLimit];
                            if (PtrSuiv <> 0)
                            then begin
                                 if PisteurClefs[PtrSuiv] <> 0
                                 then begin
                                      if PosCharCourant <> lgMaxChaines
                                      then SecondePasse(PtrSuiv, PosCharCourant + 1, Dernier) // Récursif
                                      else begin // Atteint profondeur maxi de tri, on stoppe les récursions
                                           // Chainage des autres chaines
                                           PtrTmp := PtrSuiv;
                                           while PisteurClefs[PtrTmp] <> 0
                                              do PtrTmp := PisteurClefs[PtrTmp];
                                           PisteurClefs[PtrTmp] := Dernier;
                                      end;
                                 end
                                 else PisteurClefs[PtrSuiv] := Dernier;
                                 Dernier := PtrSuiv;
                                 PtrPlageDeChar[HLimit] := 0;
                            end;
                          until HLimit <= LLimit;
     
                          if PtrSurVide <> 0
                          then begin // Chainage des chaines vides
                               PtrSuiv := PtrSurVide;
                               while PisteurClefs[PtrSurVide] <> 0
                                  do PtrSurVide := PisteurClefs[PtrSurVide];
                               PisteurClefs[PtrSurVide] := Dernier;
                          end;
                        end; // SecondePasse
     
              BEGIN // AlphaSortAOS
                Result:=FALSE;
                try // Dimensionnement + Initialisation des structures de données :
                  lgMaxChaines:=0;
                  for i:=0 to High(A) do lgMaxChaines:=Max(lgMaxChaines,length(A[i]));
                  if lgMaxChaines < 2 then
                  begin ShowMessage( 'AlphaSortAOS : non-utilisable si la longueur max'+#13#10
                                    +'des chaînes est inférieure à 2 caractères');
                        EXIT;
                  end;
                  SetLength(PisteurDeChar,lgMaxChaines+1);
                  for i:=0 to High(PisteurDeChar) do
                  for j:=0 to 255 do PisteurDeChar[i,j]:=0;
     
                  SetLength(PisteurClefs,High(A)+1);
                  for i:=0 to High(A) do PisteurClefs[i]:=0;
                except
                  ShowMessage('AlphaSortAOS : Mem-vive saturée !!!'); EXIT;
                end;
                Result:=TRUE;
     
                // Initialisation des tableaux selon le 1er caractère de chaque ligne
                LLimit := MaxInt;
                HLimit := 0;
                for I := 1 to High(A) do begin
                    if IgnoreCase then CharEnCours := Ord(Upcase(A[I][1]))
                    else CharEnCours := Ord(A[I][1]);
                    if CharEnCours < LLimit then LLimit := CharEnCours;
                    if CharEnCours > HLimit then HLimit := CharEnCours;
                    PisteurClefs[I] := PisteurDeChar[1, CharEnCours];
                    PisteurDeChar[1, CharEnCours] := I;
                end;
                DernierL := 0;
                for I := HLimit downTo LLimit do begin
                  PtrTmp := PisteurDeChar[1, I];
                  if PtrTmp <> 0
                  then begin
                       if PisteurClefs[PtrTmp] <> 0
                       then SecondePasse(PtrTmp, 2, DernierL)  // Récursif
                       else PisteurClefs[PtrTmp] := DernierL; // lien
                       DernierL := PtrTmp;
                  end;
                end;
                // Arrivé ici le tableau A reste dans son état non-trié et PisteurDeClefs
                // renvoie l'ordre dans lequel il faut parcourir A pour le restituer dans
                // l''ordre trié
              End; // AlphaSortAOS
     
    BEGIN // SupprDoublonsLB
     
              SetLength(AOS,L.Items.count);
              for i:=0 to L.Items.count-1 do AOS[i]:=L.Items[i];
              L.Clear;
     
              if Not AlphaSortAOS( AOS, PisteClef, True) then EXIT;
     
              i := DernierL;
              L.Items.Add(AOS[i]);
              repeat if IgnoreCase then
                     begin if UpperCase(AOS[i])<>UpperCase(AOS[PisteClef[i]])
                           then L.Items.Add(AOS[PisteClef[i]]);
                     end else
                     begin if AOS[i]<>AOS[PisteClef[i]]
                           then L.Items.Add(AOS[PisteClef[i]]);
                     end;
                     i:=PisteClef[i];
              until i=0;
    END; // SupprDoublonsLB
    Puis j'ai comparé les temps d'exécution avec ceux de NoDup comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    procedure NoDup(AListBox: TListBox);
    var
      lStringList: TStringList;
    begin
      lStringList := TStringList.Create;
      try
        AListBox.Items.BeginUpdate;
        lStringList.BeginUpdate;
        lStringList.Duplicates := dupIgnore;
        lStringList.Sorted := true;
        lStringList.Assign(AListBox.Items);
        AListBox.Items.Assign(lStringList);
        AListBox.Items.EndUpdate;
      finally
        lStringList.EndUpdate;
        lStringList.free
      end;
    end;
     
    procedure TfrmGen.bCreerListeClick(Sender: TObject);
    var       i : integer;
    begin     ListBox2.clear;
              for i:=1 to 120000 do begin
                  if i=120000 then ListBox2.Items.add('Ligne de la Fin') else
                  if i=1      then ListBox2.Items.add('Ligne du Début')
                  else ListBox2.Items.add('Doublons Doublons Do'); //<- 20 caractères
              end;
              labCountIni.caption:=IntToStr(ListBox2.Items.count);
    end;
     
    var GTC : DWord;
     
    procedure TfrmGen.bNoDupClick(Sender: TObject);
    begin     GTC :=GetTickCount;
              NoDup(ListBox2);
              labMis.caption:='Mis : '+IntToStr(GetTickCount-GTC)+' ms';
              labCountFinal.caption:=IntToStr(ListBox2.Items.count);
    end;
     
    procedure TfrmGen.bSupprDoublonsLBClick(Sender: TObject);
    begin     labCountIni.caption:=IntToStr(ListBox2.Items.count);
              GTC :=GetTickCount;
              SupprDoublonsLB(ListBox2,True);
              labMis.caption:='Mis : '+IntToStr(GetTickCount-GTC)+' ms';
              labCountFinal.caption:=IntToStr(ListBox2.Items.count);
    end;
    Résultats pour 120 000 Items dont tous, sauf le 1ier et le dernier, sont des Items de 20 caractères formant doublons : (durées d'exécution pour Pentium III à 1,13 GHz) :
    - avec NoDup : mis 3672 ms
    - avec SupprDoublonsLB : mis 2640 ms.
    soit un facteur de gain de 1,39.
    J'espérais en gagner davantage ... mais un gain est un gain.
    Par contre ta solution a l'avantage de la simplicité.

    A+.

  11. #11
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Citation Envoyé par Gilbert Geyer Voir le message
    Re-bonjour,

    Benymypony : merci pour les infos complémentaires.

    Entre-temps j'ai...
    [...]
    ...mais un gain est un gain.
    Par contre ta solution a l'avantage de la simplicité.

    A+.
    Merci Gilbert pour ce test très précis !
    Effectivement c'est très intéressant de comparer.

    Pour le moment je ne cherche plus vraiment à diminuer ce temps d'exécution qui me semble très raisonnable.
    Finalement, après avoir retravailler et optimiser toute ma procédure, l’exécution complète prend entre 20 et 30 secondes.

    Comme tu dis, un gain est un gain.
    Je pense que quand je reviendrais sur ce point à la finalisation de l'application, pour peaufiner les petits détails.

    (Merci encore à vous !)

    Beny

  12. #12
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Bonjour,

    Benymypony : Effectivement c'est très intéressant de comparer.
    ... Mais manque de chance j'ai comparé dans des conditions foireuses : comme j'ai fait les tests avec une listBox dont tous les Items sauf trois formaient des doublons mon code était placé dans les conditions les plus favorables.
    Par contre si la ListBox ne comporte aucun doublon le petit gain de 39% est vite compensé par une perte.
    Ayant refait un test avec 14 000 Items différents + (120 000 - 14 000) Items formant tous des doublons j'ai eu les résultats suivants :
    - NoDup : mis 6391 ms
    - SupprDoublonsLB : mis 6796 ms
    ... donc si aucun doublon l'écart se creuse en faveur de NoDup.
    ... Bon ça n'empêche pas que le code d'origine reste performant pour le cas traité dans la discussion pointée par le lien cité dans mon message du 15/07/2011 10h21.
    ... L'essentiel est d'avoir essayé.
    ... Pour la ListBox vaut donc mieux conserver ton code qui en plus a l'avantage de la simplicité.

    A+.

  13. #13
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Bonjour,

    Je retire ce que j'ai écrit hier à propos des résultats comparatifs. Les résultats ont apparemment été faussés par le fait que Skype et Avira piquent la priorité de temps en temps, et je n'avais fait qu'un seul test au lieu de tirer une moyenne sur 3 tests.

    J'ai donc refait les tests avec une listBox chargée de 120 000 Items sans aucun doublon comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TfrmGen.bCreerListeClick(Sender: TObject);
    var       i : integer;
    begin     ListBox2.clear;
              for i:=1 to 120000 do begin
                  ListBox2.Items.add('Aucun Doublon Auncu'+IntToStr(i)+IntToStr(Random(255)));
              end;
              labCountIni.caption:=IntToStr(ListBox2.Items.count);
    end;
    Resultats issus pour chaque code de trois tests successifs :
    - avec NoDup : moyenne des trois tests : mis 121927 ms
    - avec SupprDoublonsLB : moyenne des trois tests : mis 48864 ms
    facteur de gain en faveur de SupprDoublonsLB : 2,495.

    A+.

  14. #14
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 815
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 815
    Points : 13 531
    Points
    13 531
    Par défaut
    Si, si, il peut y avoir des doublons

    Sinon, une autre variante avec une table de hachage:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    function ClearListDup(aList: TStrings): integer;
    const
      Hash1 = 130469;
      Hash2 = 16384;
     
    label
      Next;
     
    var
      HashTable :array [0..Hash2 -1] of array of integer;
      List      :TStringList;
      Hash      :integer;
      i         :integer;
      j         :integer;
     
    begin
      Result := 0;
      i      := 0;
      List   := TStringList.Create;
     
      try
        List.Assign(aList);
     
        while i < List.Count do
        begin
          // Calcul du hash
          Hash := 0;
          for j := 1 to Length(List[i]) do
            Hash := (Hash *Hash1 +Ord(List[i][j])) mod Hash2;
     
          // Déjà un texte avec ce hash ?...
          for j := 0 to High(HashTable[Hash]) do
          begin
            // ...oui. Est un doublon ?
            if List[i] = List[HashTable[Hash][j]] then
            begin
              Inc(Result);
              List.Delete(i);
              goto Next;
            end;
          end;
     
          // ...non. Texte unique (pour l'instant :-). L'ajoute à la table de hachage
          SetLength(HashTable[Hash], Length(HashTable[Hash]) +1);
          HashTable[Hash][High(HashTable[Hash])] := i;
          Inc(i);
    Next:
        end;
     
        aList.Assign(List);
      finally
        List.Free;
      end;
    end;

  15. #15
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Bonjour,

    Andnotor : Si, si, il peut y avoir des doublons
    ... bien entendu. Comme j'avais déjà fait des tests en présence de doublons, l'objectif des derniers tests était de vérifier le comportement des codes en l'absence de doublons vu que l'on ne sait pas par avance si oui ou non il y a des doublons.

    Par contre je vais en profiter pour tester ta solution.

    A+.

  16. #16
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    Voici les résultats des tests avec ClearListDup : 3 tests avec 120 000 Items sans aucun doublon, et 3 tests avec 120 000 Items dont seuls celui du Début et celui de la Fin sont différents et tous les autres Items sont identiques donc la liste expurgée ne comporte que trois Items.

    Ces Items ont été générés comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TfrmGen.bCreerListeClick(Sender: TObject);
    var       i : integer;
    begin     ListBox2.clear;
              for i:=1 to 120000 do begin
                  if i=120000 then ListBox2.Items.add('Ligne de la Fin') else
                  if i=1      then ListBox2.Items.add('Ligne du Début')
                  else ListBox2.Items.add('Doublons Doublons Do'); //<- 20 caractères
                  //ListBox2.Items.add('Aucun Doublon Aucun'+IntToStr(i)+IntToStr(Random(255)));
              end;
              labCountIni.caption:=IntToStr(ListBox2.Items.count);
    end;
    Résultats avec ListBox sans aucun doublon :
    - avec ClearListDup : moyenne des trois tests : mis 48115 ms.
    (à comparer avec les résultats d'hier :
    - avec NoDup : moyenne des trois tests : mis 121927 ms
    - avec SupprDoublonsLB : moyenne des trois tests : mis 48864 ms)

    Résultats avec ListBox quasi-pleine de doublons :
    - avec ClearListDup : moyenne des trois tests : mis 207962 ms. (3 min 46)
    (à comparer aux résultats du 16/07 :
    - avec NoDup : mis 3672 ms
    - avec SupprDoublonsLB : mis 2640 ms.

    La lenteur de ClearListDup, en cas de présence de doublons, s'explique par le fait de l'utilisation de List.Delete(i);
    car chaque fois qu'on Delete un Item ça oblige Delphi à décaler vers l'avant tous les Items depuis le iième jusqu'au dernier ... alors que NoDup et SupprDoublonsLB ne Deletent rien et se contentent d'ignorer la présence des doublons avant d'ajouter un Item à la fin d'une deuxième liste formant résultat.
    Par contre comme le code de ClearListDup est bien plus simple que celui de SupprDoublonsLB cela vaudrait le coup de l'optimiser en utilisant la même astuce en "ignorant sans supprimer" ... ce qui en bref se traduit par "If Déjà un texte avec ce hash then on ignore d'ajouter else ListeResultat.Add()".

    A+.

  17. #17
    Membre régulier
    Homme Profil pro
    Inscrit en
    Août 2006
    Messages
    108
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2006
    Messages : 108
    Points : 82
    Points
    82
    Par défaut
    Bonjour,

    Merci beaucoup à vous tous pour vos tests !
    Tout cela m'a permis de comprendre beaucoup de petites choses que j'ignoraient.

    Très intéressant de comparer 3 méthodes différentes et de voir des résultats très variés.

    Beny

  18. #18
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 815
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 815
    Points : 13 531
    Points
    13 531
    Par défaut
    Citation Envoyé par Gilbert Geyer Voir le message
    l'objectif des derniers tests était de vérifier le comportement des codes en l'absence de doublons...
    J'ai bien compris, mais la façon dont tu génères tes 120'000 éléments peut entraîne des doublons
    Une autre chose dans ta fonction: si le dernier élément est un doublon, il subsiste au retour de la fonction.

    Sinon, j'ai suivi ton conseil en passant par une table de textes (et des objects par la même occasion).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    function ClearListDup(const aList: TStrings): integer;
    type
      TDupItem = record
        Text :string;
        Obj  :TObject;
      end;
     
    const
      Hash1 = 130469;
      Hash2 = 16384;
     
    label
      Next;
     
    var
      Texts     :array of TDupItem;
      HashTable :array [0..Hash2 -1] of array of integer;
      Hash      :integer;
      i         :integer;
      j         :integer;
     
    begin
      Result := 0;
     
      //Copie la liste dans la table temporaire
      SetLength(Texts, aList.Count);
      for i := 0 to aList.Count -1 do
      begin
        Texts[i].Text := aList[i];
        Texts[i].Obj  := aList.Objects[i];
      end;
     
      //Contrôle des doublons
      for i := 0 to High(Texts) do
        with Texts[i] do
        begin
          // Calcul du hash
          Hash := 0;
          for j := 1 to Length(Text) do
            Hash := (Hash *Hash1 +Ord(Text[j])) mod Hash2;
     
          // Déjà un texte avec ce hash ?...
          for j := 0 to High(HashTable[Hash]) do
          begin
            // ...oui. Est un doublon ?
            if Text = Texts[HashTable[Hash][j]].Text then
            begin
              Inc(Result);
              Text := '';
              goto Next;
            end;
          end;
     
          // ...non. Texte unique (pour l'instant :-). L'ajoute à la table de hachage
          j := Length(HashTable[Hash]);
          SetLength(HashTable[Hash], j +1);
          HashTable[Hash][j] := i;
     
    Next:
        end;
     
      //Restaure la liste nettoyée
      if Result > 0 then
      begin
        aList.Clear;
     
        for i := 0 to High(Texts) do
          with Texts[i] do
            if Text <> '' then
              aList.AddObject(Text, Obj);
      end;
    end;
    @benymypony: C'est un plaisir

  19. #19
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    J'ai bidouillé le Premier code d'AndNotOr (celui d'hier 19h34) comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    function ClearListIgnoreDup(aList: TStrings): integer; //
    const
      Hash1 = 130469;
      Hash2 = 16384;
     
    label 
      Next;
     
    var
      HashTable :array [0..Hash2 -1] of array of integer;
      List      : TStringList;
      Hash      : integer;
      i         : integer;
      j         : integer;
     
    begin
      Result := 0;
      i      := 0;
      List   := TStringList.Create;
     
      try
        List.Assign(aList);
     
        while i < List.Count do begin
              // Calcul du hash
              Hash := 0;
              for j := 1 to Length(List[i])
               do Hash := (Hash *Hash1 +Ord(List[i][j])) mod Hash2;
     
              // Déjà un texte avec ce hash ?...
              for j := 0 to High(HashTable[Hash]) do begin
                  // ...oui. Est un doublon ?
                  if List[i] = List[HashTable[Hash][j]] then begin
                     Inc(Result);
                     List[i]:='zzz'; //<-- Au lieu de List.Delete(i);
                     Inc(i);
                     GoTo Next;
                  end;
              end;
     
              // ...non. Texte unique (pour l'instant :-). L'ajoute à la table de hachage
              SetLength(HashTable[Hash], Length(HashTable[Hash]) +1);
              HashTable[Hash][High(HashTable[Hash])] := i;
              Inc(i);
    Next:
        end;
        List.sort; // on place les 'zzz' vers la fin
        for i:=List.count-1 downTo 0 do if List[i]='zzz' then List.Delete(i);
        aList.Assign(List);
      finally
        List.Free;
      end;
    end;
    Résultats avec ListBox de 120 000 Items quasi-pleine de doublons :
    - moyenne des trois tests : mis 4719 ms contre les 207962 ms de mon message de 11h39.

    Par contre ma bidouille ne me plaît pas beaucoup car je n'ai pas réussi à éviter le List.Delete(i) sauf qu'en l'utilisant de la sorte cela évite à Delphi d'avoir à décaler vers l'avant tous les Items depuis le iième jusqu'au dernier vu que dans mon cas le iième est toujours le dernier.

    A+.

    EDIT : post coisé avec celui d'AndNotOr.

  20. #20
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 266
    Points
    3 266
    Par défaut
    Re-bonjour,

    AndNotOr : J'ai bien compris, mais la façon dont tu génères tes 120'000 éléments peut entraîne des doublons.
    Une autre chose dans ta fonction: si le dernier élément est un doublon, il subsiste au retour de la fonction.
    ... bon s'il reste quelques doublons parmi les 120 000 il risquent de ne pas nombreux vu les deux Random.

    Pour ce qui est de "dans ta fonction: si le dernier élément est un doublon, il subsiste au retour de la fonction." je verrai ça demain.

    Vu l'heure, et comme je suis scotché à mon micro depuis cet matin, je testerai la dernière version de ClearListDup demain.

    A+.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 3
    Dernier message: 26/07/2009, 10h06
  2. suppression des doublons
    Par sucreMan dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 03/10/2007, 12h12
  3. [SQL] Suppression des doublons d'un champs SQL
    Par fabien14 dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 21/05/2007, 14h28
  4. problème avec la suppression des doublons dans arraylsit
    Par ulysse031 dans le forum Langage
    Réponses: 13
    Dernier message: 04/03/2007, 12h52
  5. suppression des doublons
    Par LuckySoft dans le forum Requêtes
    Réponses: 9
    Dernier message: 04/08/2006, 12h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo