web-dev-qa-db-fra.com

Calculez la médiane d'un milliard de nombres

Si vous avez un milliard de nombres et cent ordinateurs, quelle est la meilleure façon de localiser la médiane de ces nombres?

Une solution que j'ai est:

  • Répartissez l'ensemble également entre les ordinateurs.
  • Triez-les.
  • Trouvez les médianes pour chaque ensemble.
  • Trier les ensembles sur les médianes.
  • Fusionnez deux ensembles à la fois de la médiane la plus basse à la plus élevée.

Si nous avons m1 < m2 < m3 ... puis fusionner d'abord Set1 et Set2 et dans l'ensemble résultant, nous pouvons éliminer tous les nombres inférieurs à la médiane de Set12 (fusionné). Donc, à tout moment, nous avons des ensembles de taille égale. Soit dit en passant, cela ne peut pas être fait de manière parallèle. Des idées?

123
anony

Ah, mon cerveau vient de démarrer, j'ai une suggestion sensée maintenant. Probablement trop tard s'il s'agissait d'une interview, mais tant pis:

La machine 1 sera appelée "machine de contrôle", et pour les besoins de l'argument, elle commence par toutes les données et l'envoie en paquets égaux aux 99 autres machines, ou bien les données commencent uniformément réparties entre les machines, et elles envoie 1/99 de ses données à chacun des autres. Les partitions n'ont pas besoin d'être égales, juste proches.

Chaque autre machine trie ses données et le fait d'une manière qui favorise d'abord la recherche des valeurs inférieures. Ainsi, par exemple, un tri rapide, triant toujours la partie inférieure de la partition en premier [*]. Il réécrit ses données dans la machine de contrôle dans l'ordre croissant dès qu'il le peut (en utilisant asynchrone IO afin de continuer le tri, et probablement avec Nagle activé: expérimentez un peu).

La machine de contrôle effectue une fusion à 99 voies sur les données à mesure qu'elles arrivent, mais rejette les données fusionnées, en comptant simplement le nombre de valeurs qu'elle a vues. Il calcule la médiane comme la moyenne des valeurs de 1/2 milliardième et 1/2 milliard plus un.

Cela souffre du problème "le plus lent du troupeau". L'algorithme ne peut pas terminer tant que chaque valeur inférieure à la médiane n'a pas été envoyée par une machine de tri. Il y a une chance raisonnable qu'une telle valeur soit assez élevée dans sa parcelle de données. Ainsi, une fois le partitionnement initial des données terminé, le temps d'exécution estimé est la combinaison du temps pour trier 1/99e des données et les renvoyer à l'ordinateur de contrôle, et du temps pour que le contrôle lise la moitié des données . La "combinaison" se situe quelque part entre le maximum et la somme de ces temps, probablement proche du maximum.

Mon instinct est que pour envoyer des données sur un réseau plus rapidement que de les trier (et encore moins de sélectionner la médiane), il faut que ce soit un réseau sacrément rapide. Cela pourrait être une meilleure perspective si le réseau peut être présumé instantané, par exemple si vous avez 100 cœurs avec un accès égal à RAM contenant les données.

Étant donné que les E/S réseau sont susceptibles d'être la limite, il peut y avoir des astuces que vous pouvez jouer, au moins pour les données revenant à la machine de contrôle. Par exemple, au lieu d'envoyer "1,2,3, .. 100", une machine de tri pourrait peut-être envoyer un message signifiant "100 valeurs inférieures à 101". La machine de contrôle pourrait alors effectuer une fusion modifiée, dans laquelle elle trouve le moins de toutes ces valeurs haut de gamme, puis indique à toutes les machines de tri ce que c'était, afin qu'elles puissent (a) dire à la machine de contrôle comment de nombreuses valeurs à "compter" en dessous de cette valeur, et (b) reprendre l'envoi de leurs données triées à partir de ce point.

Plus généralement, il existe probablement un jeu de devinettes astucieux de réponse-réponse que la machine de contrôle peut jouer avec les 99 machines de tri.

Cela implique cependant des allers-retours entre les machines, ce que ma première version plus simple évite. Je ne sais pas vraiment comment estimer à l'aveugle leurs performances relatives, et comme les compromis sont complexes, j'imagine qu'il existe de bien meilleures solutions que tout ce que je penserai à moi-même, en supposant que c'est toujours un vrai problème.

