Powiedzmy, że masz samolot, który ma mało paliwa. O ile samolot nie zrzuci 3000 funtów wagi pasażera, nie będzie w stanie dotrzeć do następnego lotniska. Aby uratować maksymalną liczbę istnień ludzkich, chcielibyśmy najpierw zrzucić z samolotu najcięższych ludzi.
O tak, w samolocie są miliony ludzi i chcielibyśmy optymalnego algorytmu, aby znaleźć najcięższych pasażerów, bez konieczności sortowania całej listy.
To jest problem z proxy dla czegoś, co próbuję napisać w C ++. Chciałbym wykonać „częściową segregację” na podstawie manifestu pasażerskiego według wagi, ale nie wiem, ile elementów będę potrzebować. Mogę zaimplementować własny algorytm „częściowy_sort” („częściowy_sort_accumulate_until”), ale zastanawiam się, czy istnieje łatwiejszy sposób na wykonanie tego przy użyciu standardowego STL.
Odpowiedzi:
Jednym ze sposobów byłoby użycie sterty min (
std::priority_queue
w C ++). Oto, jak byś to zrobił, zakładając, że maszMinHeap
klasę. (Tak, mój przykład jest w języku C #. Myślę, że masz pomysł.)Zgodnie ze standardowymi odniesieniami czas pracy powinien być proporcjonalny do
n log k
, gdzien
jest liczba pasażerów ik
maksymalna liczba elementów na stercie. Jeśli założymy, że waga pasażerów będzie zwykle wynosić 100 funtów lub więcej, to jest mało prawdopodobne, aby na stosie znajdowało się więcej niż 30 przedmiotów w dowolnym momencie.W najgorszym przypadku pasażerowie są prezentowani w kolejności od najniższej wagi do najwyższej. Wymagałoby to dodania każdego stosu do stosu i każdego pasażera usuniętego ze stosu. Mimo to, z milionem pasażerów i przy założeniu, że najlżejszy waży 100 funtów, osiąga
n log k
to stosunkowo niewielką liczbę.Jeśli losowo otrzymujesz wagi pasażerów, wydajność jest znacznie lepsza. Używam czegoś takiego do silnika rekomendacji (wybieram 200 najlepszych pozycji z listy kilku milionów). Zwykle mam tylko 50 000 lub 70 000 przedmiotów faktycznie dodanych do sterty.
Podejrzewam, że zobaczysz coś podobnego: większość twoich kandydatów zostanie odrzucona, ponieważ są lżejsi niż najlżejsza osoba już na stosie. I
Peek
jestO(1)
operacją.Aby uzyskać więcej informacji na temat wydajności wybierania stosu i szybkiego wybierania, zobacz Kiedy teoria spotyka się z praktyką . Krótka wersja: jeśli wybierasz mniej niż 1% ogólnej liczby przedmiotów, wybór sterty jest wyraźnym zwycięzcą w stosunku do szybkiego wyboru. Ponad 1%, a następnie użyj szybkiego wyboru lub wariantu takiego jak Introselect .
źródło
std::priority_queue
Nie pomoże to jednak w przypadku problemu z serwerem proxy:
Aby 1 000 000 pasażerów zrzuciło 3000 funtów wagi, każdy pasażer musi stracić (3000/1000000) = 0,003 funta na osobę. Można to osiągnąć, odrzucając każdą koszulę, buty, a może nawet odciski paznokci, ratując wszystkich. Zakłada to efektywne zbieranie i pomijanie zanim potrzebna utrata masy wzrośnie, gdy samolot zużyje więcej paliwa.
W rzeczywistości nie pozwalają już na obcinanie paznokci na pokładzie, więc nie ma.
źródło
Poniżej znajduje się dość prosta implementacja prostego rozwiązania. Nie sądzę, że istnieje szybszy sposób, który jest w 100% poprawny.
Działa to poprzez wypełnienie zestawu „martwych ludzi”, aż osiągnie próg. Po osiągnięciu progu przeglądamy listę pasażerów starających się znaleźć tych, którzy są ciężsi od najlżejszej zmarłej osoby. Kiedy go znajdziemy, dodajemy go do listy, a następnie rozpoczynamy „Zapisywanie” najlżejszych osób z listy, dopóki nie będziemy mogli więcej zapisać.
W najgorszym przypadku będzie to działać tak samo, jak rodzaj całej listy. Ale w najlepszym przypadku („martwa lista” jest poprawnie wypełniona pierwszymi X osobami), będzie działać
O(n)
.źródło
total
obok pozycjicontinue;
Poza tym, to jest odpowiedź, którą zamierzałem opublikować. Super szybkie rozwiązanieZakładając, że wszyscy pasażerowie będą współpracować: Użyj równoległej sieci sortowania . (zobacz także to )
Oto demonstracja na żywoAktualizacja: Alternatywne wideo (przejdź do 1:00)
Poprosić pary ludzi o wymianę-wymianę - nie możesz być szybszy niż to.
źródło
n
procesory, nie ma zastosowania.@Blastfurnace był na dobrej drodze. Skorzystasz z szybkiego wyboru, gdzie osiami są progi masy. Każda partycja dzieli jeden zestaw osób na zestawy i zwraca całkowitą wagę dla każdego zestawu osób. Kontynuujesz łamanie odpowiedniego wiadra, dopóki twoje wiadra odpowiadające osobom o największej wadze nie przekroczą 3000 funtów, a twoje najniższe wiadro, które znajduje się w tym zestawie, ma 1 osobę (to znaczy, nie można go dalej podzielić).
Algorytm ten jest amortyzowany w czasie liniowym, ale w najgorszym przypadku jest kwadratowy. Myślę, że to jedyny algorytm czasu liniowego .
Oto rozwiązanie w języku Python ilustrujące ten algorytm:
Wynik:
źródło
Zakładając, że podobnie jak masy ludzi, masz dobre wyobrażenie o tym, jakie wartości maksymalne i minimalne mogą być użyte w sortowaniu radix do posortowania ich w O (n). Następnie po prostu pracuj od najcięższego końca listy w kierunku najlżejszego. Całkowity czas pracy: O (n). Niestety w STL nie ma implementacji sortowania radix, ale napisanie jej jest dość proste.
źródło
Dlaczego nie użyjesz częściowego szybkiego sortowania z inną regułą przerwania niż „posortowane”. Możesz go uruchomić, a następnie użyć tylko wyższej połowy i kontynuować, dopóki waga w tej wyższej połowie nie będzie zawierała ciężaru, który należy przynajmniej wyrzucić, niż cofniesz się o jeden krok w rekurencji i posortujesz listę. Następnie możesz zacząć wyrzucać ludzi z wyższej półki tej posortowanej listy.
źródło
Sortowanie turniejów masowo równoległych: -
Zakładając standardowe trzy miejsca po każdej stronie linii:
Poproś pasażerów siedzących przy oknie, aby przenieśli się na środkowe siedzenie, jeśli są cięższe od osoby siedzącej przy oknie.
Poproś pasażerów na środkowym siedzeniu, aby zamienili się z pasażerem siedzącym w przejściu, jeśli są ciężsi.
Poproś pasażera siedzącego na lewym przejściu, aby zamienił się z pasażerem na prawym siedzeniu, gdy są cięższe.
Sortuj bąbelkowo pasażerów na prawym miejscu w przejściu. (Wykonuje n kroków dla n rzędów). - poproś pasażerów siedzących na prawym przejściu, aby zamienili się z osobą z przodu n -1 razy.
5 Wykop je przez drzwi, aż osiągniesz 3000 funtów.
3 kroki + n kroków plus 30 kroków, jeśli masz naprawdę chudy ładunek pasażerski.
W przypadku samolotu z dwoma nawami - instrukcje są bardziej złożone, ale wydajność jest prawie taka sama.
źródło
Prawdopodobnie użyłbym
std::nth_element
do podzielenia 20 najcięższych ludzi w czasie liniowym. Następnie użyj bardziej złożonej metody, aby znaleźć i zrzucić najcięższy z ciężkich.źródło
Możesz wykonać jedno przejście przez listę, aby uzyskać średnią i standardowe odchylenie, a następnie użyć tego do przybliżenia liczby osób, które muszą odejść. Użyj częściowego sortowania, aby wygenerować listę na podstawie tej liczby. Jeśli domysły były niskie, ponownie użyj częściowego_sortowania dla pozostałych z nowym domysłem.
źródło
@James ma odpowiedź w komentarzach: a,
std::priority_queue
jeśli możesz użyć dowolnego kontenera lub kombinacjistd::make_heap
istd::pop_heap
(istd::push_heap
), jeśli chcesz użyć czegoś takiego jakstd::vector
.źródło
Oto rozwiązanie oparte na stercie przy użyciu wbudowanego modułu heapq Pythona. Jest w Pythonie, więc nie odpowiada na oryginalne pytanie, ale jest czystszy (IMHO) niż inne opublikowane rozwiązanie Pythona.
Jeśli k = liczba pasażerów do podrzucenia, a N = liczba pasażerów, najlepszym przypadkiem dla tego algorytmu jest O (N), a najgorszym przypadkiem dla tego algorytmu jest Nlog (N). Najgorszy przypadek występuje, gdy k jest przez długi czas blisko N. Oto przykład najgorszej obsady:
Jednak w tym przypadku (wyrzucanie ludzi z samolotu (przypuszczam, że ze spadochronem)) k musi być mniejsze niż 3000, co oznacza << „miliony ludzi”. Średni czas działania powinien zatem wynosić około Nlog (k), co jest liniowe względem liczby osób.
źródło