web-dev-qa-db-fra.com

Algorithme rapide pour rechercher une gamme de floats triés pour trouver la paire de flotteurs bracketing d'une valeur d'entrée

J'ai une gamme de flotteurs, triés du plus petit au plus grand et j'ai besoin de pouvoir choisir le flotteur le plus proche supérieur ou inférieur à une valeur d'entrée passée. Cette valeur d'entrée n'est pas nécessairement présente comme une valeur dans la matrice.

Une approche naïve serait de faire une simple recherche linéaire à travers la matrice. Cela pourrait ressembler à ceci:

void FindClosestFloatsInArray( float input, std::vector<float> array, 
                               float *min_out, float *max_out )
{
    assert( input >= array[0] && input < array[ array.size()-1 ] );
    for( int i = 1; i < array.size(); i++ )
    {
        if ( array[i] >= input )
        {
            *min = array[i-1];
            *max = array[i];
        }
    }
}

Mais évidemment, car le tableau devient plus grand, cela deviendra plus lent et plus lent.

Quelqu'un a-t-il une idée d'un algorithme qui me permettrait de trouver ces données plus de manière optimale? Je suis déjà passée à une recherche binaire, qui a une amélioration quelque peu, mais cela reste beaucoup plus lent que je le souhaiterais, et comme je ne cherche pas réellement une valeur spécifique qui existe dans le tableau, elle ne peut jamais se terminer. de bonne heure.

Plus d'informations: Les valeurs de points flottants dans la matrice ne sont pas nécessairement distribuées uniformément (c'est-à-dire que la matrice pourrait être constituée des valeurs "1.f, 2.f, 3.f, 4.f, 100.f, 1200.f, 1200.f , 1203.f, 1400.f ".

Je fais cette opération des centaines de milliers de fois, mais je peux faire une quantité de pré-traitement sur le tableau de flotteurs, si cela améliorera le temps de recherche. Je peux absolument changer pour utiliser autre chose qu'un vecteur pour les stocker, si cela vous aidera.

10
Trevor Powell

Le code de la question (une recherche linéaire), comme vous l'avez souligné à juste titre, cela va se lendre pour de grandes matrices de flottaison. Techniquement, c'est O(n) où n est le nombre de valeurs de flotteur dans votre tableau.

En général, le mieux que vous puissiez faire pour trouver une valeur dans un tableau commandé est une recherche d'arborescence récursive de quelque nature que ce soit (par exemple, la recherche binaire), auquel cas vous pouvez obtenir une heure de recherche O (log n) dans le nombre d'éléments. dans votre tableau. O (journal n) est beaucoup meilleur que O(n) pour les grandes valeurs de n.

Mon approche suggérée serait donc une simple recherche binaire de la matrice , c'est-à-dire

  1. Définir les index entier min/max pour couvrir votre réseau de flotteur entier
  2. testez la valeur au milieu de la plage à l'index moyen = (min + max/2) contre la valeur de recherche x
  3. si X est inférieur à cette valeur, réglez max sur MID, sinon Min à mi-
  4. répéter (2-4) jusqu'à ce que vous ayez trouvé la valeur correcte

C'est un algorithme O (log n) qui devrait être assez rapide pour presque toutes les situations. Intuitivement, cela fonctionne de moitié la plage à rechercher à chaque étape jusqu'à ce que vous trouviez la valeur correcte.

Il est vraiment difficile de baisser la recherche binaire simple, donc si vous l'avez déjà implémenté correctement, vous êtes peut-être assez proche de votre optimisme déjà. Toutefois, si vous connaissez les distributions des données et/ou si vous avez une gamme limitée de valeurs de recherche (x), il reste encore d'autres astuces plus avancées que vous pouvez essayer:

  • Bucketting - Créez des godets (par exemple pour chaque intervalle entre deux entiers), chacun contenant une liste triée plus petite des valeurs de flotteur entre les deux entiers bornés plus Deux valeurs immédiatement ci-dessous et immédiatement au-dessus de chaque gamme. Vous pouvez ensuite commencer votre recherche à (Trunc (X) +0.5). Cela devrait vous donner une bonne vitesse si vous choisissez des godets de taille appropriée (il augmente efficacement le facteur de ramification de l'arborescence .....). Si les entiers ne fonctionnent pas pour vous, vous pouvez essayer des godets d'une autre précision à point fixe (par exemple, des multiples de 1/16).
  • Mappage de bits - Si la plage de valeurs de recherche possibles est suffisamment petite, vous pouvez essayer de créer une table de recherche d'une grande recherche indexée par la valeur binaire de x. Ce sera O(1) mais vous aurez peut-être besoin de beaucoup de mémoire qui sera très hostile sur votre cache ... Utilisez donc avec prudence. Ceci est particulièrement méchant parce que vous levez des loisirs Valeurs, vous pouvez donc bien avoir besoin de plusieurs AGB pour rendre compte de tous les bits moins importants ......
  • arrondi et hachage - Les tables de hachage ne sont probablement pas la meilleure structure de données de ce problème, mais si vous pouvez survivre à perdre un peu de précision, ils pourraient travailler - tout simplement Tour des bits les plus bas de vos valeurs de recherche et utilisez un hashmap pour rechercher directement la valeur correcte. Vous devrez expérimenter sur le bon comprimé entre la taille et la précision de HASHMAP, et assurez-vous également que toutes les valeurs de hasch possibles sont remplies afin que cela puisse être un peu délicat ......
  • Équilibrage de l'arborescence - Votre arbre idéal doit avoir une chance de 50% d'aller à gauche ou à droite. Donc, si vous créez un arbre basé sur la distribution de valeurs de recherche (x), vous pouvez optimiser l'arborescence pour produire des réponses avec la quantité minimale de tests. Ceci est susceptible d'être une bonne solution si de nombreuses valeurs de votre réseau flottant sont très proches ensemble, car cela vous permettra d'éviter de rechercher ces branches trop souvent.
  • (( arbres de bits de critères - Celles-ci sont toujours des arbres (alors encore O (log n) ...) mais certains cas: Vous devez toutefois convertir vos flotteurs en un format à point fixe afin de faire fonctionner les comparaisons.

Toutefois, à moins que vous ne soyez dans une situation très particulière, je vous recommande probablement de coller avec la simple recherche binaire. Les raisons:

  • il est beaucoup plus facile de mettre en œuvre
  • c'est très rapide pour la plupart des cas communs
  • les frais généraux supplémentaires des approches les plus complexes (par exemple une pression de mémoire supérieure/la pression de cache) l'emportent souvent sur les gains théoriques mineurs
  • il sera plus robuste pour les futurs changements dans les distributions de données ....
11
mikera

Cela semble assez simple:

Faites une recherche binaire du flotteur que vous souhaitez lier - O (log n).

Ensuite, l'élément à gauche est la limite inférieure et l'élément à droite est la limite supérieure.

1
Ankit Soni

La réponse évidente est de stocker les flotteurs dans un arbre . Soutenir les opérations "précédentes" et "suivantes" sont triviales dans un arbre. Donc, faites simplement une "suivante" sur votre valeur, puis faites un "précédent" sur la valeur que vous trouvez dans la première étape.

0
David Schwartz