Disons que vous avez un avion et qu'il est faible en carburant. À moins que l'avion ne pèse 3000 livres de passagers, il ne pourra pas atteindre le prochain aéroport. Pour sauver le maximum de vies, nous aimerions d'abord jeter les personnes les plus lourdes hors de l'avion.
Et oh oui, il y a des millions de personnes dans l'avion, et nous aimerions un algorithme optimal pour trouver les passagers les plus lourds, sans nécessairement trier la liste entière.
Il s'agit d'un problème de proxy pour quelque chose que j'essaie de coder en C ++. Je voudrais faire un "partial_sort" sur le manifeste passager en poids, mais je ne sais pas de combien d'éléments je vais avoir besoin. Je pourrais implémenter mon propre algorithme "partial_sort" ("partial_sort_accumulate_until"), mais je me demande s'il existe un moyen plus simple de le faire en utilisant la norme STL.
Réponses:
Une façon serait d'utiliser un tas min (
std::priority_queue
en C ++). Voici comment vous le feriez, en supposant que vous ayez eu unMinHeap
cours. (Oui, mon exemple est en C #. Je pense que vous avez l'idée.)Selon les références standard, le temps de course devrait être proportionnel à
n log k
, oùn
est le nombre de passagers etk
le nombre maximum d'articles sur le tas. Si nous supposons que le poids des passagers sera généralement de 100 livres ou plus, il est peu probable que le tas contienne plus de 30 articles à tout moment.Le pire serait que les passagers soient présentés dans l'ordre, du poids le plus faible au plus élevé. Cela nécessiterait que chaque passager soit ajouté au tas et que chaque passager soit retiré du tas. Pourtant, avec un million de passagers et en supposant que le plus léger pèse 100 livres, cela équivaut
n log k
à un nombre raisonnablement petit.Si vous obtenez les poids des passagers au hasard, les performances sont bien meilleures. J'utilise quelque chose comme ça pour un moteur de recommandation (je sélectionne les 200 premiers éléments d'une liste de plusieurs millions). Je me retrouve généralement avec seulement 50 000 ou 70 000 éléments réellement ajoutés au tas.
Je soupçonne que vous verrez quelque chose d'assez similaire: la majorité de vos candidats seront rejetés parce qu'ils sont plus légers que la personne la plus légère déjà sur le tas. Et
Peek
c'est uneO(1)
opération.Pour plus d'informations sur les performances de la sélection de tas et de la sélection rapide, voir Quand la théorie rencontre la pratique . Version courte: si vous sélectionnez moins de 1% du nombre total d'articles, la sélection en tas est clairement gagnante par rapport à la sélection rapide. Plus de 1%, puis utilisez la sélection rapide ou une variante comme Introselect .
la source
std::priority_queue
Cela n'aidera cependant pas votre problème de proxy:
Pour que 1000000 de passagers perdent 3000 livres de poids, chaque passager doit perdre (3000/1000000) = 0,003 lb par personne. Cela pourrait être réalisé en jetant la chemise ou les chaussures de chacun, ou probablement même des coupures d'ongles, sauvant tout le monde. Cela suppose une collecte et un largage efficaces avant que la perte de poids nécessaire n'augmente à mesure que l'avion utilise plus de carburant.
En fait, ils n'autorisent plus les coupe-ongles à bord, donc c'est fini.
la source
Voici une implémentation assez simple de la solution simple. Je ne pense pas qu'il existe un moyen plus rapide qui soit 100% correct.
Cela fonctionne en remplissant l'ensemble des «morts» jusqu'à ce qu'il atteigne le seuil. Une fois le seuil atteint, nous continuons à parcourir la liste des passagers essayant de trouver ceux qui sont plus lourds que le mort le plus léger. Lorsque nous en avons trouvé un, nous les ajoutons à la liste, puis nous commençons à «enregistrer» les personnes les plus légères de la liste jusqu'à ce que nous ne puissions plus en enregistrer.
Dans le pire des cas, cela fonctionnera à peu près comme une sorte de liste entière. Mais dans le meilleur des cas (la "liste morte" est correctement remplie avec les X premières personnes), cela fonctionnera
O(n)
.la source
total
côté decontinue;
Autre que cela, c'est la réponse que j'allais poster. Solution super rapideEn supposant que tous les passagers coopéreront: utilisez un réseau de tri parallèle . (voir aussi ceci )
Voici une démonstration en directMise à jour: vidéo alternative (passer à 1h00)
Demander à des paires de personnes de comparer-échanger - vous ne pouvez pas aller plus vite que cela.
la source
n
processeurs ne tient pas.@Blastfurnace était sur la bonne voie. Vous utilisez la sélection rapide où les pivots sont des seuils de poids. Chaque partition divise un ensemble de personnes en ensembles et renvoie le poids total de chaque ensemble de personnes. Vous continuez à casser le seau approprié jusqu'à ce que vos seaux correspondant aux personnes dont le poids est le plus élevé dépassent 3000 livres, et votre seau le plus bas qui se trouve dans cet ensemble ait 1 personne (c'est-à-dire qu'il ne peut plus être divisé).
Cet algorithme est amorti en temps linéaire, mais dans le pire des cas quadratique. Je pense que c'est le seul algorithme de temps linéaire .
Voici une solution Python qui illustre cet algorithme:
Production:
la source
En supposant que, comme les poids des personnes, vous avez une bonne idée de ce que les valeurs maximales et minimales sont susceptibles d'être utilisées, utilisez un tri radix pour les trier dans O (n). Ensuite, travaillez simplement de l'extrémité la plus lourde de la liste vers la plus légère. Durée totale de fonctionnement: O (n). Malheureusement, il n'y a pas d'implémentation d'un tri radix dans la STL, mais c'est assez simple à écrire.
la source
Pourquoi n'utilisez-vous pas un tri rapide partiel avec une règle d'abandon différente de "triée". Vous pouvez l'exécuter, puis utiliser uniquement la moitié supérieure et continuer jusqu'à ce que le poids dans cette moitié supérieure ne contienne plus le poids qui doit au moins être jeté, que de revenir en arrière d'une étape dans la récursivité et de trier la liste. Après cela, vous pouvez commencer à expulser les gens du haut de la liste triée.
la source
Type de tournoi massivement parallèle: -
En supposant une norme de trois sièges de chaque côté de l'ailse: -
Demandez aux passagers assis dans le siège de fenêtre de se déplacer vers le siège du milieu s'ils sont plus lourds que la personne assise dans le siège de fenêtre.
Demandez aux passagers du siège central de changer de place avec le passager du siège côté couloir s'ils sont plus lourds.
Demandez au passager du siège de l'allée gauche de permuter avec le passager du siège de l'allée droite s'il est plus lourd.
Bulle trie les passagers dans le siège de l'allée droite. (Prend n étapes pour n lignes). - demander aux passagers assis dans l'allée droite d'échanger avec la personne devant n -1 fois.
5 Donnez-leur un coup de pied jusqu'à ce que vous atteigniez 3000 livres.
3 étapes + n étapes plus 30 étapes si vous avez une charge de passagers vraiment maigre.
Pour un avion à deux couloirs - les instructions sont plus complexes mais les performances sont à peu près les mêmes.
la source
J'utiliserais probablement
std::nth_element
pour séparer les 20 personnes les plus lourdes en temps linéaire. Ensuite, utilisez une méthode plus complexe pour trouver et heurter le plus lourd des lourds.la source
Vous pouvez faire un passage sur la liste pour obtenir la moyenne et l'écart type, puis l'utiliser pour approximer le nombre de personnes qui doivent y aller. Utilisez partial_sort pour générer la liste en fonction de ce nombre. Si la supposition était faible, utilisez à nouveau partial_sort sur le reste avec une nouvelle supposition.
la source
@James a la réponse dans les commentaires: a
std::priority_queue
si vous pouvez utiliser n'importe quel conteneur, ou une combinaison destd::make_heap
etstd::pop_heap
(etstd::push_heap
) si vous voulez utiliser quelque chose comme astd::vector
.la source
Voici une solution basée sur le tas utilisant le module heapq intégré de Python. Il est en Python, donc ne répond pas à la question d'origine, mais il est plus propre (à mon humble avis) que l'autre solution Python publiée.
Si k = le nombre de passagers à lancer et N = le nombre de passagers, alors le meilleur cas pour cet algorithme est O (N) et le pire cas pour cet algorithme est Nlog (N). Le pire des cas se produit si k est proche de N pendant une longue période. Voici un exemple de la pire distribution:
Cependant, dans ce cas (jeter des gens hors de l'avion (avec un parachute, je présume)), alors k doit être inférieur à 3000, ce qui représente << "des millions de personnes". Le temps d'exécution moyen devrait donc être d'environ Nlog (k), qui est linéaire par rapport au nombre de personnes.
la source