web-dev-qa-db-fra.com

Algorithme optimal pour renvoyer les k premières valeurs d'un tableau de longueur N

J'ai un tableau de n flottants et je souhaite retourner le k Le plus haut (dans mon cas, n ~ 100, k ~ 10)

Existe-t-il une solution optimale connue pour ce problème?

Quelqu'un pourrait-il fournir un algorithme C?

EDIT: en fait, il y a deux problèmes ici: triés et non triés. Je suis intéressé par non trié, ce qui devrait être plus rapide!

23
P i

Méthode 1

Comme k est petit, vous pouvez utiliser la méthode du tournoi pour trouver le kème le plus grand. Cette méthode est décrite dans Knuth Art of Programming, volume 3, page 212.

Commencez par créer un tournoi sur n-k + 2 éléments. Quelque chose comme un tournoi de tennis KO. Tout d’abord, vous vous divisez en paires et comparez les membres des paires (comme si ces deux jouaient un match et un perdait). Puis les gagnants, vous vous divisez à nouveau en paires et ainsi de suite, jusqu’à ce que vous ayez un gagnant. Vous pouvez le voir comme un arbre, avec le gagnant en haut.

Cela prend n-k + 1 se compare exactement.

Maintenant, le gagnant de ces n-k + 2 ne peut pas être votre kème plus grand élément. Considérez son chemin P le tournoi.

Parmi les k-2 restants, choisissez-en un et suivez le chemin P qui vous donnera un nouveau plus grand. En gros, vous refaites le tournoi en remplaçant le vainqueur précédent par l’un des éléments k-2. Soit P le chemin du nouveau gagnant. Maintenant, choisissez-en un autre parmi le k-3 et suivez le nouveau chemin qui monte, etc.

À la fin, après avoir épuisé le k-2, remplacez le plus gros par -infinity et le plus grand du tournoi sera le kème le plus grand. Les éléments que vous avez jetés sont les meilleurs éléments k-1.

Il faut au maximum n - k + (k-1) [log (n-k+2)] comparaisons pour trouver le top k. Il utilise cependant O(n) mémoire. 

En termes de nombre de comparaisons, cela devrait probablement battre tous les algorithmes de sélection.

Méthode 2

Au lieu de cela, vous pouvez conserver un minimum de k éléments.

Commencez par insérer k éléments. Puis, pour chaque élément du tableau, s'il est inférieur à l'élément min du tas, jetez-le. Sinon, supprimez-min du tas et insérez l'élément du tableau.

A la fin, le tas contiendra les k premiers éléments. Cela prendra O(n log k) compare.

Bien sûr, si n est petit, le tri de la matrice devrait suffire. Le code sera aussi plus simple.

27
Aryabhatta

Vous pouvez le faire dans O(n) en utilisant un algorithme de sélection . Recherchez l'élément kth le plus grand avec l'algorithme de partition, puis tous les éléments qui le suivront seront plus grands que celui-ci, et ce sont vos k haut.

Si vous avez besoin de ces k top dans l'ordre de tri, vous pouvez les trier dans O(k log k).

30
IVlad

Réponse courte: non.

Réponse plus longue: oui, plusieurs solutions optimales incompatibles sont connues. Cela dépend de n, k et des propriétés du tableau que vous pouvez garantir.

Si vous ne savez rien du tableau, la limite inférieure de complexité est évidemment O (n), car tous les éléments du tableau source doivent être examinés pour voir s'ils se rangent dans le top 10. Si vous savez quelque chose sur le tableau source qui autorise les éléments pour être évité en toute sécurité, vous devriez utiliser cette connaissance.

