Faire correspondre des intervalles
Fermé
mslider
-
30 mars 2009 à 11:43
mslider Messages postés 109 Date d'inscription jeudi 21 octobre 2004 Statut Membre Dernière intervention 7 mars 2010 - 3 avril 2009 à 21:17
mslider Messages postés 109 Date d'inscription jeudi 21 octobre 2004 Statut Membre Dernière intervention 7 mars 2010 - 3 avril 2009 à 21:17
A voir également:
- Faire correspondre des intervalles
- Dans la présentation à télécharger, déplacez l'image dans le cadre sans en modifier la taille. redressez l'image pour que le niveau de la mer soit à l'horizontale. faites correspondre : la ligne avec le niveau de la mer ; le point avec le sommet de la grande voile. combien d'oiseaux sont dans le cadre ? ✓ - Forum Photofiltre
- Suivi de la camera 3d la taille du calque doit correspondre - Forum After Effects
- Petit probleme au niveau de l'affichage des images - Forum Mozilla Firefox
- Fonction si avec intervalles excel - Forum Programmation
- Si à plusieurs critères avec intervalles ✓ - Forum Bureautique
19 réponses
Salut,
J'ai une petite idée, à essayer : Commences par trier la liste d'intervalles de file1 dans l'ordre croissant de la borne inférieure (temps n*log(n)).
Ensuite, pour chaque intervalle [i..j] de file2.txt :
-Tu fais une recherche dichotomique, pour trouver l'intervalle de file1 qui a la borne inf la plus proche de i, tout en étant plus petite,
-Tant que l'intervalle trouvé a une borne sup inférieure à i, tu regarde le précédent dnas la liste.
Au pire cas, la complexité sera de n^2 * log(n), mais dans la pratique, ça devrait pas mal accélérer les choses je pense...
A voir...
J'ai une petite idée, à essayer : Commences par trier la liste d'intervalles de file1 dans l'ordre croissant de la borne inférieure (temps n*log(n)).
Ensuite, pour chaque intervalle [i..j] de file2.txt :
-Tu fais une recherche dichotomique, pour trouver l'intervalle de file1 qui a la borne inf la plus proche de i, tout en étant plus petite,
-Tant que l'intervalle trouvé a une borne sup inférieure à i, tu regarde le précédent dnas la liste.
Au pire cas, la complexité sera de n^2 * log(n), mais dans la pratique, ça devrait pas mal accélérer les choses je pense...
A voir...
Ben non, là tu fais une recherche linéaire... (ton while part de j=1 et s'incrémente à chaque tour de boucle...)
Par contre, je n'avais pas vu que tu recherchais tous les intervalles de file1 incluant [i,j] (j'avais dans l'idée de trouver un seul intervalle)... Donc, ma première solution ne résout pas grand chose.
Je ne pense pas qu'il soit possible d'améliorer grandement ton algo : au pire cas, tu dois générer M*N lignes (M étant le nombre d'intervalles de file1 et N celui de file2), donc ça ne sera pas possible de décendre en dessous d'une complexité quadratique dans le cas général.
Reste à voir après où ton script passe le plus de temps : peut être que si tu stockes tous les résultats en mémoire et que tu les écris d'un seul coup dans un fichier, ça accélèrera un peu les choses... (ça dépends de comment l'interpréteur AWK gère les entrées-sorties. A tester...)
Par contre, je n'avais pas vu que tu recherchais tous les intervalles de file1 incluant [i,j] (j'avais dans l'idée de trouver un seul intervalle)... Donc, ma première solution ne résout pas grand chose.
Je ne pense pas qu'il soit possible d'améliorer grandement ton algo : au pire cas, tu dois générer M*N lignes (M étant le nombre d'intervalles de file1 et N celui de file2), donc ça ne sera pas possible de décendre en dessous d'une complexité quadratique dans le cas général.
Reste à voir après où ton script passe le plus de temps : peut être que si tu stockes tous les résultats en mémoire et que tu les écris d'un seul coup dans un fichier, ça accélèrera un peu les choses... (ça dépends de comment l'interpréteur AWK gère les entrées-sorties. A tester...)
ben moi je stocke le file 1 en mémoire vive, le file 1 sert à annoter le fichier 2. Le fichier 2 est 8 fois plus gros que
le fichier 1.
ensuite je parcours les lignes du fichier 2 en bouclant de j=1 à j<=cpt pour chaque ligne.
j'ai essayer de stocker le fichier 2 en mémoire vive, et boucler dessus, çà change pas grand chose en terme
de vitesse, ya toujours MxN itérations à faire.
Peut être qu'en C ou C++ çà irait plus vite je sais pas. AwK c'est qd même un cousin du C.
le fichier 1.
ensuite je parcours les lignes du fichier 2 en bouclant de j=1 à j<=cpt pour chaque ligne.
j'ai essayer de stocker le fichier 2 en mémoire vive, et boucler dessus, çà change pas grand chose en terme
de vitesse, ya toujours MxN itérations à faire.
Peut être qu'en C ou C++ çà irait plus vite je sais pas. AwK c'est qd même un cousin du C.
Hmmm, peut être qu'en C, ça irait plus vite étant donné que awk reste un langage interprété, quoique les primitives de comparaison doivent être faites en dur... Là aussi, à tester...
Pour le fichier à stocker en mémoire, je ne pensais pas au fichier 2 mais aux lignes que tu génère. Quelque chose du genre :
cpt2=1
j=1
while(start[j]<=$2 && j<=cpt){
if(end[j]>=$3){a1[cpt2]=$1;a2[cpt2]=$2;a3[cpt2]=$3;a4[cpt2]=descr[j];a5[cpt2]=start[j];a6[cpt2]=end[j];j++;cpt2++}
else{j++}
}
et ensuite, soit faire un print des lignes générées, soit les écrire directement dans un fichier si awk le permet (je mise plus sur cette seconde solution pour accélérer les choses. L'idée étant de réduire le temps passé à faire des entrées sorties).
Ca ne réduit pas la complexité générale, mais ça peut aider à repousser un peu la limite de l'algo en divisant son temps d'exécution par une petite constante... (tout comme le fait de le coder en C).
Pour le fichier à stocker en mémoire, je ne pensais pas au fichier 2 mais aux lignes que tu génère. Quelque chose du genre :
cpt2=1
j=1
while(start[j]<=$2 && j<=cpt){
if(end[j]>=$3){a1[cpt2]=$1;a2[cpt2]=$2;a3[cpt2]=$3;a4[cpt2]=descr[j];a5[cpt2]=start[j];a6[cpt2]=end[j];j++;cpt2++}
else{j++}
}
et ensuite, soit faire un print des lignes générées, soit les écrire directement dans un fichier si awk le permet (je mise plus sur cette seconde solution pour accélérer les choses. L'idée étant de réduire le temps passé à faire des entrées sorties).
Ca ne réduit pas la complexité générale, mais ça peut aider à repousser un peu la limite de l'algo en divisant son temps d'exécution par une petite constante... (tout comme le fait de le coder en C).
awk est un langage interprété écrit en C par en grosse partie Brian Kernighan qui n'est autre que le papa du langage C avec Denis Ritchie.
Awk utilise des appels système en C.
oui effectivement awk peut ecrire directement dans un fichier à partir du code:
if(end[j]>=$3){print $1,$2,$3,descr[j],start[j],end[j] >> "sortie.txt" ;j++}
une autre parade serait aussi de découper le fichier 2, çà diminuerait la taille de la boucle, mais bon c vraiment
une solution empirique.
Et moi qui pensait que ce cas d'école serait vite résolu...c peut être pas un cas d'école tout compte fait.
Awk utilise des appels système en C.
oui effectivement awk peut ecrire directement dans un fichier à partir du code:
if(end[j]>=$3){print $1,$2,$3,descr[j],start[j],end[j] >> "sortie.txt" ;j++}
une autre parade serait aussi de découper le fichier 2, çà diminuerait la taille de la boucle, mais bon c vraiment
une solution empirique.
Et moi qui pensait que ce cas d'école serait vite résolu...c peut être pas un cas d'école tout compte fait.
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
31 mars 2009 à 21:03
31 mars 2009 à 21:03
Le mieux c'est de faire un segment tree.
https://en.wikipedia.org/wiki/Segment_tree
1) Tu lis le premier fichier tu en construis (build) un segment tree (O(n.log(n)) pour n intervalles dans le 1er fichier.
2) Ensuite tu lis le second fichier et tu interroges (query) ton arbre (O(m.log(n))) pour m intervalles dans le 2e fichier.
Bonne chance
https://en.wikipedia.org/wiki/Segment_tree
1) Tu lis le premier fichier tu en construis (build) un segment tree (O(n.log(n)) pour n intervalles dans le 1er fichier.
2) Ensuite tu lis le second fichier et tu interroges (query) ton arbre (O(m.log(n))) pour m intervalles dans le 2e fichier.
Bonne chance
merci beaucoup pour ton renseignement,
à l'heure actuelle j'ai essayé en utilisant une fonction dichotomie, çà accélère pas mal mais pas assez, vu que le
nombre de lignes est assez important.
Les segments tree je connais pas, j'ai jamais utilisé, tu pourrais me donner un exemple en utilisant les deux listes
d'intervalles que j'ai donné dans mon premier post ?
merci encore.
à l'heure actuelle j'ai essayé en utilisant une fonction dichotomie, çà accélère pas mal mais pas assez, vu que le
nombre de lignes est assez important.
Les segments tree je connais pas, j'ai jamais utilisé, tu pourrais me donner un exemple en utilisant les deux listes
d'intervalles que j'ai donné dans mon premier post ?
merci encore.
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
31 mars 2009 à 21:37
31 mars 2009 à 21:37
Regarde sur wikipedia il y a un exemple et l'algorithme.
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
31 mars 2009 à 21:44
31 mars 2009 à 21:44
Euh... Et ça ?
http://upload.wikimedia.org/wikipedia/en/e/e5/Segment_tree_instance.gif
Les intervalles s_i sont dessinés en vert (6 intervalles dans l'exemple).
Les bornes des intervalles p_i sont en noir.
Les feuilles (leaf) de l'arbre sont en bleus, les autre noeuds (node) en rouge.
Bonne chance
http://upload.wikimedia.org/wikipedia/en/e/e5/Segment_tree_instance.gif
Les intervalles s_i sont dessinés en vert (6 intervalles dans l'exemple).
Les bornes des intervalles p_i sont en noir.
Les feuilles (leaf) de l'arbre sont en bleus, les autre noeuds (node) en rouge.
Bonne chance
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
31 mars 2009 à 22:03
31 mars 2009 à 22:03
Tu n'y mets pas du tien mon poussin. Imagine que dans ton premier fichier tu aies les intervalles
Le segment tree correspondant est celui que tu as sur wikipedia.
Un noeud modélise l'intervalle délimité par sa feuille la plus à gauche (borne inférieure) et sa borne la plus à droite (borne supérieur). Un intervalle modélisé par un noeud est appelé intervalle canonique.
Un noeud t est taggué comme compris dans un intervalle s_i si
- son père n'est pas inclu dans l'intervalle s_i
- ce noeud modélise un intervalle inclu dans s_i
Un intervalle s_i est une union d'intervalles canoniques. Chacun des intervalles canoniques constituant cette union est estampillée d'un tag s_i.
Tu peux pour chaque noeud écrire un vecteur v de taille n (le nombre d'intervalles stockés dans le segment tree) tel v[i] = 1 si et seulement si s_i est représenté à l'aide de l'intervalle canonique représenté par le noeud.
Essaye déjà de bien comprendre ça mais ne te leur pas, pour améliorer la complexité d'un programme, il faut toujours se gratter la tête en choisissant subtilement sa structure de donnée, et en plus ce n'est pas toujours possible.
Une fois que tu auras compris comment marche la recherche d'un point p dans l'arbre (comment placer p dans l'arbre) on peut attaquer l'inclusion d'un intervalle I = [a,b].
1) on recherche les intervalles qui contiennent a (union des intervalles rencontrés jusqu'à trouver la feuille décrivant la plus grande borne canonique borne inférieure à a),
2) on recherche qui contiennent b (union des intervalles rencontrés jusqu'à trouver la feuille décrivant la plus grande borne canonique borne inférieure à b),
3) on fait l'intersection de ses deux ensembles.
En C des unions d'intervalles se font très facilement avec des masques, ie des champs de bits. Si tu as n intervalles dans ton premier fichier il faut donc un champ de n bits.
- pour faire une union : opérateur |,
- pour faire une intersection : opérateur &.
Supposons qu'on cherche les intervalles s_i inclus dans I, et que chaque ensemble s_i est associé au ième bit. On cherche donc un ensemble d'ensemble et on va le décrire grâce à un champ de bits. Au début cet ensemble est vide, donc chacun des bits est nuls. En d'autres termes :
Si le type de x est un type de base (par exemple un int si tu as besoin d'un champ de 32 bits soit au plus 32 intervalles s_i) tu peux directement écrire :
Ensuite pour rajouter s_i dans cet ensemble (union) :
Je te rappelle que 2 << i désigne le champ de bit ou tous les bits sont nul sauf le ième. Cette valeur doit être castée dans le type de x.
Pour faire l'intersection de deux ensembles :
Pour faire l'union de deux ensembles :
On verra plus en détail quand tu auras compris ce message.
Bonne chance
s1 = ]-infini,1] s2 = [1,3] s3 = [1,7] s4 = [3,4] s5 = [4,5] s6 = [3,6]
Le segment tree correspondant est celui que tu as sur wikipedia.
Un noeud modélise l'intervalle délimité par sa feuille la plus à gauche (borne inférieure) et sa borne la plus à droite (borne supérieur). Un intervalle modélisé par un noeud est appelé intervalle canonique.
Un noeud t est taggué comme compris dans un intervalle s_i si
- son père n'est pas inclu dans l'intervalle s_i
- ce noeud modélise un intervalle inclu dans s_i
Un intervalle s_i est une union d'intervalles canoniques. Chacun des intervalles canoniques constituant cette union est estampillée d'un tag s_i.
Tu peux pour chaque noeud écrire un vecteur v de taille n (le nombre d'intervalles stockés dans le segment tree) tel v[i] = 1 si et seulement si s_i est représenté à l'aide de l'intervalle canonique représenté par le noeud.
Essaye déjà de bien comprendre ça mais ne te leur pas, pour améliorer la complexité d'un programme, il faut toujours se gratter la tête en choisissant subtilement sa structure de donnée, et en plus ce n'est pas toujours possible.
Une fois que tu auras compris comment marche la recherche d'un point p dans l'arbre (comment placer p dans l'arbre) on peut attaquer l'inclusion d'un intervalle I = [a,b].
1) on recherche les intervalles qui contiennent a (union des intervalles rencontrés jusqu'à trouver la feuille décrivant la plus grande borne canonique borne inférieure à a),
2) on recherche qui contiennent b (union des intervalles rencontrés jusqu'à trouver la feuille décrivant la plus grande borne canonique borne inférieure à b),
3) on fait l'intersection de ses deux ensembles.
En C des unions d'intervalles se font très facilement avec des masques, ie des champs de bits. Si tu as n intervalles dans ton premier fichier il faut donc un champ de n bits.
- pour faire une union : opérateur |,
- pour faire une intersection : opérateur &.
Supposons qu'on cherche les intervalles s_i inclus dans I, et que chaque ensemble s_i est associé au ième bit. On cherche donc un ensemble d'ensemble et on va le décrire grâce à un champ de bits. Au début cet ensemble est vide, donc chacun des bits est nuls. En d'autres termes :
memset(&x,0,sizeof(x));
Si le type de x est un type de base (par exemple un int si tu as besoin d'un champ de 32 bits soit au plus 32 intervalles s_i) tu peux directement écrire :
x = 0;
Ensuite pour rajouter s_i dans cet ensemble (union) :
x |= 2 << i; // x = x | (2 << i)
Je te rappelle que 2 << i désigne le champ de bit ou tous les bits sont nul sauf le ième. Cette valeur doit être castée dans le type de x.
Pour faire l'intersection de deux ensembles :
z = x & y;
Pour faire l'union de deux ensembles :
z = x | y;
On verra plus en détail quand tu auras compris ce message.
Bonne chance
mslider
Messages postés
109
Date d'inscription
jeudi 21 octobre 2004
Statut
Membre
Dernière intervention
7 mars 2010
31 mars 2009 à 22:18
31 mars 2009 à 22:18
tu parles, d'un poussin, je crois que coté programmation je ne suis pas encore sorti de l'oeuf !
tu penses que les segments tree seraient plus rapide que çà:
Intervale_fic_1 = premier intervalle du fichier 1
Intervale_fic_2 = premier intervalle du fichier 2
tant que (Intervale_fic_1!= dernier intervalle du fichier 1) et (Intervale_fic_2!= dernier intervalle du fichier 2)
si Intervale_fic_2 est inclus dans Intervale_fic_1
rajouter Intervale_fic_2 au résultat
++Intervale_fic_2
sinon
si Intervale_fic_2.borne Inferieur < Intervale_fic_1.Borne inferieur
++Intervale_fic_2
sinon
++Intervale_fic_1
fin si
fin si
fin tant que
ou qu'une approche dichotomique ?
tu penses que les segments tree seraient plus rapide que çà:
Intervale_fic_1 = premier intervalle du fichier 1
Intervale_fic_2 = premier intervalle du fichier 2
tant que (Intervale_fic_1!= dernier intervalle du fichier 1) et (Intervale_fic_2!= dernier intervalle du fichier 2)
si Intervale_fic_2 est inclus dans Intervale_fic_1
rajouter Intervale_fic_2 au résultat
++Intervale_fic_2
sinon
si Intervale_fic_2.borne Inferieur < Intervale_fic_1.Borne inferieur
++Intervale_fic_2
sinon
++Intervale_fic_1
fin si
fin si
fin tant que
ou qu'une approche dichotomique ?
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
31 mars 2009 à 22:38
31 mars 2009 à 22:38
n : nombre d'intervalles du 1er fichier,
m : nombre d'intervalles du 2nd fichier.
Complexité de ton algo : O(n.m).
Complexité de l'algo segment tree : O(n.(log(n) + log(m))).
Réponse : Oui
Par exemple si m = n, O(n^2) est sensiblement moins bon que O(n.log(n)). Trace les deux courbes sur une calculette graphique si tu veux être convaincu. Après à toi de voir.
Bonne chance
m : nombre d'intervalles du 2nd fichier.
Complexité de ton algo : O(n.m).
Complexité de l'algo segment tree : O(n.(log(n) + log(m))).
Réponse : Oui
Par exemple si m = n, O(n^2) est sensiblement moins bon que O(n.log(n)). Trace les deux courbes sur une calculette graphique si tu veux être convaincu. Après à toi de voir.
Bonne chance
bon okay je te crois, c toi le pro.
bon si par le plus grand des hazards (encore faut t'il qu'il le soit) tu as 5 nanosecondes de ton temps libre
à m'accorder, un petit coup de pouce coté programmation de mon problème serait le bienvenu.
çà m'éviterais de perdre un mois à essayer de coder ce problème.
merci.
bon si par le plus grand des hazards (encore faut t'il qu'il le soit) tu as 5 nanosecondes de ton temps libre
à m'accorder, un petit coup de pouce coté programmation de mon problème serait le bienvenu.
çà m'éviterais de perdre un mois à essayer de coder ce problème.
merci.
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
31 mars 2009 à 22:50
31 mars 2009 à 22:50
Désolée mais ça ne va pas être possible, je n'ai déjà pas le temps de coder ce que j'aimerais coder. Je peux te débloquer sur des points de code particulier mais pas plus...
Bonne chance
Bonne chance
mslider
Messages postés
109
Date d'inscription
jeudi 21 octobre 2004
Statut
Membre
Dernière intervention
7 mars 2010
1 avril 2009 à 12:31
1 avril 2009 à 12:31
bon c pas grave, j'ai pas mal galéré et fini par trouvé une solution en C, certes çà utilise
pas le segment tree mais la performance du code est convenable malgré une erreur à la compilation dont
je ne trouve pas le pourquoi du comment.
voilà le code:
[CODE]
#include <stdio.h>
int SafeOpenFile(char *lpFileName, char *lpMode, FILE **lpFile)
{
*lpFile = fopen(lpFileName, lpMode);
if (! *lpFile)
printf("Cannot open file: \"%s\"\n", lpFileName);
return (int)*lpFile;
}
int main(int argc, char **argv)
{
int nResult;
FILE *lpF1;
FILE *lpF2;
FILE *lpFOut;
char lpF1Desc[50];
int nF1Start;
int nF1End;
char lpF2Desc[50];
int nF2Start;
int nF2End;
long nPosInF2;
nResult = 1;
/* if (argc != 4)
{
printf("Trouve les intervalles inclus dans d'autres intervalles.\n\n");
printf("Intervalles fichier1 fichier2 fichier_de_sortie\n\n");
printf(" fichier1 Fichier contenant des intervalles.\n\n");
printf(" fichier2 Fichier contenant les intervalles qui doivent\n");
printf(" être inclus dans les intervalles de fichier1.\n\n");
printf(" fichier_de_sortie Fichier récupérant les résultats.\n\n");
goto the_end;
} */
if (! SafeOpenFile(argv[1], "r", &lpF1)) goto the_end;
if (! SafeOpenFile(argv[2], "r", &lpF2)) goto close_1;
if (! SafeOpenFile(argv[3], "w", &lpFOut)) goto close_1_and_2;
nPosInF2 = 0;
while (! feof(lpF1))
{
if (fscanf(lpF1, "%s %d %d", lpF1Desc, &nF1Start, &nF1End) != 3) break;
/* printf("%s %d %d\n", lpF1Desc, nF1Start, nF1End); */
fseek(lpF2, nPosInF2, SEEK_SET);
while (! feof(lpF2))
{
if (fscanf(lpF2, "%s %d %d", lpF2Desc, &nF2Start, &nF2End) != 3) break;
/* printf(" %s %d %d\n", lpF2Desc, nF2Start, nF2End); */
/* Pas la peine de relire le fichier depuis le début */
if (nF2Start < nF1Start)
nPosInF2 = ftell(lpF2);
/* On s'arrête quand le début de l'interval 2 est supérieur à la fin du 1 */
if (nF2Start > nF1End) break;
/* Test d'inclusion du deuxième intervalle dans le premier */
if ((nF2Start >= nF1Start) && (nF2End <= nF1End))
fprintf(lpFOut, "%s %d %d %s %d %d\n", lpF1Desc, nF1Start, nF1End, lpF2Desc, nF2Start, nF2End);
}
}
nResult = 0;
fclose(lpFOut);
close_1_and_2:
fclose(lpF2);
close_1:
fclose(lpF1);
the_end:
return nResult;
}
[/CODE]
à la compilation j'ai cette erreur:
cc -o interval interval.c
interval.c: In function `SafeOpenFile':
interval.c:9: warning: cast from pointer to integer of different size
malgré ce, çà tourne bien, je l'ai testé sur deux gros fichiers: fichier1 ~ 100000 lignes et fichier2 ~ 800000 lignes çà a pris 3 secondes.
tu vois une optimisation de ton coté ?
pas le segment tree mais la performance du code est convenable malgré une erreur à la compilation dont
je ne trouve pas le pourquoi du comment.
voilà le code:
[CODE]
#include <stdio.h>
int SafeOpenFile(char *lpFileName, char *lpMode, FILE **lpFile)
{
*lpFile = fopen(lpFileName, lpMode);
if (! *lpFile)
printf("Cannot open file: \"%s\"\n", lpFileName);
return (int)*lpFile;
}
int main(int argc, char **argv)
{
int nResult;
FILE *lpF1;
FILE *lpF2;
FILE *lpFOut;
char lpF1Desc[50];
int nF1Start;
int nF1End;
char lpF2Desc[50];
int nF2Start;
int nF2End;
long nPosInF2;
nResult = 1;
/* if (argc != 4)
{
printf("Trouve les intervalles inclus dans d'autres intervalles.\n\n");
printf("Intervalles fichier1 fichier2 fichier_de_sortie\n\n");
printf(" fichier1 Fichier contenant des intervalles.\n\n");
printf(" fichier2 Fichier contenant les intervalles qui doivent\n");
printf(" être inclus dans les intervalles de fichier1.\n\n");
printf(" fichier_de_sortie Fichier récupérant les résultats.\n\n");
goto the_end;
} */
if (! SafeOpenFile(argv[1], "r", &lpF1)) goto the_end;
if (! SafeOpenFile(argv[2], "r", &lpF2)) goto close_1;
if (! SafeOpenFile(argv[3], "w", &lpFOut)) goto close_1_and_2;
nPosInF2 = 0;
while (! feof(lpF1))
{
if (fscanf(lpF1, "%s %d %d", lpF1Desc, &nF1Start, &nF1End) != 3) break;
/* printf("%s %d %d\n", lpF1Desc, nF1Start, nF1End); */
fseek(lpF2, nPosInF2, SEEK_SET);
while (! feof(lpF2))
{
if (fscanf(lpF2, "%s %d %d", lpF2Desc, &nF2Start, &nF2End) != 3) break;
/* printf(" %s %d %d\n", lpF2Desc, nF2Start, nF2End); */
/* Pas la peine de relire le fichier depuis le début */
if (nF2Start < nF1Start)
nPosInF2 = ftell(lpF2);
/* On s'arrête quand le début de l'interval 2 est supérieur à la fin du 1 */
if (nF2Start > nF1End) break;
/* Test d'inclusion du deuxième intervalle dans le premier */
if ((nF2Start >= nF1Start) && (nF2End <= nF1End))
fprintf(lpFOut, "%s %d %d %s %d %d\n", lpF1Desc, nF1Start, nF1End, lpF2Desc, nF2Start, nF2End);
}
}
nResult = 0;
fclose(lpFOut);
close_1_and_2:
fclose(lpF2);
close_1:
fclose(lpF1);
the_end:
return nResult;
}
[/CODE]
à la compilation j'ai cette erreur:
cc -o interval interval.c
interval.c: In function `SafeOpenFile':
interval.c:9: warning: cast from pointer to integer of different size
malgré ce, çà tourne bien, je l'ai testé sur deux gros fichiers: fichier1 ~ 100000 lignes et fichier2 ~ 800000 lignes çà a pris 3 secondes.
tu vois une optimisation de ton coté ?
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
1 avril 2009 à 14:56
1 avril 2009 à 14:56
Je suis en train de coder un segment tree pour un besoin spécifique à mon travail. Je te mettrai le source quand il sera prêt. N'hésite pas à me relancer si j'oublie.
merci c cool.
d'après toi c quoi qui coince sur la fonction SafeOpenFile vu le code erreur:
interval.c: In function `SafeOpenFile':
interval.c:26: warning: cast from pointer to integer of different size
int SafeOpenFile(char *lpFileName, char *lpMode, FILE **lpFile)
{
*lpFile = fopen(lpFileName, lpMode);
if (! *lpFile)
printf("Cannot open file: \"%s\"\n", lpFileName);
return (int)*lpFile;
}
d'après toi c quoi qui coince sur la fonction SafeOpenFile vu le code erreur:
interval.c: In function `SafeOpenFile':
interval.c:26: warning: cast from pointer to integer of different size
int SafeOpenFile(char *lpFileName, char *lpMode, FILE **lpFile)
{
*lpFile = fopen(lpFileName, lpMode);
if (! *lpFile)
printf("Cannot open file: \"%s\"\n", lpFileName);
return (int)*lpFile;
}
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
1 avril 2009 à 15:23
1 avril 2009 à 15:23
Ca ne va pas du tout. Il faut que tu gères les ouvertures de fichiers comme suit :
En outre il est particulièrement important de ne pas fermer (fclose) un FILE * si le fichier ne s'est pas ouvert.
Bonne chance
#include <stdio.h> int main(){ const char* filename = "mon_fichier.txt"; FILE *fp = fopen(filename,"r"); if(!fp){ fprintf(stderr,"%s: can't read %s\n",argv[0],filename); return 1; // quitte le programme avec le code d'erreur 1 } // Lire le contenu de fp //... fclose(fp); return 0; // quitte le programme avec le code d'exécution 0 (tout va bien) }
En outre il est particulièrement important de ne pas fermer (fclose) un FILE * si le fichier ne s'est pas ouvert.
Bonne chance
oui c plus sur et correct comme çà.
l'nterêt de ma fonction c'était d'éviter de re-écrire des fopen autant de fois qu'il y a de
fichiers à ouvrir.
l'nterêt de ma fonction c'était d'éviter de re-écrire des fopen autant de fois qu'il y a de
fichiers à ouvrir.
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
3 avril 2009 à 20:35
3 avril 2009 à 20:35
Comme promis, voici une implémentation du segment tree.
Le deal
Le code étant assez chiant à écrire et étant donné que je ne suis pas très fan du récursif, il est possible qu'il y ait quelques bugs. Donc je veux bien :
1) que tu fasses quelques tests
2) si tu trouves des jeux de segments qui se comportent mal, que tu me les reportes (voir que tu me dises les corrections à apporter :p).
Un exemple
On commence par insérer un jeu de segment dans l'arbre. Ici je prends des segments dont les bornes sont des char, mais en fait tu peux utiliser des entiers ou n'importe quel type de base.
Ensuite on regarde à quels segments appartiennent les caractères allant de 'a' à 'i' et on retourne le résultat sous forme d'un tableau (test de la fonction get_intervals). Puis on cherche un intervalle dans lequel est contenu les caractères allant de 'a' à 'i' (test de get_interval).
Le code
Principe :
1) La structure tree stocke un jeu d'intervalles (champ segment). Une fois les segments déterminés (voir fonction main) on peut calculer le nombre de feuilles nécessaires pour construire l'arbre (voir fonction make_tree) et construire le maillage. Les intervalles sont marqués aux nœuds correspondant à l'aide d'un bit spécifique dans le masque intervals stocké dans un nœud.
2) Une requête pour trouver quel(s) intervalle(s) correspondent avec une valeur donnée consiste à parcourir les branches pertinentes de l'arbre et examiner le masque des nœuds croisés. Si on cherche juste un intervalle on utilise get_interval qui explore l'arbre tant qu'aucun nœud n'a été trouvé. get_intervals parcours tous les nœuds pertinents susceptibles de marquer un segment enregistré. Le masque fabriqué par get_intervals représente l'union de tous les intervalles contenant la valeur recherchée (voir fonction main pour l'utilisation).
Bonne chance
Le deal
Le code étant assez chiant à écrire et étant donné que je ne suis pas très fan du récursif, il est possible qu'il y ait quelques bugs. Donc je veux bien :
1) que tu fasses quelques tests
2) si tu trouves des jeux de segments qui se comportent mal, que tu me les reportes (voir que tu me dises les corrections à apporter :p).
Un exemple
On commence par insérer un jeu de segment dans l'arbre. Ici je prends des segments dont les bornes sont des char, mais en fait tu peux utiliser des entiers ou n'importe quel type de base.
Ensuite on regarde à quels segments appartiennent les caractères allant de 'a' à 'i' et on retourne le résultat sous forme d'un tableau (test de la fonction get_intervals). Puis on cherche un intervalle dans lequel est contenu les caractères allant de 'a' à 'i' (test de get_interval).
segment[0] = [c,e] segment[1] = [a,b] segment[2] = [b,d] segment[3] = [f,h] segment[4] = [e,f] segment[5] = [h,i] | 0 1 2 3 4 5 | a | 0 1 0 0 0 0 | b | 0 1 1 0 0 0 | c | 1 0 1 0 0 0 | d | 1 0 1 0 0 0 | e | 1 0 0 0 1 0 | f | 0 0 0 1 1 0 | g | 0 0 0 1 0 0 | h | 0 0 0 1 0 1 | i | 0 0 0 0 0 1 | a belongs to segment [a,b] b belongs to segment [b,d] c belongs to segment [c,e] d belongs to segment [c,e] e belongs to segment [e,f] f belongs to segment [f,h] g belongs to segment [f,h] h belongs to segment [h,i] i belongs to segment [h,i]
Le code
#include <stdio.h> #include <stdlib.h> //======================================================================== // Implémentation du segment tree // Voir https://en.wikipedia.org/wiki/Segment_tree // // - Un segment tree est un arbre binaire équilibré dont chaque feuille // décrit une borne (unique) étant donné un jeu d'intervalles (segment). // // - Chaque noeud décrit un intervalle canonique (tous les intervalles étant // décrit par une union d'intervalles canoniques). Chaque noeud maintient // un masque indiquant quels intervalles incluent leur intervalle canonique. // //======================================================================== typedef char bound_t; /**< Le type des bornes décrivant un segment */ typedef long bit_field_t; /**< Le type du masque maintenu dans un noeud */ #define BOUND_MIN 0 /**< - infini */ #define BOUND_MAX 255 /**< + infini */ /** * \class segment_t Décrit un intervalle */ typedef struct segment_t{ bound_t lb; /**< La borne inférieure de l'intervalle */ bound_t ub; /**< La borne supérieure de l'intervalle */ } segment_t; /** * \class node_t Noeud de l'arbre canonique */ typedef struct node_t{ unsigned index; /**< L'index du noeud dans l'arbre */ struct node_t * l; /**< Fils gauche */ struct node_t * r; /**< Fils droit */ struct segment_t segment; /**< L'intervalle canonique couvert par le noeud */ bit_field_t intervals; /**< Le bit 1 << i vaut 1 ssi 'segment' décrit un intervalle canonique maximal inclu dans l'intervalle i */ } node_t; /** * \class tree_t Le segment tree */ typedef struct tree_t{ struct node_t * nodes; /**< Les noeuds stockés dans l'arbre. &nodes[0] correspond au noeud racine */ struct segment_t * segments; /**< Les segments stockés dans l'arbre. segments[i] décrit l'intervalle correspondant au bit 'i' dans le masque intervals d'un noeud. */ unsigned nb_segments; /**< Le nombre de segment installés */ } tree_t; // Usage interne au segment tree static tree_t prepare_tree(unsigned,const bound_t *,unsigned); static int install_segment(tree_t *,const segment_t *); static void init_tree_rec(tree_t *,node_t *,unsigned *,unsigned,unsigned *,const bound_t *,unsigned); static void install_interval_rec(tree_t *,node_t *,unsigned,const segment_t *); static void get_intervals_rec(tree_t *,node_t *,bound_t,bit_field_t *); static void get_interval_rec (tree_t *,node_t *,bound_t,bit_field_t *); static int sort_growing(const void *x,const void *y); // Utilisation du segment tree tree_t make_tree(const segment_t *segments,unsigned nb_segments,unsigned *nb_bounds); void release_tree(tree_t *); int bit_field2index(bit_field_t *); bit_field_t get_intervals(tree_t *,bound_t); segment_t * get_interval(tree_t *,bound_t); /** * \brief Construit l'arbre, initialise tous les champs de chaque noeuds * \param tree Le segment tree * \param node Le noeud courant. Pour un appel récursif de partant de la racine, passer &tree->nodes[0] * \param cur_index L'index du noeud dans le vecteur * \param floor L'étage du noeud courant (0 pour les feuilles, ..., E(log2(nb_leaves)) pour la racine, avec nb_leaves = nb_bounds) * \param premaining_bounds Pointe sur le nobre de borne qu'il faut encore installer * \param bounds Les bornes ordonnées des intervalles stockés dans l'arbre * \param nb_bounds Le nombre de bornes */ void init_tree_rec( tree_t * tree, node_t * node, unsigned * cur_index, unsigned floor, unsigned * premaining_bounds, const bound_t * bounds, unsigned nb_bounds ){ node->index = (*cur_index)++; node->intervals = 0; if(floor){ // Construire le fils gauche if(*premaining_bounds){ node->l = &tree->nodes[*cur_index]; init_tree_rec(tree,node->l,cur_index,floor-1,premaining_bounds,bounds,nb_bounds); if(floor == 1){ node->l->segment.lb = node->l->segment.ub = bounds[nb_bounds - *premaining_bounds]; node->l->l = node->l->r = NULL; --*premaining_bounds; } }else node->l = NULL; // Construire le fils droit if(*premaining_bounds){ node->r = &tree->nodes[*cur_index]; init_tree_rec(tree,node->r,cur_index,floor-1,premaining_bounds,bounds,nb_bounds); if(floor == 1){ node->r->segment.lb = bounds[nb_bounds - *premaining_bounds - 1]; node->r->segment.ub = bounds[nb_bounds - *premaining_bounds]; node->r->l = node->r->r = NULL; } }else node->r = NULL; // Installer les bornes node->segment.lb = node->l->segment.lb; node->segment.ub = node->r ? node->r->segment.ub : node->l->segment.ub; } } /** * \brief Construit l'arbre binaire équilibré * \param bounds Les bornes à installer dans l'arbre * \param nb_bounds Le nombre de borne * \return L'arbre créé */ tree_t prepare_tree(unsigned nb_segments,const bound_t *bounds,unsigned nb_bounds){ tree_t tree; unsigned highest_floor, remaining_bounds, index ; // Vérifier que le masque des noeuds est suffisamment grand par rapport au nombre d'intervalles à stocker if(nb_segments > 8*sizeof(bit_field_t)){ printf("prepare_tree: too many segments (%d) (max = %d). last segments will be ignored\n",nb_segments,8*sizeof(bit_field_t)); nb_segments = 8*sizeof(bit_field_t); } // Le nombre de noeuds dans un arbre est borné par 2 fois le nombre de feuilles (nb feuilles = 2 fois le nombre de bornes + 1) tree.nodes = (node_t *) malloc(2*(2 * nb_bounds + 1)*sizeof(node_t)); tree.segments = (segment_t *) malloc(nb_segments*sizeof(segment_t)); tree.nb_segments = 0; // Les étages sont indexés (de 0 pour les feuilles à highest_floor = E(log2(nb_leaves) pour la racine) for(highest_floor = 0;(unsigned) (1 << highest_floor) < (2 * nb_bounds + 1); ++highest_floor); // Construire le maillage, installer les index et remettre à zéro intervals index = 0; remaining_bounds = nb_bounds; init_tree_rec(&tree,&tree.nodes[0],&index,highest_floor,&remaining_bounds,bounds,nb_bounds); return tree; } /** * \brief Désalloue l'arbre * \param tree L'arbre à désallouer */ void release_tree(tree_t *tree){ if(tree->nodes) { free(tree->nodes); tree->nodes = NULL; } if(tree->segments) { free(tree->segments); tree->segments = NULL; } tree->nb_segments = 0; } /** * \brief Installe un intervalle * \param tree Le segment tree * \param node Le noeud courant. Pour un appel récursif de partant de la racine, passer &tree->nodes[0] * \param idx_interval La position du bit qui identifie l'intervalle seg dans chaque noeud de l'arbre * \param seg Pointeur sur l'intervalle à installer */ void install_interval_rec( tree_t * tree, node_t * node, unsigned idx_interval, const segment_t * seg ){ if(seg->lb <= node->segment.lb && node->segment.ub <= seg->ub){ // Si l'intervalle canonique du noeud courant est inclu dans seg node->intervals |= (1 << idx_interval); }else{ // Si l'intervalle canonique d'un fils recouvre partiellement seg, plonger dedans if(node->l && (node->l->segment.lb <= seg->ub && node->l->segment.ub > seg->lb)){ install_interval_rec(tree,node->l,idx_interval,seg); } if(node->r && (node->r->segment.lb <= seg->ub && node->r->segment.ub > seg->lb)){ install_interval_rec(tree,node->r,idx_interval,seg); } } } /** * \brief Installe un intervalle dans l'arbre * \param tree Le segment tree * \param seg Pointeur sur l'intervalle à installer * \return Le nombre de segments insérés avec succès */ int install_segment( tree_t * tree, const segment_t * seg ){ // Vérifier que le pool d'intervalles enregistrés n'est pas plein if(tree->nb_segments > 8*sizeof(bit_field_t)) return 0; // Installer l'intervalle dans les segments enregistrés tree->segments[tree->nb_segments].lb = seg->lb; tree->segments[tree->nb_segments].ub = seg->ub; // Marquer les noeuds install_interval_rec(tree,&tree->nodes[0],(tree->nb_segments)++,seg); return 1; } /** * \brief Retrouve le masque constitué des identifiants d'intervalles contenant un point donné * \param tree Le segment tree * \param node Le noeud courant. Pour un appel récursif de partant de la racine, passer &tree->nodes[0] * \param value Le point * \param b Un pointeur vers le masque initialisé à 0 */ void get_intervals_rec( tree_t * tree, node_t * node, bound_t value, bit_field_t * b ){ *b |= node->intervals; if(node->l || node->r){ if(node->l && (value < node->l->segment.ub || (value == node->l->segment.ub && !node->l->r))){ // Si le point est situé avant la borne supérieure du fils gauche, ou que ce fils gauche // est une feuille contenant la valeur value, on cherche dans le fils gauche. get_intervals_rec(tree,node->l,value,b); }else if(node->r){ // Sinon le point est dans le sous-arbre droit. get_intervals_rec(tree,node->r,value,b); } } } /** * \brief Retrouve le masque constitué des identifiants d'intervalles contenant un point donné. * Si on sait qu'au plus un segment correspond, utiliser get_interval (plus efficace) * \param tree Le segment tree * \param value Le point * \return Le masque constitué des identifiants d'intervalles contenant un point donné */ bit_field_t get_intervals( tree_t * tree, bound_t value ){ bit_field_t b; if(!tree->nodes) return 0; // arbre vide b = 0; get_intervals_rec(tree,&tree->nodes[0],value,&b); return b; } /** * \brief Retrouve le premier intervalle qui contient un point * \param tree Le segment tree * \param node Le noeud courant. Pour un appel récursif de partant de la racine, passer &tree->nodes[0] * \param pt Le point * \param b Un pointeur vers le masque initialisé à 0 */ void get_interval_rec( tree_t * tree, node_t * node, bound_t value, bit_field_t * b ){ *b |= node->intervals; if(!*b &&(node->l || node->r)){ if(node->l && (value < node->l->segment.ub || (value == node->l->segment.ub && !node->l->r))) get_interval_rec(tree,node->l,value,b); else if (node->r) get_interval_rec(tree,node->r,value,b); } } /** * \brief Cherche un intervalle stocké dans l'arbre contenant un point particulier * \param tree Le segment tree * \param value Le point * \return Un pointeur vers le segment correspondant */ segment_t * get_interval( tree_t * tree, bound_t value ){ bit_field_t b; if(!tree->nodes) return NULL; // arbre vide b = 0; get_interval_rec(tree,&tree->nodes[0],value,&b); if(!b) return NULL; // 'value' n'est inclu dans aucun segment canonique return &tree->segments[bit_field2index(&b)]; } /** * \brief Convertit un bitfield * \param b Pointeur sur le champ de bit * \return La position du plus petit bit à 1 (-1 si b ne contient que des 0) */ int bit_field2index(bit_field_t *b){ unsigned i; for(i = 0; i < 8 * sizeof(bit_field_t) ; ++i){ if (*b & (1 << i)) return i; } return -1; } /** * \brief Ordonne deux bornes * \param x Pointeur sur la première borne * \param y Pointeur sur la seconde borne * \return une valeur négative si *x inférieur *y, 0 si *x == *y, une valeur positive sinon */ int sort_growing(const void *px,const void *py){ const bound_t *x = px; const bound_t *y = py; return *x - *y; } /** * \brief Construit un segment tree étant donné un jeu d'intervalles * \param segments Les intervalles * \param nb_segments Le nombre d'intervalles * \param nb_bounds Un pointeur vers un entier qui contiendra le nombre de bornes distinctes * (feuilles) contenu dans l'arbre retourné * \return Le segment tree */ tree_t make_tree(const segment_t *segments,unsigned nb_segments,unsigned *nb_bounds){ unsigned i,n; bound_t * bounds; tree_t _tree; if(!nb_segments || !segments){ // Arbre vide *nb_bounds = _tree.nb_segments = 0; _tree.nodes = NULL; _tree.segments = NULL; }else{ n = 2 * (nb_segments + 1); bounds = (bound_t *) malloc(n * sizeof(bound_t)); // Stocker les bornes des intervalles, -infini et +infini dans bounds bounds[0] = BOUND_MIN; for(i=0;i<nb_segments;++i){ bounds[2 * i + 1] = segments[i].lb; bounds[2 * i + 2] = segments[i].ub; } bounds[n - 1] = BOUND_MAX; // Trier les bornes et virer les doublons (les *nb_bounds premières cases contiendront les bornes 2 à 2 distincites) qsort(bounds,n,sizeof(bound_t),sort_growing); *nb_bounds = 1; for(i=1;i<n;++i){ if(bounds[i] != bounds[*nb_bounds-1]){ bounds[*nb_bounds] = bounds[i]; ++*nb_bounds; } } // Construire le maillage _tree = prepare_tree(nb_segments,bounds,*nb_bounds); // Installer des segments for(i=0;i<nb_segments;++i) install_segment(&_tree,&segments[i]); free(bounds); } return _tree; } int main(){ const unsigned nb_segments = 6; unsigned i,nb_bounds; tree_t _tree; segment_t segments[nb_segments]; segment_t * seg; tree_t * tree = &_tree; bit_field_t mask; char c; // Les segments que l'on va stocker dans l'arbre segments[0].lb = 'c'; segments[0].ub = 'e'; // [c,e] segments[1].lb = 'a'; segments[1].ub = 'b'; // [a,b] segments[2].lb = 'b'; segments[2].ub = 'd'; // [b,d] segments[3].lb = 'f'; segments[3].ub = 'h'; // [f,h] segments[4].lb = 'e'; segments[4].ub = 'f'; // [e,f] segments[5].lb = 'h'; segments[5].ub = 'i'; // [h,i] _tree = make_tree(segments,nb_segments,&nb_bounds); // afficher les intervalles for(i = 0;i<nb_segments;++i){ printf("segment[%d] = [%c,%c]\n",i,tree->segments[i].lb,tree->segments[i].ub); } // get_intervals printf(" | "); for(i = 0;i<tree->nb_segments;++i) printf("%i ",i); printf("|\n"); for(c = 'a';c<'j';++c){ printf("%c | ",c); mask = get_intervals(tree,c); for(i=0;i<tree->nb_segments;++i){ printf("%d ",mask & (1 << i) ? 1 : 0); } printf("|\n"); } // get_interval for(c = 'a';c<'j';++c){ seg = get_interval(tree,c); if (seg) printf("%c belongs to segment [%c,%c]\n",c,seg->lb,seg->ub); else printf("%c does not belong to any stored segment\n",c); } // Libérer l'arbre release_tree(tree); return 0; } //========================================================================
Principe :
1) La structure tree stocke un jeu d'intervalles (champ segment). Une fois les segments déterminés (voir fonction main) on peut calculer le nombre de feuilles nécessaires pour construire l'arbre (voir fonction make_tree) et construire le maillage. Les intervalles sont marqués aux nœuds correspondant à l'aide d'un bit spécifique dans le masque intervals stocké dans un nœud.
2) Une requête pour trouver quel(s) intervalle(s) correspondent avec une valeur donnée consiste à parcourir les branches pertinentes de l'arbre et examiner le masque des nœuds croisés. Si on cherche juste un intervalle on utilise get_interval qui explore l'arbre tant qu'aucun nœud n'a été trouvé. get_intervals parcours tous les nœuds pertinents susceptibles de marquer un segment enregistré. Le masque fabriqué par get_intervals représente l'union de tous les intervalles contenant la valeur recherchée (voir fonction main pour l'utilisation).
Bonne chance
mslider
Messages postés
109
Date d'inscription
jeudi 21 octobre 2004
Statut
Membre
Dernière intervention
7 mars 2010
3 avril 2009 à 21:02
3 avril 2009 à 21:02
déjà de ton coté tu peux tester le code avec le jeu de données que je fournis dans mon premier post, voir tout en haut. Il y a les deux fichiers d'entrée à croiser et le résultat que tu dois obtenir.
Si le résultat renvoyé par ton code est correct c'est que le segment tree ne commet pas d'erreur.
Si le résultat renvoyé par ton code est correct c'est que le segment tree ne commet pas d'erreur.
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
3 avril 2009 à 21:09
3 avril 2009 à 21:09
Pour être complètement honnête j'ai un peu la flemme de parser tes fichiers à 21h :-) Le plus gros est fait, je pense que tu peux faire un effort, c'est un peu pour toi aussi non ?
mslider
Messages postés
109
Date d'inscription
jeudi 21 octobre 2004
Statut
Membre
Dernière intervention
7 mars 2010
3 avril 2009 à 21:17
3 avril 2009 à 21:17
ouais moi aussi dodo.
je le ferais demain.
je le ferais demain.
30 mars 2009 à 11:59
30 mars 2009 à 12:05
c'est ce que fait la boucle while dans le bout de code awk que j'ai donné.