OVH Cloud OVH Cloud

Routine de trie pour une liste de structure.

39 réponses
Avatar
Zorro
Bonjour,

Je voudrais trier par ordre alphabethique( toto->name) une liste de
structure se présentant sous cette forme:

typedef struct _toto
{
..blabla..
char *name;
struct _toto *next;
}toto;

La mémoire de chaques structures a été alloué avec un malloc, je ne suis
donc pas dans le cas d'une liste de structure stocké dans un tableau. (
genre struct toto[xx];), de ce fait, je ne peux utiliser qsort().

Quelqu'un pourait-il me proposer une routine rapide pour ce traitement?

Merci d'avance.

Zorro
--
Webpage: http://zorro.arcadia-crew.com
Blog: http://zorro-arcadia.blogspot.com

10 réponses

1 2 3 4
Avatar
Antoine Leca
En <news:,
Jean-Marc Bourguet va escriure:
Zorro writes:
Zorro wrote:
Je voudrais trier
man qsort



qsort() directement avec une liste chainé? on sent le professionel..


Pourquoi pas?


Je crois que « Zorro » a bien trouvé le mot : on sent le professionnel ;
mais on ne sait pas s'il s'agit d'un professionnel « de base » qui applique
quelques recettes bien basiques à toutes les sauces, un gâte-sauce donc ; ou
bien d'un professionnel « de pointe » qui sait où il va (voire plus loin), a
analysé les pours et les contres, a pesé des arguments auxquels personne
n'avait pensé jusque là, en bref, est un vrai chef (de cuisine).

Les professionnels ne donnent que des avis valables en général, par manque
d'informations sur le contexte.


Antoine




Avatar
Richard Delorme
"Richard Delorme" wrote in message
news:4324794b$0$303$


Richard Delorme wrote:


Ecrire un algorithme de tri en C, ce n'est quand même pas la mer à
boire. Adapter les structures de données à qsort doit sans doute être
aussi compliqué (en nombre de lignes) que réécrire (ou recopier parce
que le web regorge d'exemples déjà écrit) de novo un algorithme de
tri.


Je ne suis pas d'accord, cela doit faire une 30aine de lignes de C alors
qu'implémenter un algorithme demandrait bien plus.


20 lignes pour celui ce tri par insertion (il trie des coups du meilleur
au plus mauvais pour un jeu d'othello) :

void movelist_sort(MoveList *movelist) {
Move *i, *pi, *j, *pj;

pi = movelist->move->next;
while ((i = pi->next) != NULL) {
pj = movelist->move;
do {
j = pj->next;
if (j->val < i->val) {
pj->next = i;
pi->next = i->next;
i->next = j;
i = pi;
break;
}
pj = j;
} while (pj != pi);
pi = i;
}
}


Ceci dit, développez un tri sur les listes chaînées en implémentant un
algo et venez comparer ce que l'on peut faire avec qsort(), il n'est
pas certain que vous gagniez une micro-seconde de CPU chaque année, il
me semble clair que vous y passeriez un certain temps.


La fonction que j'ai écrite ci-dessus est appelée des centaines de
milliers de fois chaque seconde par un programme qui tourne 24 heures
sur 24, 7 jours sur 7. Par rapport à qsort, le gain CPU est de plusieurs
mois chaque année.



Cela dépend énormément des données : ton algorithme a une complexité
quadratique, ce qui signifie qu'il prend un temps proportionnel au carré de la
longueur de la liste, le cas le pire étant celui de la liste déjà triée (!).


Possible, mais le pire cas est excessivement rare.

En
revanche la complexité de qsort est n * log(n) ce qui est bien plus favorable
pour des valeurs de n suffisamment grandes. Quelle est en moyenne la taille de
tes listes ?


environ 10, au maximum (très rare) 32, au minimum 1. Le fait que qsort()
doit appeler une fonction de comparaison externe le ralentit beaucoup en
pratique.

Note de plus que ton implémentation plante si la liste est vide.


Impossible, on a toujours un coup à jouer (passer son tour est un coup
légal dans mon implémentation).

La boucle do/while n'a aucun intérêt : une boucle while ou for serait plus
efficace.
Enfin il est facile d'accélérer le cas pathologique de la liste déjà
partiellement ou totalement triée.

void movelist_sort(MoveList *movelist) {
Move *i, *pi, *j, *pj;

pi = movelist->move->next;
if (pi == NULL)
return;
for (; (i = pi->next) != NULL; pi = i) {
if (i->val >= pi->val)
continue;
for (pj = movelist->move; (j = pj->next) != i; pj = j) {
if (j->val < i->val) {
pj->next = i;
pi->next = i->next;
i->next = j;
i = pi;
break;
}
}
}
}

A toi de voir si cela change les performances.


En effet. Sur une série de position de tests tirée d'une revue de la
fédération française d'Othello :
Avant :
fforum-20-39.scr : 138939725 nodes in 00:00:29.9, 4634809 N/s
Après, avec l'algorithme modifié de Chqrlie Gordon :
fforum-20-39.scr : 2400849827 nodes in 00:06:20.9, 6301574 N/s
C'est 13 fois plus lent...
Il y a une erreur dans ton exemple, il ne trie pas les coups... En
corrigeant le « if (i->val >= pi->val) » en « if (i->val <= pi->val) »
et en supprimant l'inutile test de vide liste, on obtient les mêmes
performances qu'avant.

--
Richard




Avatar
Richard Delorme
Richard Delorme wrote:


20 lignes pour celui ce tri par insertion



Oui, il n'est pas très aéré


Les lignes d'aération ne sont pas compté par les programmes qui comptent
les lignes de codes *significatives*.

et il est adapté à une structure particulière, un ordre de tri figé etc.


Oui, mais je suis particulièrement malchanceux, car je suis toujours
confronté à des situations particulières, jamais à des situations
générales ou les algorithmes génériques (comme qsort) sont adaptés.

Vous jouez trop à othello.


C'est pas moi, c'est mon ordinateur ;-)

Par rapport à qsort, le gain CPU est de
plusieurs mois chaque année.


Ca ressemble à un bubble sort, c'est un excellent algo de tri pour les
listes qui font 1 ou 2 éléments.


Le tri par insertion est réputé très efficace jusqu'à une dizaine
d'éléments. Il fait aussi le tri en-place (ce qui est une exigence dans
le cas de mon programme), ce que ne fait pas le qsort.

