Aller au menu - Aller au contenu

Icône Le tri à paniers

Avatar
Mise à jour : 19/01/2009
98 visites depuis 7 jours , classé 567/777
Bonjour à tous. :)

Aujourd'hui je vais vous montrer un nouvel algorithme de tri. Il va nous permettre de trier un tableau de nombres entiers (et non réels, vous verrez pourquoi quand je vous donnerai le principe). Il est surtout utilisé lorsque vous voulez trier des entiers très proches les uns des autres, sans quoi l'algorithme commence à prendre beaucoup de place en mémoire.

L'intérêt est essentiellement « culturel », vu que la méthode employée est assez originale.

Mise à jour du 18/04/2009 : reformulation d'à peu près tout le tutoriel, plus lifting des exemple et implémentation C++ par ShareMan. Merci à lui !

Le principe

Le principe est assez simple : on va compter le nombre d'occurences de chaque nombre de la liste, pour réécrire celle-ci. Ainsi, on va compter le nombre de « 2 », de « 3 », ... qu'elle contient, puis écrire x « 2 », suivis par y « 3 », et ainsi de suite...

Prenons un exemple. Imaginons que j'aie cette liste à trier dans l'ordre croissant :

Code : Autre
1
2 - 8 - 6 - 4 - 10 - 12 - 6 - 4


La première étape, est de déterminer le plus petit nombre de la liste, ainsi que le plus grand. Donc ici, ces nombres sont 2 et 12. Jusque là, c'est facile à comprendre et à implémenter :) .

Ensuite, nous allons créer autant de « paniers » qu'il n'y a de nombres entre 2 et 12. Pour connaître le nombre de paniers, on utilise la formule (max - min) + 1. Dans notre exemple, on obtient 11. On crée donc un panier par nombre possible dans la liste (compris entre le minimum et le maximum). Chaque panier correspondra ainsi au nombre d'occurrences d'un élément dans la liste : le premier panier (numéro 2) contiendra le nombre de 2 dans la liste, le second (numéro 3) le nombre de 3 dans la liste, et ainsi de suite.

Ceci nous explique pourquoi ce tri ne fonctionne pas avec les réels, ni avec d'autres éléments que les entiers : entre deux réels, il y a une infinité d'autres réels. Vu que l'on ne peut pas créer une infinité de paniers, trier des réels avec cet algorithme est donc interdit !

Revenons à notre exemple. Pour le moment on a :

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Liste à trier : 

2 - 8 - 6 - 4 - 10 - 12 - 6 - 4


Paniers :

02 : 0
03 : 0
04 : 0
05 : 0
06 : 0
07 : 0
08 : 0
09 : 0
10 : 0
11 : 0
12 : 0


L'étape suivante est de parcourir la liste à trier, du début à la fin, et, pour chaque valeur rencontrée, de rajouter 1 « jeton » dans le panier qui lui correspond (c'est-à-dire incrémenter le compteur).

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Liste à trier : 

2 - 8 - 6 - 4 - 10 - 12 - 6 - 4


Paniers :

02 : 1
03 : 0
04 : 2
05 : 0
06 : 2
07 : 0
08 : 1
09 : 0
10 : 1
11 : 0
12 : 1


Et on obtient ainsi le nombre d'occurences de chaque élément de la liste !
Maintenant, on va s'en servir pour réécrire entièrement la liste à trier. On prend le 1er panier, et on écrit sa valeur correspondante autant de fois qu'il y a de jetons dedans. On recommence avec le 2e panier, le 3e, et ainsi de suite, en écrivant toujours les valeurs à la suite.

Exemple : dans le 1er panier, le 02, il y a 1 jeton : on écrit une fois 02 dans le tableau. Le panier suivant, 03, ne contient rien. On passe. Le 04 contient 2 jetons : on écrit deux fois le nombre 04 dans le tableau. Et ainsi de suite...

On obtient alors :

Code : Autre
1
02 - 04 - 04 - 06 - 06 - 08 - 10 - 12


Hourrah ! La liste est triée !

Implémentation du tri

