Bonjour,

Je pensais qu'avec le code du message du 27/11/2007 12h08 il serait difficile d'améliorer encore davantage la vitesse d'exécution ... mais voici un code encore plus rapide qui exploite les performances du Tri AlphaSort plus rapide que les variantes du QuickSort :
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
 
Type ArrChaines   = Array Of String;
     ArrInteger   = Array Of Integer;
 
PROCEDURE SupprDoublonsAOS(const nomFiSource, NomFiDest : string; IgnoreCase : boolean);
var   lgMaxChaines, i, DernierL : integer;
      L  : TStringList; S : string;
      FS : TFileStream; // Chrono : oChrono; < Chronomètre
      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
 
      procedure Prio(const PriorityClass : Cardinal);
      //        lancer Prio($80) pour placer en priorité haute,
      //        et Prio($20) pour revenir à une priorité normale.
      //        Accélère certains processus tels le chargement et la sauvegarde de fichiers
      var       hProcess, pid : Cardinal;
      begin     pid := GetCurrentProcessId();
                hProcess := OpenProcess($40, true, pid);
                SetPriorityClass(hProcess, PriorityClass);
                CloseHandle(hProcess);
      end;
 
BEGIN // SupprDoublonsAOS
      Prio($80);
      // 1 ) Chargement du fichier-source :
      // Chrono.Top;
      L:=TStringList.create;
      L.Sorted := FALSE;
      L.Capacity:=200005; //< à augmenter si besoin est
      L.LoadFromFile(nomFiSource);
      SetLength(AOS,L.count);
      for i:=0 to L.count-1 do AOS[i]:=L[i]; //< temps de transfert très largement
      // compensé par le gain de vitesse obtenu lors du tri suivant sur AOS
      // alors que le tri direct sur StringList s'avère deux fois lent
      L.Free;
      // smsRed('SupprDoublonsAOS : Chargé en : '+Chrono.Mis, frmGenP.RedTrace);
 
      // 2 ) Tri :
      // Chrono.Top;
      if AlphaSortAOS( AOS, PisteClef, True)
      then // smsRed('SupprDoublonsAOS : Trié en : '+Chrono.Mis, frmGenP.RedTrace)
      else EXIT;
 
      // 3 ) Sauvegarde en ignorant les doublons :
      // Chrono.Top;
      FS := TFileStream.Create(NomFiDest, fmCreate);
      S:='Supprimé doublons avec SupprDoublonsAOS'+#13#10;
      FS.Write(PChar(S)^, length(S));
      i := DernierL;
      S :=AOS[i]+#13#10;
      FS.Write(PChar(S)^, length(S));
      repeat if IgnoreCase then
             begin if UpperCase(AOS[i])<>UpperCase(AOS[PisteClef[i]]) then //alors directos vers disque
                   begin S:=AOS[PisteClef[i]]+#13#10;
                         FS.Write(PChar(S)^, length(S));
                   end;
             end else
             begin if AOS[i]<>AOS[PisteClef[i]] then //alors directos vers disque
                   begin S:=AOS[PisteClef[i]]+#13#10;
                         FS.Write(PChar(S)^, length(S));
                   end;
             end;
             i:=PisteClef[i];
      until i=0;
      FS.Free;
      // smsRed('SupprDoublonsAOS : Sauvé en : '+Chrono.Mis, frmGenP.RedTrace);
      Prio($20);
END; // SupprDoublonsAOS
 
// --- Utilisation -----------
 
var       NomLongFichierOrig, NomSauverSous : string;
          //ChronoG : oChrono; < Chronomètre
 
procedure TfrmGen.btnSupprDoublonsAOSClick(Sender: TObject);
var       NomCourtFichier : string; pop : Word;
begin     OpenDialog1.InitialDir := RepAppli;
          OpenDialog1.Filter:= 'Fichiers Texte (*.txt)|*.TXT';
          if OpenDialog1.Execute then
          begin //ChronoG.Top;
                NomLongFichierOrig:=OpenDialog1.FileName;
                NomCourtFichier:=ExtractFileName(NomLongFichierOrig);
                pop := pos('.',NomCourtFichier);
                if pop>0 then Insert('_Trie',NomCourtFichier,pop)
                         else NomCourtFichier:=NomCourtFichier+'_Trie';
                NomSauverSous:=RepAppli+NomCourtFichier;
 
               // smsRed('SupprDoublonsSL : '+ChronoG.Mis, RedTrace);
 
                SupprDoublonsAOS(NomLongFichierOrig, NomSauverSous, False);
                // smsRed('SupprDoublonsAOS : '+ChronoG.Mis, RedTrace);
          end;
end;
// Remarque la procedure smsRed() placée en commentaire dans ce code ne figure pas ici elle se contente d'envoyer un message dans un RichEdit, le code de l'objet Chrono (simple chronomètre) n'y figue pas non plus ( il utilise tout simplement GetTickCount).
Résultats avec fichier de tests de 200000 lignes dont une ligne sur deux est du texte aléatoire de 62 caractères suivis du numéro de ligne et une ligne sur deux est égale à la chaîne '<<< MI-DOUBLON >>>' (8540 Ko) :
- avec l'algo AlgoArt7juin2007 : 351893 ms (5,86 min)
- avec procedure DoublonsPapyJohn3bis : 2652 ms (dont 1555 ms pour le tri)
- avec procedure SupprDoublonsAOS : 1599 ms (dont 640 ms pour le tri)
soit 1,66 fois plus rapide que DoublonsPapyJohn3bis
et 220 fois plus rapide que AlgoArt7juin2007
.
(Temps d'éxec valables pour Pentium III à 1,13 GHz)

A+