--
Richard


Avatar
Harpo
Jean-Marc Bourguet wrote:

qsort() directement avec une liste chainé? on sent le professionel..


Pourquoi pas? On va garder uniquement les cles et les pointeurs vers
l'enregistrement en batissant le tableau,


Il n'est pas absolument nécessaire de mettre les clés puisqu'elles sont
accessible via le bointeur, mais c'est une bonne idée de mettre la clé
si c'est un integer ou un pointeur vers une chaîne de char, cela évite
une indirection.


Avatar
Jean-Marc Bourguet
Harpo writes:

Jean-Marc Bourguet wrote:

qsort() directement avec une liste chainé? on sent le professionel..


Pourquoi pas? On va garder uniquement les cles et les pointeurs vers
l'enregistrement en batissant le tableau,


Il n'est pas absolument nécessaire de mettre les clés puisqu'elles sont
accessible via le bointeur, mais c'est une bonne idée de mettre la clé
si c'est un integer ou un pointeur vers une chaîne de char, cela évite
une indirection.


L'idee etait de ne pas tracher les caches, entrees de TLB et de ne pas
swapper (Un probleme des listes chainees en ce qui concerne les perf,
c'est que ca peut etre tres disperse), donc la copie des cles pour les
avoir toutes au meme endroit et pas faire des bonds partout. En plus
les cles dans des enregistrements, ca a tendance a faire de la
resonnance dans les caches s'ils sont "direct mapped".

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
Harpo
Antoine Leca wrote:

un vrai chef (de cuisine).


Je vous conseille 'l'algorithmique du goût' de Brillat-Savarin.

Les professionnels ne donnent que des avis valables en général, par
manque d'informations sur le contexte.


Ëtre pro, ce serait reconnaître ses limites ? Ca dépend pro en quoi ! Je
me suis laissé dire que ça peut aussi être l'inverse, il suffit de
savoir pédaler - ou faire pédaler - derrière.
'Vos problèmes sont seulement là pour que nous proposions nos
solutions.'

Avatar
Emmanuel Delahaye
Richard Delorme wrote on 12/09/05 :
Les lignes d'aération ne sont pas compté par les programmes qui comptent les
lignes de codes *significatives*.


La notion de ligne de code n'existe pas en C (C != Assembleur). On
mesure plutôt le nombre d'instructions.


--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"Mal nommer les choses c'est ajouter du malheur au
monde." -- Albert Camus.

Avatar
Harpo
Jean-Marc Bourguet wrote:

Il n'est pas absolument nécessaire de mettre les clés puisqu'elles
sont accessible via le bointeur, mais c'est une bonne idée de mettre
la clé si c'est un integer ou un pointeur vers une chaîne de char,
cela évite une indirection.


L'idee etait de ne pas tracher les caches, entrees de TLB et de ne pas
swapper (Un probleme des listes chainees en ce qui concerne les perf,
c'est que ca peut etre tres disperse), donc la copie des cles pour les
avoir toutes au meme endroit et pas faire des bonds partout. En plus
les cles dans des enregistrements, ca a tendance a faire de la
resonnance dans les caches s'ils sont "direct mapped".


Voilà, le problème c'est de ne pas soupoudrer des accès mémpoire un peu
partout.et delimiter les accès à un cetain nombre restreint de pages.
Si généralement cela se passe bien pour un volume de données même assez
important, au delà d'un certain seuil ça va de mal en pis. les gens
alloue facilement un méga pour un oui ou pour un non, moi, je prends
256 octets et si le gars n'est pas content, je lui répond : "Hé ducon !
je te demandais juste de répondre 'y' ou 'n', je me fous de tes démélés
avec ta bourgeoise !"


Avatar
Harpo
Richard Delorme wrote:

Oui, il n'est pas très aéré


Les lignes d'aération ne sont pas compté par les programmes qui
comptent les lignes de codes *significatives*.


On voit que vous n'avez jamais été payé à la ligne, ces programmes
écrits par des programmeurs renégats ne sont qu'une incitation à les
truander et donc une perte de productivité.

Vous jouez trop à othello.


C'est pas moi, c'est mon ordinateur ;-)


S'il s'amuse c'est le principal.


Avatar
Antoine Leca
En <news:,
Emmanuel Delahaye va escriure:
La notion de ligne de code n'existe pas en C (C != Assembleur).


C'est bien connu que les projets sont mesurés en LOA...

On mesure plutôt le nombre d'instructions.


... que tu définis comment ?
Pour faire simple, est-ce que cela donne le même nombre de celui de
fonctions (puisque en C, chaque instruction est imbriquée dans une
instruction comme par exemple un bloc, et tout ceci remonte au niveau de la
fonction qui n'admet comme « instruction » que le type « bloc ».


Antoine

1 2 3 4