Ici, nous allons donc vous présenter une implémentation de l'algorithme du tri à paniers en C++ par ShareMan, qui permet de trier un vector (tableau dynamique). Ne soyez pas effrayés par le terme de "vector", il s'agit simplement d'une classe de la STL (la bibliothèque standard du C++) encapsulant les détails parfois complexes de la gestion et de la manipulation des tableaux dynamiques (en C, peut-être connaissez-vous malloc et free qui permettent d'utiliser ce genre de tableau ?). std::vector est LA bonne solution pour manipuler les tableaux dynamiques (et les tableaux tout courts) en C++ (il ne faut pas oublier que ce langage est orienté objet). Dans le code qui va suivre, chaque ligne, ou presque, est commentée, il ne devrait donc pas être trop dur à déchiffrer, même pour les gens qui ne font pas de C++ :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
void tri_paniers(std::vector<int> & vec)
{
    /* On commence par trouver le minimum et le maximum du vector */
    int min = *std::min_element(vec.begin(),vec.end());
    int max = *std::max_element(vec.begin(),vec.end());

    /* Puis on crée un nouveau vector, qui représente les paniers */
    std::vector<int> paniers(max-min+1);

    /* Ensuite, on parcourt le tableau, et on remplit les paniers (opération de comptage)*/
    for(unsigned int i = 0; i < vec.size(); ++i)
        paniers[vec[i]-min]++;

    /* Et enfin, on réécrit le tableau à partir de nos paniers. */
    for(unsigned int i=0, c=0; i < paniers.size(); ++i)
        for(int j=0; j < paniers[i]; ++j, ++c)
            vec[c] = i+min;
}


L'algorithme étant relativement simple, le code source est très court. Notez que la fonction présentée ici trie des tableaux (std::vector) d'entiers ; contrairement à une majorité d'autres algorithmes de tri (principalement ceux se basant sur les comparaisons), il n'est pas possible de la généraliser à des tableaux contenant des éléments d'un autre type (n'oubliez pas que le tri à paniers ne fonctionne que sur des entiers). À titre d'exercice, vous pouvez recoder cette fonction dans votre langage préféré et tenter de la généraliser, afin de bien voir ce qui bloque.

Autrement, vous pouvez tester l'algorithme avec ce fichier d'exemple complet qui génère 10 entiers pseudo-aléatoirement et les trie.:

Code : C++
 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
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <vector>

/* Le tri vu avant. Notez que l'argument vec est passé par référence (&),
    cela signifie que le tableau ne va pas être copié avant tri */
void tri_paniers(std::vector<int>& vec)
{
    int min = *std::min_element(vec.begin(),vec.end());
    int max = *std::max_element(vec.begin(),vec.end());

    std::vector<int> paniers(max-min+1);

    for(unsigned int i = 0; i < vec.size(); ++i)
        paniers[vec[i]-min]++;

    for(unsigned int i=0, c=0; i < paniers.size(); ++i)
        for(int j=0; j < paniers[i]; ++j, ++c)
            vec[c] = i+min;
}

void print_int(int n)
{
    std::cout << n << std::endl;
}

int alea()
{
    return rand()%10;
}

int main()
{
    srand(time(NULL));

    /* On crée un vecteur de 10 entiers, et on le remplit du
    début (vec.begin()) à la fin (vec.end()) avec des nombres
    pseudo-aléatoires (tirés avec la fonction alea). */
    std::vector<int> vec(10);
    std::generate(vec.begin(),vec.end(),alea);

    /* On affiche chaque entier du vecteur, du début à la fin. */
    std::for_each(vec.begin(),vec.end(),print_int);

    std::cout << "Appuyez sur entrée pour débuter le tri...";
    getchar();

    /* On appelle le tri à paniers sur le vecteur, puis on le
    réaffiche, trié cette fois ci. */
    tri_paniers(vec);
    std::for_each(vec.begin(),vec.end(),print_int);

    return EXIT_SUCCESS;
}

