8
votes

Façon la plus efficace de compter des occurrences?

Je cherche à calculer l'entropie et les informations mutuelles un nombre énorme de fois dans le code critique de la performance. Comme étape intermédiaire, je dois compter le nombre d'occurrences de chaque valeur. Par exemple:

uint[] myArray = [1,1,2,1,4,5,2];
uint[] occurrences = countOccurrences(myArray);
// Occurrences == [3, 2, 1, 1] or some permutation of that.
// 3 occurrences of 1, 2 occurrences of 2, one each of 4 and 5.


3 commentaires

Je ne peux pas penser à plus que ce que vous avez dit ci-dessus. Triez le tableau, puis passez-la séquentiellement en passe.


Peut-être que vous pourriez utiliser une sorte de hadoop ou de mapper / réduire pour accélérer votre algorithme? Autre que cela je ne vois rien.


@kgrad: J'utilise déjà pleinement tous mes noyaux en parallèle la boucle extérieure, il n'ya donc pas de point en parallèle une exécution individuelle de cette fonction.


3 Réponses :


1
votes

Avec un tableau d'entiers, comme dans l'exemple, la manière la plus efficace serait d'avoir une matrice de int s et index qu'il basé sur vos valeurs (comme vous semblez le faire déjà).

Si vous ne pouvez pas faire cela, je ne peux pas penser à une meilleure alternative qu'un hashmap. Vous avez juste besoin d'avoir un algorithme de hachage rapide. Vous ne pouvez pas obtenir mieux que o (n) performances si vous souhaitez utiliser toutes vos données. Est-ce une option d'utiliser uniquement une partie des données que vous avez?

(Notez que le tri et le comptage sont asymptotiquement plus lentement (O (n * journal (n))) que l'utilisation d'une solution basée sur HASHMAP (O (N)).)


1 commentaires

Le tri est asymptotique plus lent, mais dans la situation d'entropie élevée (pas que de nombreuses occurrences de chaque valeur), il est plus rapide dans la pratique, même pour très grand n (dans les millions), car il est plus efficace de la cache.



3
votes

Le hachage est généralement plus évolutif, car une autre réponse indique. Cependant, pour de nombreuses distributions possibles (et de nombreux cas réels de la vie réelle, où les compartiments sont souvent triés, en fonction de la manière dont la matrice globale a été assemblée), Timsort est souvent" de manière prénaturelle "(plus proche de O (n) que to O (n log n)) - j'entends qu'il est Va devenir probablement l'algorithme de tri standard / par défaut en Java à certaines données futures raisonnablement proches (son algorithme de tri standard en Python depuis des années).

Il n'y a pas de moyen probablement un moyen de résoudre ces problèmes, sauf au point de repère sur une sélection de cas représentatifs de la charge de travail réelle que vous prévoyez connaître (avec le risque évident que vous pouvez choisir un échantillon qui est arrivé à être biaisé / non représentatif - ce n'est pas un risque faible si vous essayez de créer une bibliothèque qui sera utilisée par de nombreux utilisateurs externes en dehors de votre contrôle).


1 commentaires

Je ne savais pas sur Timsort , semble intéressant!



2
votes

Veuillez raconter plus sur vos données.

  • Combien d'articles y a-t-il?
  • Quel est le ratio attendu d'éléments uniques au total des articles?
  • Quelle est la répartition des valeurs réelles de vos entiers? Sont-ils généralement assez petits pour utiliser un tableau de comptage simple? Ou sont-ils regroupés dans des groupes raisonnablement étroits? C.

    Dans tous les cas, je suggère l'idée suivante: une fausse escorte modifiée pour compter les doublons.

    C'est-à-dire que vous travaillez en termes de nombres, mais que des paires (nombre, fréquence) (vous pouvez utiliser une représentation intelligente efficace de la mémoire intelligente pour cela, par exemple deux tableaux au lieu d'une paire de paires, etc.) .

    Vous commencez avec [(x1,1), (x2,1), ...] et faites une fusion comme d'habitude, mais lorsque vous fusionnez deux listes qui commencent avec la même valeur, vous mettez le valeur dans la liste de sortie avec leur somme de seventions. Sur votre exemple: xxx

    ceci pourrait être considérablement amélioré en utilisant des astuces intelligentes pour effectuer une réduction initiale de la matrice (obtenir une gamme de valeur: des paires de présence beaucoup plus petite que l'original, mais la somme de «seary» pour chaque «valeur» est égale au nombre de survers de «valeur» dans le tableau d'origine). Par exemple, diviser le tableau en blocs continu où les valeurs ne diffèrent qu'au plus de 256 ou 65536 et utilisent un petit tableau pour compter des occurrences à l'intérieur de chaque bloc. En fait, cette astuce peut également être appliquée à des phases de fusion ultérieure.


0 commentaires