De même, la limite supérieure de complexité est O(n.log(n)), car vous pouvez toujours choisir de trouver la réponse en triant le tableau (O (n.log (n)) et en renvoyant les 10 premiers éléments. (O (1)).

Une recherche linéaire comparant chaque élément au dixième plus important trouvé à ce jour et l'insérant à l'endroit approprié dans la liste des éléments les plus trouvés jusqu'à présent présente, si nécessaire, une complexité similaire pour les scénarios moyen et optimal et a le pire -case of O(kn) qui est nettement meilleur que O (n-carré). Pour les tailles que vous estimez, cette méthode devrait bien fonctionner.

Si n était beaucoup plus grand (~ 10000) et que k augmentait dans le même rapport, il serait probablement intéressant de mettre en œuvre l'algorithme quickselect. Quickselect fonctionne mieux avec plus d'éléments. Si, toutefois, l’échelle de k n’a pas augmenté avec n, vous devez vous en tenir à la recherche linéaire. Quickselect & friends modifie la matrice d'origine. Par conséquent, ils sont moins appropriés si vous ne pouvez pas le faire sur place, car vous avez besoin de beaucoup plus de stockage et de nombreuses copies que la complexité de l'algorithme ne comprend pas.

Si n est énorme (~ 1e20), vous voudriez trouver le k le plus grand de chacune des partitions du tableau en entrée, puis le k-le plus grand de l'ensemble des résultats, de sorte que vous n'essayiez pas d'analyser davantage. données que vous pouvez insérer en mémoire à la fois et permettre une mise en parallèle efficace des opérations.

10
Phil Willoughby

Voici une solution élégante basée sur le tas en Java avec une complexité O (nlogK). Ce n'est pas le plus efficace, mais je pense que c'est assez facile à comprendre. Vous pouvez remplacer Integer par Float si vous souhaitez une solution à base flottante.

import Java.util.Arrays;
import Java.util.PriorityQueue;

public class FindKLargest {

public static void find(int[] A, int k) {

    PriorityQueue<Integer> pq = new PriorityQueue<>(k);// Min heap because the element has to be greater
                                                        // than the smallest element in the heap in order
                                                        // to be qualified to be a member of top k elements.
    for (int i = 0; i < A.length; i++) {
        if (i < k) // add until heap is filled with k elements.
            pq.add(A[i]);
        else if (pq.peek() < A[i]) { // check if it's bigger than the
                                        // smallest element in the heap.
            pq.poll();
            pq.add(A[i]);
        }
    }
    int[] topK = new int[pq.size()];
    int index = 0;
    while (index != k)
        topK[index++] = pq.poll();
    System.out.println(Arrays.toString(topK));
}

public static void main(String[] args) {
    int[] arr = { 1, -2, -3, -4, -5 };
    find(arr, 4);
}

}

3
piyush121

Jetez un coup d'œil à l'algorithme de tri sélectif partiel présenté vers la fin de "Sélection efficace et tri partiel basé sur le tri sélectif" .

2
NPE

si vous avez un gpu sophistiqué, je peux vous dire comment calculer les k énormes d'énormes n instances en même temps, donc étalez-les sur une texture, par exemple, et ajoutez le mélange à une texture avec leur "hauteur" comme la position le long de la texture.

Mais notez que vous devez deviner une plage acceptable ou la connaître, sinon vous ne pourrez pas vous étendre au maximum de détails que vous auriez pu avoir.

vous clonez des positions. (Vous devriez obtenir un 2, s'il y en a 2, 10 s'il y en a 10.) dans tous les cas. (dites simplement que tout est sur une texture de 8192x8192, 64x64 de ces cases "hauteur".) et vous sautez également des logements avec 0 compte.

faites ensuite une hiérarchie mipped add, sauf que vous le faites comme un arbre binaire, vous ne traitez que comme sa dimension 1, prenez donc les 2 nombres précédents et additionnez-les, et continuez à le faire pour chaque mip binaire.

ensuite, nous utilisons ces mips (qui ont collecté des comptes) pour déterminer l’emplacement approximatif du k, en utilisant tous les mips du processus, le faire sur un fil final, vous en retirerez d’énormes morceaux, puis vous utiliserez lentement les mips plus détaillés pour: trouve la valeur par pixel à laquelle k se situe. 

cela a plus de sens de faire cela, si tout était à nouveau instancié, alors c'est une découverte thread par seuil. (Disons simplement que vous courez un ANN 128x128 fois à la fois (invarience translationnelle?)? Alors, cela prend tout son sens. 

et atteindre la hauteur seuil pour ce compte, mais sa valeur approximative ... vous obtenez donc un k approximatif. pour n listes.

Vous pouvez faire un peu plus de travail pour obtenir le k exact, mais dans un match de similitude, mais si vous pouvez vous en tirer avec une approximation, comme si vous obteniez les top k activations, ne vous inquiétez pas.