À titre d'exemple, voici une autre implémentation du tri à paniers, écrite cette fois-ci par bluestorm en OCaml, un des langages fonctionnels les plus connus :
Code : OCaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let tri_hist tab =
  let maxi, mini = 
    let compare (m, m') e = max m e, min m' e in
    Array.fold_left compare (tab.(0), tab.(0)) tab in
  let paniers = Array.make (maxi - mini + 1) 0 in
  Array.iter (fun x -> paniers.(x - mini) <- paniers.(x - mini) + 1) tab;
  let k = ref 0 in
  let replace_paniers indice taille =
    Array.fill tab !k taille (indice + mini);
    k := !k + taille in
  Array.iteri replace_paniers paniers;
  tab

Petits calculs de complexité

Nous allons donc maintenant parler de la complexité de cet algorithme. Pour ceux qui ne connaissent pas, il s'agit de savoir combien d'opérations on doit faire pour trier un tableau de N chiffres. La complexité sert à évaluer la vitesse d'exécution d'un algorithme sur telle machine, mais aussi - et surtout - à comparer les algorithmes entre eux.

Le problème est qu'évaluer très précisément le nombre d'opérations effectuées est long et difficile. On va donc se contenter de calculer un ordre de grandeur, une variation, qui nous dira : « si vous doublez la longueur du tableau à trier, le temps d'exécution quadruplera en moyenne » (par exemple).

Cette approche nous évite en outre d'avoir un nombre qui dépend de la machine sur laquelle on exécute le programme. En effet, si vous avez un ordinateur deux fois plus rapide que le mien, l'algorithme mettra deux fois moins de temps à s'exécuter chez vous : les mesures de temps que je pourrais vous donner ne vous serviraient donc à rien. Par contre, si je vous dit que le temps d'exécution croît comme le nombre d'entiers à trier, vous pourrez estimer le temps qu'il vous faudra pour trier vos chaussures par pointure.

Pour estimer cet ordre de grandeur, on néglige les instructions, très rapides à exécuter sur les machines récentes. On donc va surtout s'intéresser aux boucles, car ce sont elles qui monopolisent la majorité du temps d'exécution. Et, pour faire simple, on va définir qu'un tour de boucle vaut 1 opération. C'est la référence. Comme les boucles contiennent des instructions classiques qui sont super-rapides, il n'y a pas beaucoup de différence de l'une à l'autre (au niveau du temps), sauf dans le cas d'une boucle dans une boucle. Mais c'est encore une autre histoire :) .

Revenons maintenant au tri à paniers. Imaginons que nous lui donnions un tableau de 20 chiffres. Que va-t-il se passer ?

  • Il va déterminer le minimum et le maximum possibles dans le tableau. Il va donc parcourir une fois tout le tableau : il va faire 20 premières itérations.
  • Il va allouer et initialiser un tableau de (max-min)+1 éléments. On a donc une boucle de (max-min)+1 tours qui se fait.
  • Il va ensuite parcourir le tableau pour "remplir les paniers". Il parcourt alors le tableau du début à la fin : encore 20 itérations.
  • Enfin, il va parcourir tous les paniers pour "remplir" le tableau. Comme il y a autant d'éléments au début et à la fin du tri, il y a donc, pour cette étape, autant d'itérations que de cases dans le tableau : 20.


Ainsi, on a 3*20 + (max-min)+1 tours de boucle, soit 60+tailleIntervalle.
Dans le cas général, on remplace 20 par n'importe quel longueur N, et on dit que pour trier un tableau de N chiffres, il faut 3*N + (max-min)+1 tours de boucle. C'est une première estimation.

Cependant, ceci est encore trop « précis ». En effet, les complexités servent surtout à évaluer le comportement des algorithmes quand N devient très, très grand (plusieurs millions). À ce moment, le « fois 3 » devient ridicule devant N : on peut donc le négliger, de même que le « plus 1 » à la fin. Sur une machine moderne très rapide, on ne sentira pas la différence.

Petite parenthèse : si vous voulez vous amuser à calculer la complexité d'un algorithme récursif, la procédure que j'ai décrite ici ne fonctionne plus, vu que la notion de « boucle » n'a plus vraiment de sens. Il faut alors compter 1 récursion = 1 opération. Fin de la petite parenthèse.

Donc, quand on a 3*N tours de boucles, on "arrondit" tout ça à N, tout court. Ce qui nous donne une complexité de N + (max-min). En effet, on ne néglige pas le max-min, car, comme N, il peut devenir très, très grand dans certains cas.

On note donc que cet algorithme a une complexité O(N + (max-min)). Ce qui fait que la complexité augmente avec le nombre d'éléments et la disparité de la liste.

C'est relativement peu efficace par rapport aux autres algorithmes, notamment sur des listes tres disparates. Mais face à une liste d'éléments très proches les uns des autres (par exemple, les âges d'une classe), il peut servir. Cet algorithme peut même parfois se révéler bien plus rapide que les tris « classiques ».

Il faut aussi bien voir qu'un algorithme de tri dépendant de l'amplitude (différence entre le plus grand et le plus petit élément) de la liste considérée est assez original : la plupart ne dépendent que de la longueur de la liste. C'est un avantage ou un inconvénient selon les cas.
Voilà ! Vous savez maintenant trier des entiers avec cette nouvelle méthode, il ne vous reste plus qu'à l'implémenter :) . Vous pouvez trouver plus d'informations sur l'article Wikipédia. Enfin, un ultime détail : il faut savoir que cet algorithme se nomme également tri par dénombrement, ou tri par comptage.

Pour toute question, remarque, jet de tomates, les commentaires sont ouverts.

Partager

17 commentaires pour "Le tri à paniers"
Note moyenne : 3.75 / 4 (4 votes)
Pseudo Commentaire
Hors ligne galopin_ # Posté le 22/03/2009 à 15:37:42

Allez parceque c'est vous, voila un code (C++) du radix sort, il marche peut importe les valeurs, sur des entiers et des floatant et est de loin plus rapide qu'un trie classique!

Code : C++
  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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#pragma	once

#ifndef __RADIXSORT_H__
#define __RADIXSORT_H__

#define LITTLE_ENDIAN
namespace radix {

    // utility meta function to know type attributes
    template <typename T> inline bool IsFloat();
    template <typename T> inline bool IsSigned();

    // two floating types.
    template <>           inline bool IsFloat<float>()     { return true;}
    template <>           inline bool IsFloat<double>()    { return true;}

    // others are integers.
    template <typename T> inline bool IsFloat()            { return false;}


    // floating types are signed.
    template <> inline bool IsSigned<float>()              { return true; };
    template <> inline bool IsSigned<double>()             { return true; };

    // unsigned types.
    template <> inline bool IsSigned<unsigned short>()     { return false; };
    template <> inline bool IsSigned<unsigned int>()       { return false; };
    template <> inline bool IsSigned<unsigned long>()      { return false; };
    template <> inline bool IsSigned<unsigned long long>() { return false; };
    
    // signed types.
    template <> inline bool IsSigned<signed short>()       { return true; };
    template <> inline bool IsSigned<signed int>()         { return true; };
    template <> inline bool IsSigned<signed long>()        { return true; };
    template <> inline bool IsSigned<signed long long>()   { return true; };
    
    // we need to fix endianess ordering.
    // LE: LSB to MSB, BE: MSB to LSB
    template< typename T>
    inline unsigned int FixMsbEndianess( unsigned int b)
    {
        #ifdef LITTLE_ENDIAN
        return b;
        #else
        return sizeof(T)-1-b;
        #endif
    }

    ///////////////////////////////////////////////////////////////////////////////////
    // Radix sort
    // in: input is the array data to sort
    // in: nb is the array data size
    // out: output, will contains sorted indices
    template < typename T, typename IndexT>
    void Sort(const T* input, unsigned int nb, IndexT* output)
    {
        // Checkings
        if(!input || !output || !nb)	return;

        IndexT* output2 = (IndexT*)alloca(sizeof(IndexT)*nb);

        // Allocate histograms & offsets on the stack
        unsigned int mHistogram[256*sizeof(T)];

        // Create histograms (counters). Counters for all passes are created in one run.
        // Pros:	read input buffer once instead of four times
        // Cons:	mHistogram is sizeof(T)*256 instead of 1Kb

        /* Clear counters */
        memset(mHistogram, 0, 256*sizeof(T)*sizeof(unsigned int));												
        
        unsigned int* h0,*h1,*h2,*h3,*h4,*h5,*h6,*h7;        
        /* Prepare to count */		
        h0= &mHistogram[256*0];		    
        h1= &mHistogram[256*1];	        
        if (sizeof(T) > 2)
        {
            h2= &mHistogram[256*2];	    
            h3= &mHistogram[256*3];	    
        }
        if (sizeof(T) == 8)
        {
            h4= &mHistogram[256*4];	    
            h5= &mHistogram[256*5];	
            h6= &mHistogram[256*6];	
            h7= &mHistogram[256*7];	
        }

        // count each byte in the associate histogram
        for (unsigned int idx = 0;idx<nb;++idx)
        {																							
            /* Read input input in previous sorted order */										
            const T& Val = (T)input[idx];			
            unsigned char* p = (unsigned char*)&Val;	

            /* Create histograms */																	
            h0[*p++]++;	h1[*p++]++;	
            if (sizeof(T) > 2)
            {
                h2[*p++]++;	h3[*p++]++;											
            }
            if (sizeof(T) == 8)
            {
                h4[*p++]++;	h5[*p++]++;	h6[*p++]++;	h7[*p++]++;											
            }
        }				

        // Compute #negative values involved if needed
        unsigned int NbNegativeValues = 0;
        if (IsSigned<T>())
        {
            // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
            // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
            // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
            unsigned int* h_msb;
            h_msb = &mHistogram[256*FixMsbEndianess<T>(sizeof(T)-1)];

            for(unsigned int i=128;i<256;i++)	NbNegativeValues += h_msb[i];	// 768 for last histogram, 128 for negative part
        }

        // we'll swap buffer for each byte done.
        IndexT* outputDst = output2;
        IndexT* inputSrc = output;

        // Radix sort, j is the pass number (0=LSB, sizeof(T)-1=MSB)
        for(unsigned int j=0;j<sizeof(T);j++)
        {
            unsigned int* CurCount = &mHistogram[ FixMsbEndianess<T>(j) * 256 ];													

            /* Check pass validity */																	

            /* If all values have the same byte, sorting is useless. */									
            /* It may happen when sorting bytes or words instead of dwords. */							
            /* This routine actually sorts words faster than dwords, and bytes */						
            /* faster than words. Standard running time (O(4*n))is reduced to O(2*n) */					
            /* for words and O(n) for bytes. Running time for floats depends on actual values... */		

            unsigned char UniqueVal;
            /* Get first byte */	
            UniqueVal = *(((unsigned char*)input)+FixMsbEndianess<T>(j));		

            /* Reset flag. The sorting pass is supposed to be performed. (default) */					
            /* Check that byte's counter, j == 0 must be done to initialize output */																
            unsigned int PerformPass = (j == 0) || CurCount[UniqueVal]!=nb;

            // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is
            // not a problem, numbers are correctly sorted anyway.
            if(PerformPass)
            {
                unsigned int mOffset[256];
                
                // Should we care about negative values? only for the msb
                if ( !IsSigned<T>()  || j!=sizeof(T)-1 )
                {
                    // use a register, fix memory load hit store issue
                    unsigned int lastOffset = 0;

                    // Here we deal with positive values only
                    mOffset[0] = 0;

                    // Create offsets
                    for(edU32 i=1;i<256;i++)
                        lastOffset = (mOffset[i] = lastOffset + CurCount[i-1]);
                }
                else
                {
                    // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place.

                    // use a register, fix memory load hit store issue
                    unsigned int lastOffset = NbNegativeValues;

                    // Create biased offsets, in order for negative numbers to be sorted as well
                    mOffset[0] = NbNegativeValues;	
                    // First positive number takes place after the negative ones
                    for(unsigned int i=1;i<128;i++)		
                        lastOffset = (mOffset[i] = lastOffset + CurCount[i-1]);	// 1 to 128 for positive numbers

                    // floating type case differ from integer
                    if (!IsFloat<T>())                    
                    {
                        // use a register, fix memory load hit store issue
                        unsigned int lastOffset = 0;

                        // Fixing the wrong place for negative values
                        mOffset[128] = 0;

                        for(unsigned int i=129;i<256;i++)			
                            lastOffset = (mOffset[i] = lastOffset + CurCount[i-1]);
                    }
                    else
                    {
                        // use a register, fix memory load hit store issue
                        unsigned int lastOffset = 0;

                        // We must reverse the sorting order for negative numbers!
                        mOffset[255] = 0;

                        for(unsigned int i=0;i<127;i++)		
                            lastOffset = (mOffset[254-i] = lastOffset + CurCount[255-i]);	// Fixing the wrong order for negative values
                        for(unsigned int i=128;i<256;i++)	
                            mOffset[i] += CurCount[i];				// Fixing the wrong place for negative values
                    }
                }

                // Perform Radix Sort
                unsigned char* InputBytes	= (unsigned char*)input;

                InputBytes += FixMsbEndianess<T>(j);

                // LSB is special, we initialize fake previous ordering
                if (j==0)
                {
                    for (unsigned int idx = 0;idx<nb;++idx)
                        outputDst[mOffset[ InputBytes[idx*sizeof(T)] ]++] = idx;
                }
                else
                {
                    // common case, only MSB of floating type need something else
                    if (!IsFloat<T>() || j!=sizeof(T)-1)
                    {
                        for (unsigned int idx = 0;idx<nb;++idx)
                        {
                            unsigned int real_idx = inputSrc[idx];
                            outputDst[mOffset[ InputBytes[real_idx*sizeof(T) ]]++] = real_idx;
                        }
                    }
                    else
                    {
                        // Perform Radix Sort
                        for(unsigned int i=0;i<nb;i++)
                        {
                            unsigned int real_idx = inputSrc[i];

                            unsigned int Radix = (*((unsigned int*)input+real_idx))>>24;								// Radix byte, same as above. AND is useless here (edU32).
                            // ### cmp to be killed. Not good. Later.
                            if(Radix<128)		outputDst[mOffset[Radix]++] = real_idx;		// Number is positive, same as above
                            else				outputDst[--mOffset[Radix]] = real_idx;		// Number is negative, flip the sorting order
                        }
                    }
                }

                // Swap pointers for next pass. Valid indices - the most recent ones - are in mIndices after the swap.
                std::swap(outputDst,inputSrc);
            }
            else
            {
                // The pass is useless, yet we still have to reverse the order of current list if all values are negative.
                if(IsFloat<T>() && (j==sizeof(T)-1) && UniqueVal>=128)
                {
                    for(unsigned int i=0;i<nb;i++)	outputDst[i] = inputSrc[nb-i-1];

                    // Swap pointers for next pass. Valid indices - the most recent ones - are in mIndices after the swap.
                    std::swap(outputDst,inputSrc);
                }
            }
        }

        // may happen if not all byte need sorting.
        if (inputSrc != output)
        {
            memcpy(output,inputSrc,sizeof(IndexT)*nb);
        }
    }
}
#endif
Hors ligne GuilOooo # Posté le 22/03/2009 à 16:48:42
Attention, je mords !
Avatar
Modérateurs

Intéressant. Je n'ai pas testé pour le moment, faute de temps, mais j'ai tout de même une remarque : le radix sort trie les valeurs chiffre par chiffre, d'abord par unités, puis par dizaines, etc, de manière stable.
Ce n'est pas la même chose qu'un tri par comptage, ou tri par paniers.

HaskellReal World HaskellLearn yourself HaskellGentille intro à HaskellTuto SdZ
ErlangBuzzerl@home???

Modérateur spécialiste du langage C.
Bien poster sur le forum de CMe contacter
 
Hors ligne galopin_ # Posté le 28/03/2009 à 14:40:36

Bien sur que si, le tri panier n'est qu'une spécialisation du radix, ou la base (radix) est déjà supérieur à ton range de valeur :)

