C'est un long texte. S'il vous plaît, soyez patient. En fin de compte, la question est: Existe-t-il un algorithme de tri de base en place fonctionnel ?
Préliminaire
J'ai un grand nombre de petites chaînes de longueur fixe qui utilisent uniquement les lettres "A", "C", "G" et "T" (oui, vous l'avez deviné : ADN) que je veux trier.
En ce moment, j'utilise std::sort
qui utilise l'algorithm introsort dans toutes les implémentations communes de la STL. Cela fonctionne plutôt bien. Cependant, je suis convaincu que le tri radix convient parfaitement à mon ensemble de problèmes et devrait fonctionner beaucoup mieux en pratique.
Détails
J'ai testé cette hypothèse avec une implémentation très naïve et pour des entrées relativement petites (de l'ordre de 10 000), c'était vrai (environ deux fois plus rapide en réalité). Cependant, le temps d'exécution se dégrade de façon abyssale lorsque la taille du problème devient plus grande (N > 5 000 000).
La raison est évidente : le tri radix nécessite de copier toutes les données (plus d'une fois dans mon implémentation naïve, en réalité). Cela signifie que j'ai mis ~ 4 Gio dans ma mémoire principale ce qui tue évidemment les performances. Même si cela fonctionnait, je ne peux pas me permettre d'utiliser autant de mémoire car les tailles des problèmes deviennent en réalité encore plus grandes.
Cas d'utilisation
Idéalement, cet algorithme devrait fonctionner avec n'importe quelle longueur de chaîne entre 2 et 100, pour l'ADN ainsi que l'ADN5 (qui autorise un caractère de joker supplémentaire "N"), ou même l'ADN avec des codes d'ambiguïté IUPAC (ce qui donne 16 valeurs distinctes). Cependant, je comprends que tous ces cas ne peuvent pas être couverts, donc je suis satisfait de toute amélioration de vitesse que j'obtiens. Le code peut décider dynamiquement de quel algorithme envoyer.
Recherche
Malheureusement, l'article Wikipedia sur le tri radix est inutile. La section sur une variante en place est complètement nulle. La section NIST-DADS sur le tri radix est quasi inexistante. Il y a un article prometteur appelé Tri radix en place efficace et adaptatif qui décrit l'algorithme "MSL". Malheureusement, cet article est également décevant.
En particulier, il y a les éléments suivants.
Tout d'abord, l'algorithme contient plusieurs erreurs et laisse beaucoup d'éléments non expliqués. En particulier, il ne détaille pas l'appel récursif (je suppose simplement qu'il incrémente ou réduit un pointeur pour calculer les valeurs de décalage et de masque actuelles). De plus, il utilise les fonctions dest_group
et dest_address
sans donner de définitions. Je ne vois pas comment implémenter efficacement ces fonctions (c'est-à-dire, en O(1) ; au moins dest_address
n'est pas trivial).
Enfin, l'algorithme parvient à être en-place en échangeant les indices du tableau avec des éléments à l'intérieur du tableau d'entrée. Cela fonctionne évidemment uniquement sur les tableaux numériques. Je dois l'utiliser sur des chaînes. Bien sûr, je pourrais juste ignorer le typage fort et supposer que la mémoire tolérera que je stocke un indice là où il ne devrait pas être. Mais cela ne fonctionne que tant que je peux compresser mes chaînes en 32 bits de mémoire (en supposant des entiers sur 32 bits). Ce n'est que 16 caractères (ignorons pour l'instant que 16 > log(5 000 000)).
Un autre article par l'un des auteurs ne donne pas du tout de description précise, mais il indique un temps d'exécution de MSL inférieur à linéaire, ce qui est complètement faux.
Pour récapituler : Y a-t-il un espoir de trouver une implémentation de référence fonctionnelle ou au moins un bon pseudo-code/description d'un tri radix in-place qui fonctionne sur des chaînes ADN ?
69 votes
C'est une question excellemment rédigée.
1 votes
À quel point sont petites les petites chaînes de longueur fixe ?
1 votes
@EvilTeach: J'ai ajouté les cas d'utilisation.
0 votes
Quels sont les exigences de sortie? Voulez-vous simplement produire une liste triée? ou sont-elles mises dans une base de données pour être appariées? ou...?
0 votes
@Jason: J'ai juste besoin de la liste. Le post-traitement diffère drastiquement. Un cas d'utilisation est en fait la création d'une table de recherche de type suffix array (en utilisant des k-mers au lieu de suffixes). La construction actuelle avec quicksort bat toutes les méthodes habituelles en temps linéaire.
0 votes
La question pourrait ne pas être correcte. Sur place pourrait être aussi mauvais que la copie si beaucoup de mouvements doivent être effectués.
2 votes
@Stephan: tout va bien. Mais en cas de copies/misses de cache, je subis simplement un retard. En cas de mémoire, j'atteins une limite physique. C'est simplement inacceptable. Toutes ces techniques sophistiquées pour stocker des parties des données sur disque sont certainement plus lentes que la solution rapide actuelle utilisant quicksort.
2 votes
(cont') La solution de dsimcha, en revanche, est certainement plus rapide que quicksort pour certaines entrées. Le nombre de mouvements peut être élevé et la proximité du cache faible, mais dans le monde réel, c'est toujours bon. J'ai également légèrement ajusté la solution pour réduire le nombre d'échanges que je dois effectuer.
1 votes
@PeterMortensen Pour l'avenir, j'apprécie les corrections et les liens pour ajouter du contexte. Cependant, je n'apprécie pas particulièrement les modifications qui ne concernent que le style ("dans l'ordre" contre "sur l'ordre", "c'est-à-dire" contre "c'est").
0 votes
Je réalise que c'est une vieille question mais je suis intéressé par ce que vous essayiez de faire. Je donne un cours sur les structures de données. Avez-vous toujours un jeu de données avec lequel jouer ? Quelle était la taille des chaînes de caractères ? Et pouvez-vous décrire pourquoi vous triiez les chaînes ? Il me semble que ce que vous faisiez ensuite avec les données triées est une partie cruciale manquante de cette question. Par exemple, si votre but était d'avoir un ensemble trié de séquences que vous pourriez ensuite essayer de fusionner, je pense que construire un trie pourrait être beaucoup plus rapide que de trier la liste intacte mais ce n'est qu'une supposition.
0 votes
@Dov C'était il y a longtemps, mais je travaillais avec les lectures de séquençage de nouvelle génération Illumina (32 bases) et les triais pour construire un index de recherche en temps constant (index q-gramme). Un trie aurait bien sûr été possible, mais utiliser un index q-gramme ici s'est avéré être efficace en pratique (Eland, etc.). Si je me souviens bien, l'index était ensuite interrogé par le GPU - mais comme je l'ai dit, c'était il y a longtemps pour un projet sur lequel je ne travaille plus.
0 votes
Algorithme pour le tri radix LSD presque in-place rapide