[*] la pile disponible le permet - votre choix de la partie à faire en premier est limité si vous n'avez pas O(N) d'espace supplémentaire. Mais si vous avez suffisamment d'espace supplémentaire, vous pouvez faites votre choix, et si vous n'avez pas assez d'espace, vous pouvez au moins utiliser ce que vous avez pour couper certains coins, en faisant d'abord la petite partie pour les premières partitions.

53
Steve Jessop
sort -g numbers | head -n 500000001 | tail -n 2 | dc -e "1 k ? ? + 2 / p"
51
DrPizza

Je déteste être le contraire ici, mais je ne crois pas que le tri soit nécessaire, et je pense que tout algorithme impliquant le tri d'un milliard/100 de nombres sera lent. Prenons un algorithme sur un ordinateur.

1) Sélectionnez 1000 valeurs au hasard parmi le milliard et utilisez-les pour vous faire une idée de la distribution des nombres, en particulier une plage.

2) Au lieu de trier les valeurs, allouez-les aux compartiments en fonction de la distribution que vous venez de calculer. Le nombre de compartiments est choisi de manière à ce que l'ordinateur puisse les gérer efficacement, mais devrait sinon être aussi grand que pratique. Les plages de compartiments doivent être de manière à ce qu'un nombre approximativement égal de valeurs se trouve dans chaque compartiment (ce n'est pas essentiel pour l'algorithme, mais cela améliore l'efficacité. 100 000 compartiments peuvent être appropriés). Notez le nombre de valeurs dans chaque compartiment. Il s'agit d'un processus O(n).

3) Découvrez quelle plage de godets se situe la médiane. Cela peut être fait en examinant simplement les nombres totaux dans chaque compartiment.

4) Trouvez la médiane réelle en examinant les valeurs dans ce compartiment. Vous pouvez utiliser un tri ici si vous le souhaitez, car vous ne triez peut-être que 10 000 numéros. Si le nombre de valeurs dans ce compartiment est important, vous pouvez à nouveau utiliser cet algorithme jusqu'à ce que vous ayez un nombre suffisamment petit pour trier.

Cette approche parallélise trivialement en divisant les valeurs entre les ordinateurs. Chaque ordinateur signale les totaux de chaque compartiment à un ordinateur "de contrôle" qui effectue l'étape 3. Pour l'étape 4, chaque ordinateur envoie les valeurs (triées) du compartiment correspondant à l'ordinateur de contrôle (vous pouvez également utiliser ces deux algorithmes en parallèle, mais cela n'en vaut probablement pas la peine).

Le processus total est O (n), car les étapes 3 et 4 sont triviales, à condition que le nombre de compartiments soit suffisamment grand.

24
DJClayworth

Un milliard est en fait une tâche assez ennuyeuse pour un ordinateur moderne. Nous parlons ici de 4 Go de 4 octets entiers ... 4 Go ... c'est le RAM de certains smartphones.

public class Median {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        int[] numbers = new int[1_000_000_000];

        System.out.println("created array after " +  (System.currentTimeMillis() - start) + " ms");

        Random Rand = new Random();
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = Rand.nextInt();
        }

        System.out.println("initialized array after " + (System.currentTimeMillis() - start) + " ms");

        Arrays.sort(numbers);

        System.out.println("sorted array after " + (System.currentTimeMillis() - start) + " ms");

        if (numbers.length % 2 == 1) {
            System.out.println("median = " + numbers[numbers.length / 2 - 1]);
        } else {
            int m1 = numbers[numbers.length / 2 - 1];
            int m2 = numbers[numbers.length / 2];
            double m = ((long) m1 + m2) / 2.0;
            System.out.println("median = " + new DecimalFormat("#.#").format(m));
        }
}

Sortie sur ma machine:

created array after 518 ms
initialized array after 10177 ms
sorted array after 102936 ms
median = 19196

Donc, cela se termine sur ma machine en moins de deux minutes (1:43 dont 0:10 pour générer des nombres aléatoires) en utilisant un seul cœur et il fait même un tri complet. Rien d'extraordinaire vraiment.

C'est sûrement une tâche intéressante pour de plus grands ensembles de nombres. Je veux juste faire une remarque ici: un milliard sont des arachides. Alors réfléchissez bien avant de lancer des solutions complexes à des tâches étonnamment simples;)