Et en informatique, oublie les dizaines, centaines and co, on travaille en base 2 :D ici on tri par octet!
Hors ligne elmcherqui # Posté le 05/12/2009 à 03:28:49
la vie est un programme
Avatar

Ville : Casablanca
Pays : Maroc
Études : SUPINFO Maroc à Casablanca

galopin a raison le tri radix utilise le tri panier pour trier chaque chiffre vu que c'est decimal donc petit intervalle c'est tous benef .
par contre tu peux choisir la base avec laquelle tu veux trier c'est pas forcement par octet ni tri par bit , generalement la decimale fait largement l'affaire ,parceque avec le binaire ce que tu va gagner en long tu va le perdre en large .
pour choisir la base ideale c'est base = log nombre_element . (log de base deux ) .

- La répétition est humaine , la récurrence Divine .
- il faut être fou pour ne pas utiliser la récursivité quand il le faut !

 
Hors ligne quelqu'un du 86 # Posté le 05/08/2011 à 11:13:22
printf("Hello Zeros !\n");
Avatar

Très bon tutoriel,
simple accessible complet.

:p Il faut manger pour vivre mais pas vivre pour manger :p

#LGDF: M@teo21 vaincra !
 

Voir tous les commentaires
Ce tutoriel a été corrigé par les zCorrecteurs.