11
sfussenegger

L'estimation des statistiques d'ordre comme la médiane et le 99e centile peut être efficacement distribuée avec des algorithmes comme t-digest ou Q-digest .

En utilisant l'un ou l'autre algorithme, chaque nœud produit un résumé, qui représente la distribution des valeurs stockées localement. Les résumés sont collectés sur un seul nœud, fusionnés (additionnant efficacement les distributions), et la médiane ou tout autre centile peut ensuite être recherchée.

Cette approche est utilisée par elasticsearch et, vraisemblablement, BigQuery (selon la description de la fonction QUANTILES).

10
Richard Poole

La médiane de cet ensemble de nombres

2, 3, 5, 7, 11, 13, 67, 71, 73, 79, 83, 89, 97

est 67.

La médiane de cet ensemble de nombres

2, 3, 5, 7, 11, 13, 67, 71, 73, 79, 83, 89

a 40 ans.

En supposant que la question était d'environ 1 000 000 000 d'entiers (x) où 0> = x <= 2 147 483 647 et que le PO recherchait (élément (499 999 999) + élément (500 000 000))/2 (si les nombres ont été triés). En supposant également que les 100 ordinateurs étaient tous égaux.

en utilisant mon ordinateur portable et GigE ...

Ce que j'ai trouvé, c'est que mon ordinateur portable peut trier 10 000 000 d'Int32 en 1,3 seconde. Donc, une estimation grossière serait qu'un tri de milliards de chiffres prendrait 100 x 1,3 secondes (2 minutes 10 secondes);).

Une estimation d'un transfert de fichier unidirectionnel d'un fichier de 40 Mo sur un Gigabit Ethernet est de 0,32 seconde. Cela signifie que les résultats triés de tous les ordinateurs seront retournés dans environ 32 secondes (l'ordinateur 99 n'a obtenu son fichier que 30 secondes après le démarrage). De là, il ne devrait pas falloir longtemps pour éliminer les 499 999 998 nombres les plus bas, ajouter les 2 suivants et diviser par 2.

5
dbasnett

Cela pourrait surprendre les gens, mais si les nombres sont des entiers suffisamment petits pour tenir à l'intérieur de 32 bits (ou plus petits) - Faites simplement un tri par compartiment! Ne nécessite que 16 Go de RAM pour un nombre quelconque d'entrées 32 bits et s'exécute en O (n), ce qui devrait surpasser tous les systèmes distribués pour un n raisonnable, par exemple Un milliard.

Une fois que vous avez la liste triée, il est trivial de choisir la médiane. En fait, vous n'avez pas besoin de construire la liste triée, mais seule la recherche des compartiments devrait le faire.

Une implémentation simple est illustrée ci-dessous. Fonctionne uniquement pour les entiers 16 bits, mais l'extension à 32 bits devrait être facile.

#include <stdio.h>
#include <string.h>

int main()
{
    unsigned short buckets[65536];
    int input, n=0, count=0, i;

    // calculate buckets
    memset(buckets, 0, sizeof(buckets));
    while (scanf("%d", &input) != EOF)
    {
        buckets[input & 0xffff]++;
        n++;
    }

    // find median 
    while (count <= n/2)
    {
        count += buckets[i++];
    }

    printf("median: %d\n", i-1);

    return 0;
}

Utilisation d'un fichier texte avec un milliard (109) et courir avec time comme ça

time ./median < billion

donne un temps de fonctionnement sur ma machine 1m49.293s. La plupart du temps d'exécution est probablement le disque IO aswell.

5
vidstige

Curieusement, je pense que si vous avez suffisamment d'ordinateurs, vous feriez mieux de trier que d'utiliser des algorithmes de recherche de médiane O(n). (À moins que vos cœurs ne soient très, très lents, cependant, j'en utiliserais un et utiliserais un algorithme de recherche de médiane O(n) pour seulement les nombres 1e9; si vous aviez 1e12, cependant, cela pourrait être moins pratique. )

Quoi qu'il en soit, supposons que nous ayons plus de log n cores pour faire face à ce problème, et nous ne nous soucions pas de la consommation d'énergie, obtenant simplement la réponse rapidement. Supposons en outre qu'il s'agit d'une machine SMP avec toutes les données déjà chargées en mémoire. (Les machines à 32 cœurs de Sun sont de ce type, par exemple.)

Un thread coupe la liste à l'aveugle en morceaux de taille égale et demande aux autres threads M de les trier. Ces threads le font avec diligence, en temps (n/M) log (n/M). Ils retournent ensuite non seulement leurs médianes, mais aussi, disons, leurs 25e et 75e centiles (les pires cas pervers sont meilleurs si vous choisissez des nombres légèrement différents). Vous disposez désormais de 4 millions de plages de données. Vous triez ensuite ces plages et parcourez la liste jusqu'à ce que vous trouviez un nombre tel que, si vous jetez chaque plage plus petite ou contenant le nombre, vous aurez jeté la moitié de vos données. C'est votre limite inférieure pour la médiane. Faites de même pour la limite supérieure. Cela prend quelque chose comme M log M Temps, et tous les cœurs doivent l'attendre, donc c'est vraiment perdre M^2 log M Temps potentiel. Maintenant, vous avez votre thread unique dire aux autres de lancer toutes les données en dehors de la plage (vous devez en jeter environ la moitié à chaque passage) et de répéter - c'est une opération trivialement rapide car les données sont déjà triées. Vous ne devriez pas avoir à répéter cela plus de log(n/M) fois avant qu'il ne soit plus rapide de simplement récupérer les données restantes et d'utiliser un Finder médian O(n) standard dessus.

Ainsi, la complexité totale est quelque chose comme O((n/M) log (n/M) + M^2 log M log (n/M)). Ainsi, c'est plus rapide que O(n) tri médian sur un noyau si M >> log(n/M) et M^3 log M < n, Ce qui est vrai pour le scénario que vous avez décrit.

Je pense que c'est un vraiment mauvaise idée étant donné son inefficacité, mais c'est plus rapide.

3
Rex Kerr

Une méthode plus simple consiste à avoir des nombres pondérés.

  • Répartissez l'ensemble parmi les ordinateurs
  • Trier chaque ensemble
  • parcourir le petit ensemble et calculer les poids des éléments répétés
  • fusionner chaque 2 ensembles en 1 (chacun est déjà trié) en mettant à jour les poids
  • continuez à fusionner des ensembles jusqu'à obtenir un seul ensemble
  • parcourir cet ensemble en accumulant des poids jusqu'à ce que vous atteigniez OneBillion/2
2
Ziad Nasser

Un ordinateur suffit amplement pour résoudre le problème.

Mais supposons qu'il y a 100 ordinateurs. La seule chose complexe à faire est de trier la liste. Divisez-le en 100 parties, envoyez une partie à chaque ordinateur, laissez-les y être triées et fusionnez les parties après cela.

Prenez ensuite le numéro au milieu de la liste triée (c'est-à-dire avec l'index 5000000000).

2
Roman

Cela peut être fait plus rapidement que l'algorithme voté (n log n)

- Algorithme de sélection distribuée de statistiques de commande - O (n)
Simplifiez le problème au problème d'origine de trouver le kième nombre dans un tableau non trié.
- Histogramme de tri de comptage O (n)
Vous devez assumer certaines propriétés sur la plage des nombres - la plage peut-elle tenir dans la mémoire? - Tri par fusion externe - O (n log n) - décrit ci-dessus
Vous triez essentiellement les nombres lors de la première passe, puis trouvez la médiane sur la seconde.
- Si l'on sait quelque chose sur la distribution des nombres, d'autres algorithmes peuvent être produits.

Pour plus de détails et la mise en œuvre, voir:
http://www.fusu.us/2013/07/median-in-large-set-across-1000-servers.html

2
user1712376

Cela dépend de vos données. Le pire des cas est qu'il s'agit de nombres uniformément distribués.

Dans ce cas, vous pouvez trouver la médiane en O(N) fois comme dans cet exemple:

Supposons que vos nombres soient 2,7,5,10,1,6,4,4,6,10,4,7,1,8,4,9,9,3,4,3 (la plage est 1-10) .

Nous créons 3 seaux: 1-3, 4-7, 8-10. Notez que le haut et le bas ont la même taille.

Nous remplissons les seaux avec les nombres, comptons le nombre de chutes dans chacun, le max et le min

  • bas (5): 2,1,1,3,3, min 1, max 3
  • milieu (10): 7,5,6,4,4,6,4,4,7,4,4, min 4, max 7
  • haut (5): 10, 10, 8, 9, 9, min 8, max 10

La moyenne tombe dans le seau du milieu, nous ignorons le reste

Nous créons 3 compartiments: 4, 5-6, 7. Low commencera avec un compte de 5 et avec un maximum de 3 et haut avec un min de 8 et un compte de 5.

Pour chaque nombre, nous comptons le nombre de chutes dans le seau bas et haut, le max et le min, et gardons le seau du milieu.

  • vieux bas (5)
  • bas (5): 4, 4, 4, 4, 4, max 4
  • milieu (3): 5,6,6
  • haut (2): 7, 7, min 7
  • vieux haut (5)

Maintenant, nous pouvons calculer la médiane directement: nous avons une situation comme celle-ci

old low    low          middle  high  old high
x x x x x  4 4 4 4 4 4   5 6 6  7 7   x x x x x

la médiane est donc de 4,5.

En supposant que vous connaissiez un peu la distribution, vous pouvez affiner la façon de définir les plages pour optimiser la vitesse. Dans tous les cas, la performance devrait aller avec O (N), car 1 + 1/3 + 1/9 ... = 1,5

Vous avez besoin de min et max en raison des cas Edge (par exemple, si la médiane est la moyenne entre le max de l'ancien bas et l'élément suivant).

Toutes ces opérations peuvent être parallélisées, vous pouvez donner 1/100 des données à chaque ordinateur et calculer les 3 compartiments dans chaque nœud, puis distribuer le compartiment que vous conservez. Cela vous fait à nouveau utiliser le réseau efficacement car chaque nombre est transmis en moyenne 1,5 fois (donc O (N)). Vous pouvez même battre cela si vous ne passez que le nombre minimal entre les nœuds (par exemple, si le nœud 1 a 100 numéros et le nœud 2 a 150 numéros, alors le nœud 2 peut donner 25 numéros au nœud 1).

Sauf si vous en savez plus sur la distribution, je doute que vous puissiez faire mieux que O(N) ici, car vous devez réellement compter les éléments au moins une fois.

2
Sklivvz

Cela pourrait être fait sur des nœuds à l'aide de données qui ne sont pas triées sur des nœuds (par exemple à partir de fichiers journaux) de la manière suivante.

Il y a 1 nœud parent et 99 nœuds enfants. Les nœuds enfants ont deux appels api:

  • stats (): retourne min, max et count
  • compare (median_guess): renvoie le nombre correspondant à la valeur, le nombre inférieur à la valeur et le nombre supérieur à la valeur

Le nœud parent appelle stats () sur tous les nœuds enfants, notant le minimum et le maximum de tous les nœuds.

Une recherche binaire peut maintenant être effectuée de la manière suivante:

  1. Bisecter l'arrondi minimum et maximum - c'est la `` supposition '' médiane
  2. Si le nombre supérieur à est supérieur au nombre inférieur au nombre, définissez le minimum sur la valeur
  3. Si le nombre supérieur à est inférieur au nombre inférieur, définissez le maximum sur la supposition
  4. Si le comptage est impair terminer quand le minimum et le maximum sont égaux
  5. Si le comptage est même terminé lorsque maximum <= minimum + guess.match_count Cela peut être fait sur des nœuds utilisant des données non triées (par exemple à partir de fichiers journaux) de la manière suivante.

Il y a 1 nœud parent et 99 nœuds enfants. Les nœuds enfants ont deux appels api:

  • stats (): retourne min, max et count
  • compare (median_guess): renvoie le nombre correspondant à la valeur, le nombre inférieur à la valeur et le nombre supérieur à la valeur

Le nœud parent appelle stats () sur tous les nœuds enfants, notant le minimum et le maximum de tous les nœuds.

Une recherche binaire peut maintenant être effectuée de la manière suivante:

  1. Bisecter l'arrondi minimum et maximum - c'est la `` supposition '' médiane
  2. Si le nombre supérieur à est supérieur au nombre inférieur au nombre, définissez le minimum sur la valeur
  3. Si le nombre supérieur à est inférieur au nombre inférieur, définissez le maximum sur la supposition
  4. Si le comptage est impair terminer quand le minimum et le maximum sont égaux
  5. Si le comptage est même terminé lorsque maximum <= minimum + guess.match_count

Si les statistiques () et compare () pouvaient être pré-calculées avec un tri O(N/Mlogn/M), alors un pré-calcul O(N/M) avec une complexité mémoire de O(N) pour le pré-calcul. Ensuite, vous pouvez faire compare () en temps constant, de sorte que le tout (y compris le pré-calcul) s'exécute en O (N/MlogN/M) + O (logN)

Faites-moi savoir si j'ai fait une erreur!

1
teambob

Divisez les 10 ^ 9 numéros, 10 ^ 7 pour chaque ordinateur ~ 80 Mo sur chacun. Chaque ordinateur trie ses numéros. Ensuite, l'ordinateur 1 fusionne-trie ses propres numéros avec ceux de l'ordinateur 2, des ordinateurs 3 et 4, etc. Ensuite, l'ordinateur 1 réécrit la moitié des nombres en 2, 3 à 4, etc. Ensuite, 1 fusion fusionne les numéros des ordinateurs. 1,2,3,4, les réécrit. Etc. En fonction de la taille de RAM sur les ordinateurs que vous pouvez éviter de ne pas écrire tous les nombres sur les ordinateurs individuels à chaque étape, vous pourrez peut-être accumuler les nombres sur l'ordinateur 1 pour plusieurs étapes, mais vous faites le calcul.

Oh, obtenez enfin la moyenne des valeurs 500000000th et 500000001st (mais vérifiez qu'il y a suffisamment de 00, je n'ai pas).

EDIT: @Roman - eh bien, si vous ne pouvez pas le croire, même si c'est vrai, il est inutile de révéler la vérité ou le mensonge de la proposition. Ce que je voulais dire, c'est que la force brute bat parfois intelligemment dans une course. Il m'a fallu environ 15 secondes pour concevoir un algorithme que je suis sûr de pouvoir implémenter, qui fonctionnera et qui sera adaptable à une large gamme de tailles d'entrées et de nombres d'ordinateurs, et ajustable aux caractéristiques des ordinateurs et arrangements de mise en réseau. Si cela vous prend, ou quelqu'un d'autre, dites 15 minutes pour concevoir un algorithme plus sophistiqué, j'ai un avantage de 14m45s pour coder ma solution et la lancer.

Mais j'admets librement que ce n'est qu'une affirmation, je n'ai rien mesuré.

1

Je pense que la réponse de Steve Jessop sera la plus rapide.

Si le transfert de données réseau taille est le goulot d'étranglement, voici une autre approche.

Divide the numbers into 100 computers (10 MB each). 
Loop until we have one element in each list     
    Find the meadian in each of them with quickselect which is O(N) and we are processing in parallel. The lists will be partitioned at the end wrt median.
    Send the medians to a central computer and find the median of medians. Then send the median back to each computer. 
    For each computer, if the overall median that we just computed is smaller than its median, continue in the lower part of the list (it is already partitioned), and if larger in the upper part.
When we have one number in each list, send them to the central computer and find and return the median.
0
Cem

Vous pouvez utiliser la méthode de l'arbre du tournoi pour trouver la médiane. Nous pouvons créer un arbre avec 1000 nœuds sortants de telle sorte que chaque nœud feuille est un tableau. Nous organisons ensuite n/2 tournois entre les différents tableaux. La valeur à la racine après les n/2 tournois est le résultat.

http://www.geeksforgeeks.org/tournament-tree-and-binary-heap/

0
karan kapoor

Supposons que vous sachiez que le nombre d'entiers distincts est de (disons) 4 milliards, alors vous pouvez les regrouper en 64 000 compartiments et obtenir un nombre distribué pour chaque compartiment de chaque machine du cluster (100 ordinateurs). Combinez tous ces chefs d'accusation. Maintenant, trouvez le compartiment qui a la médiane, et cette fois, ne demandez que des compartiments pour les 64k éléments qui se trouveraient dans votre compartiment cible. Cela nécessite O(1) (spécifiquement 2) requêtes sur votre "cluster".: D

0
gandharv garg

Mon sou vaut, après tout ce qui a déjà été évoqué par d'autres:

Trouver la médiane sur une seule machine est O (N): https://en.wikipedia.org/wiki/Selection_algorithm .

L'envoi de numéros N à 100 machines est également O (N). Donc, pour rendre l'utilisation de 100 machines intéressante, soit la communication doit être relativement rapide, soit N est si grand qu'une seule machine ne peut pas le gérer alors que N/100 est faisable, ou nous voulons simplement considérer le problème mathématique sans se soucier de communication de données.

Pour faire court, je suppose donc que, dans des limites raisonnables, nous pouvons envoyer/distribuer les chiffres sans affecter l'analyse d'efficacité.

Considérez alors l'approche suivante, où une machine est assignée pour être le "maître" pour certains traitements généraux. Ce sera relativement rapide, de sorte que le "maître" participe également aux tâches courantes que chaque machine effectue.

  1. Chaque machine reçoit N/100 des nombres, calcule sa propre médiane et envoie ces informations au maître.
  2. Le maître compile une liste triée de toutes les médianes distinctes et la renvoie à chaque machine, définissant une séquence ordonnée de compartiments (sur chaque machine identique), une pour chaque valeur médiane (un compartiment à valeur unique) et une pour chaque intervalle entre médianes adjacentes. Bien sûr, il existe également les catégories inférieures et supérieures pour les valeurs inférieures à la médiane la plus basse et supérieures à la plus élevée.
  3. Chaque machine calcule le nombre de numéros qui se trouvent dans chaque compartiment et communique ces informations au maître.
  4. Le maître détermine quel compartiment contient la médiane, combien de valeurs inférieures (au total) tombent en dessous de ce compartiment et combien au-dessus.
  5. Si le compartiment sélectionné est un compartiment à valeur unique (une des médianes) ou sinon, le compartiment sélectionné ne contient que 1 (N impair) ou 2 (N pair) valeurs, nous avons terminé. Sinon, nous répétons les étapes ci-dessus avec les modifications (évidentes) suivantes:
  6. Seuls les numéros du bucket sélectionné sont (re) distribués du maître aux 100 machines, et en plus
  7. Nous n'allons pas calculer (sur chaque machine) la médiane, mais la valeur k-ème, où nous prenons en compte le nombre de nombres supérieurs qui ont été rejetés du total et le nombre de nombres inférieurs. Sur le plan conceptuel, chaque machine a également sa part des nombres bas/hauts rejetés et en tient compte lors du calcul de la nouvelle médiane dans l'ensemble qui inclut (conceptuellement) (sa part) des nombres rejetés.

Complexité temporelle:

  1. Un peu de réflexion vous convaincra qu'à chaque étape le nombre total de valeurs à analyser est réduit d'un facteur au moins deux (2 serait un cas assez malade; vous pouvez vous attendre à une réduction nettement meilleure). De cela, nous obtenons:
  2. En supposant que trouver la médiane (ou k-ème valeur), qui est O (N), prend du temps c * N où le préfacteur c ne varie pas trop avec N pour que nous puissions le prendre comme constante pour le moment, nous 'obtiendra notre résultat final dans un maximum de 2 * c * N/100 fois. L'utilisation de 100 machines nous donne donc un facteur d'accélération de 100/2 (au moins).
  3. Comme indiqué initialement: le temps nécessaire à la communication des nombres entre les machines peut rendre plus attrayant tout simplement tout faire sur une seule machine. Cependant, si nous optons pour l'approche distribuée, le nombre total de nombres à communiquer à toutes les étapes ensemble ne dépassera pas 2 * N (N pour la première fois, <= N/2 la deuxième fois, <= la moitié de celle troisième, et ainsi de suite).
0
Bert te Velde

Voyons d'abord comment trouver une médiane de n nombres sur une seule machine: j'utilise essentiellement une stratégie de partitionnement.

Problème: sélection (n, n/2): Trouver le n/2 ème nombre à partir du plus petit nombre.

Vous choisissez par exemple l'élément central k et partitionnez les données en 2 sous-tableaux. le 1er contient tous les éléments <k et le 2e contient tous les éléments> = k.

si sizeof (1er sous-tableau)> = n/2, vous savez que ce sous-tableau contient la médiane. Vous pouvez ensuite supprimer le 2ème sous-tableau. Résoudre ce problème sélection (taille du 1er sous-tableau, n/2).

Sinon, jetez ce 1er sous-tableau et résolvez sélection (2ème sous-tableau, n/2 - sizeof (1er sous-tableau))

Faites-le récursivement.

la complexité temporelle est O(n) heure prévue.

Maintenant, si nous avons beaucoup de machines, à chaque itération, nous devons traiter un tableau à diviser, nous distribuons le tableau dans des machines diff. Chaque machine traite son bloc de matrice et renvoie le résumé à la machine de contrôle du concentrateur, c'est-à-dire la taille du 1er sous-tableau et la taille du 2e sous-tableau. Les machines du concentrateur additionnent les résumés et décident dans quel sous-tableau (1er ou 2e) continuer et 2ème paramètre de sélection et le renvoyer à chaque machine. etc.

Cet algorithme peut être implémenté très soigneusement en utilisant la réduction de carte?

À quoi ça ressemble?

0
xyz

Que diriez-vous de cela: - chaque nœud peut prendre 1 milliard/100 numéros. À chaque nœud, les éléments peuvent être triés et la médiane peut être trouvée. Trouvez la médiane des médianes. nous pouvons, en agrégeant les nombres de nombres inférieurs à la médiane de la médiane sur tous les nœuds, trouver la division x%: y% que fait la médiane des médianes. Demandez maintenant à tous les nœuds de supprimer les éléments inférieurs à la médiane des médianes (en prenant l'exemple de 30%: 70% de partage) .Les nombres de 30% sont supprimés. 70% de 1 milliard représente 700 millions. Désormais, tous les nœuds qui ont supprimé moins de 3 millions de nœuds peuvent renvoyer ces nœuds supplémentaires vers un ordinateur principal. L'ordinateur principal se redistribue de telle manière que maintenant tous les nœuds auront un nombre presque égal de nœuds (7 millions). Maintenant que le problème est réduit à 700 millions de chiffres ... continue jusqu'à ce que nous ayons un ensemble plus petit qui peut être calculé sur une seule maquette.

0
anony

Si les nombres ne sont pas distincts et n'appartiennent qu'à une certaine plage, c'est-à-dire qu'ils sont répétés, alors une solution simple qui me vient à l'esprit est de répartir les nombres entre 99 machines de manière égale et de garder une machine comme maître. Désormais, chaque machine effectue une itération sur ses nombres donnés et stocke le nombre de chaque nombre dans un ensemble de hachage. Chaque fois que le nombre est répété dans l'ensemble de nombres attribué à cet ordinateur particulier, il met à jour son nombre dans l'ensemble de hachage.

Toutes les machines retournent ensuite leur jeu de hachage à la machine principale. La machine principale combine les ensembles de hachage, en additionnant le nombre de la même clé trouvée dans un ensemble de hachage. Par exemple, le jeu de hachage de la machine n ° 1 avait une entrée de ("1", 7) et le jeu de hachage de la machine n ° 2 avait une entrée de ("1", 9). ("1", 16), etc.

Une fois que les ensembles de hachage ont été fusionnés, il suffit de trier les clés, et maintenant vous pouvez facilement trouver le (n/2) e élément et le (n + 2/2) e élément, à partir de l'ensemble de hachage trié.

Cette méthode ne sera pas bénéfique si les milliards de nombres sont distincts.

0
Eric B.

Je le ferais comme ça:

au début, les 100 travaillent pour trouver le nombre le plus élevé et le plus bas; chacun de l'ordinateur a sa partie de la base de données/fichier qu'il interroge;

lorsque les nombres les plus élevés et les plus bas sont trouvés, un ordinateur lit les données et distribue chaque nombre, uniformément, au reste des 99; les nombres sont répartis par intervalles égaux; (un peut prendre de -100 millions à 0, un autre - de 0 à 100 millions, etc.);

Lors de la réception des numéros, chacun des 99 ordinateurs les trie déjà;

Ensuite, il est facile de trouver la médiane ... Voyez combien de nombres a chaque ordinateur, ajoutez-les tous (la somme du nombre de nombres, pas les nombres eux-mêmes), divisez par 2; calculer dans quel ordinateur se trouve le nombre et à quel indice;

:) voilla

P.S. Il semble qu'il y ait beaucoup de confusion ici; le MEDIAN - est le NUMÉRO AU MILIEU D'UNE LISTE DE NUMÉROS TRIÉS!

0
Ion