Biorąc pod uwagę dwie tablice; $births
zawierający listę lat urodzenia wskazującą, kiedy ktoś się urodził, oraz $deaths
listę lat śmierci wskazującą, kiedy ktoś umarł, w jaki sposób możemy znaleźć rok, w którym populacja była najwyższa?
Na przykład biorąc pod uwagę następujące tablice:
$births = [1984, 1981, 1984, 1991, 1996];
$deaths = [1991, 1984];
Powinien być rok, w którym populacja była najwyższa 1996
, ponieważ 3
ludzie żyli w tym roku, który był najwyższą liczbą ludności we wszystkich tych latach.
Oto bieżąca matematyka na ten temat:
| Narodziny | Śmierć | Ludność | | ------- | ------- | ------------ | | 1981 | | 1 | | 1984 | | 2 | | 1984 | 1984 | 2 | | 1991 | 1991 | 2 | | 1996 | | 3 |
Założenia
Możemy bezpiecznie założyć, że rok, w którym ktoś się urodzi, populacja może wzrosnąć o jeden rok, a rok, w którym ktoś umrze, populacja może się zmniejszyć o jeden. W tym przykładzie 2 osoby urodziły się w 1984 r., A 1 osoba zmarła w 1984 r., Co oznacza, że liczba ludności wzrosła o 1 w tym roku.
Możemy również bezpiecznie założyć, że liczba zgonów nigdy nie przekroczy liczby urodzeń i że śmierć nie może wystąpić, gdy populacja wynosi 0.
Możemy również bezpiecznie założyć, że lata w obu przypadkach $deaths
i $births
nigdy nie będą wartościami ujemnymi lub zmiennoprzecinkowymi ( zawsze są dodatnimi liczbami całkowitymi większymi niż 0 ).
Nie możemy jednak zakładać, że tablice zostaną posortowane lub że nie będzie zduplikowanych wartości.
Wymagania
Musimy napisać funkcję zwracającą rok, w którym wystąpiła najwyższa populacja, biorąc pod uwagę te dwie tablice jako dane wejściowe. Funkcja może powrócić 0
, false
, ""
lub NULL
( każda wartość falsey jest dopuszczalna ), gdy tablice wejściowe są puste czy populacja zawsze w 0 ° C przez cały czas. Jeśli najwyższa populacja występowała przez wiele lat, funkcja może powrócić do pierwszego roku, w którym osiągnięto najwyższą populację, lub dowolnego następnego roku.
Na przykład:
$births = [1997, 1997, 1997, 1998, 1999];
$deaths = [1998, 1999];
/* The highest population was 3 on 1997, 1998 and 1999, either answer is correct */
Dodatkowo pomocne byłoby włączenie Big O rozwiązania.
Moja najlepsza próba zrobienia tego to:
function highestPopulationYear(Array $births, Array $deaths): Int {
sort($births);
sort($deaths);
$nextBirthYear = reset($births);
$nextDeathYear = reset($deaths);
$years = [];
if ($nextBirthYear) {
$years[] = $nextBirthYear;
}
if ($nextDeathYear) {
$years[] = $nextDeathYear;
}
if ($years) {
$currentYear = max(0, ...$years);
} else {
$currentYear = 0;
}
$maxYear = $maxPopulation = $currentPopulation = 0;
while(current($births) !== false || current($deaths) !== false || $years) {
while($currentYear === $nextBirthYear) {
$currentPopulation++;
$nextBirthYear = next($births);
}
while($currentYear === $nextDeathYear) {
$currentPopulation--;
$nextDeathYear = next($deaths);
}
if ($currentPopulation >= $maxPopulation) {
$maxPopulation = $currentPopulation;
$maxYear = $currentYear;
}
$years = [];
if ($nextBirthYear) {
$years[] = $nextBirthYear;
}
if ($nextDeathYear) {
$years[] = $nextDeathYear;
}
if ($years) {
$currentYear = min($years);
} else {
$currentYear = 0;
}
}
return $maxYear;
}
Powyższy algorytm powinien działać w czasie wielomianowym, biorąc pod uwagę, że w najgorszym O(((n log n) * 2) + k)
przypadku n
jest to liczba elementów do posortowania z każdej tablicy i k
liczba lat urodzenia ( ponieważ wiemy, że tak k
jest zawszek >= y
), gdzie y
jest liczba lat śmierci. Nie jestem jednak pewien, czy istnieje bardziej wydajne rozwiązanie.
Moje zainteresowania skupiają się wyłącznie na ulepszonym Big O złożoności obliczeniowej w stosunku do istniejącego algorytmu. Złożoność pamięci nie ma znaczenia. Nie jest też optymalizacja środowiska wykonawczego. Przynajmniej nie jest to główny problem . Wszelkie drobne / większe optymalizacje środowiska wykonawczego są mile widziane, ale nie są to kluczowe czynniki.
źródło
Odpowiedzi:
Myślę, że możemy mieć
O(n log n)
czas naO(1)
dodatkowe miejsce, najpierw sortując, a następnie utrzymując bieżącą populację i globalne maksimum podczas iteracji. Starałem się wykorzystać bieżący rok jako punkt odniesienia, ale logika nadal wydawała się nieco trudna, więc nie jestem pewien, czy to się udało. Mamy nadzieję, że da to wyobrażenie o tym podejściu.Kod JavaScript (mile widziane kontrprzykłady / błędy)
Jeśli zakres roku
m
, jest rzędun
, moglibyśmy przechowywać liczby dla każdego roku w tym przedziale i miećO(n)
złożoność czasową. Gdybyśmy chcieli się zachwycić, moglibyśmy również miećO(n * log log m)
złożoność czasową, używając szybkiej wersji Y, która umożliwia wyszukiwanie następcy wO(log log m)
czasie.źródło
if(birth_i < death_j){//increment stuff + check max} else{//decrement}; birth_i||=infty; death_j||=infty
. Możesz także iterować domin(birthSize, deathSize)
. jeśli min to poród, przestań. jeśli min jest śmiercią (podejrzane ..), zatrzymaj się i sprawdź(max + birth.length-i)
Możemy to rozwiązać w czasie liniowym za pomocą sortowania kubełkowego. Powiedzmy, że rozmiar danych wejściowych wynosi n, a zakres lat to m.
Największa skumulowana wartość maksymalna jest odpowiedzią.
Czas działania wynosi O (n + m), a potrzebna dodatkowa przestrzeń to O (m).
Jest to rozwiązanie liniowe w n, jeżeli m oznacza O (n); tzn. jeśli zakres lat nie rośnie szybciej niż liczba urodzeń i zgonów. Jest to prawie na pewno prawdziwe w przypadku danych z prawdziwego świata.
źródło
O(n): Find the min and max year across births and deaths
2. iteruj raz jeszcze przez wszystkie narodziny + śmierć:O(n): Parse the births+death array, incrementing the appropriate index of the array
następnie robisz: O (m): Analizuj tablicę, śledząc sumę skumulowaną i jej maksymalną wartość. (nie musisz analizować tej tablicy - możesz śledzić MAX, zwiększając wskaźniki o 2)Najpierw zbierz
year => population change
liczbę urodzeń i zgonów w mapę ( ), posortuj je według klucza i oblicz na tej podstawie populację.Powinno to być w przybliżeniu
O(2n + n log n)
, gdzien
jest liczba urodzeń.źródło
ksort($indexed)
) staje się nieistotne.$indexed = array_count_values($births);
.Rozwiązałem ten problem wymagając pamięci
O(n+m)
[w najgorszym przypadku, najlepszym przypadkuO(n)
]oraz złożoność czasowa
O(n logn)
.Oto
n & m
długośćbirths
ideaths
tablice.Nie znam PHP ani javascript. Zaimplementowałem to w Javie, a logika jest bardzo prosta. Ale wierzę, że mój pomysł można również wdrożyć w tych językach.
Szczegóły techniki:
Użyłem
TreeMap
struktury java do przechowywania akt narodzin i zgonów.TreeMap
wstawia dane posortowane (na podstawie klucza ) jako parę (klucz, wartość), tutaj klucz to rok, a wartość to łączna suma urodzeń i zgonów (ujemna dla zgonów).Nie musimy wstawiać wartości zgonów, które miały miejsce po najwyższym roku urodzenia.
Gdy TreeMap zostanie zapełniony rekordami urodzeń i zgonów, wszystkie skumulowane sumy są aktualizowane i przechowują maksymalną populację wraz z rokiem w miarę postępu.
Przykładowe wejście i wyjście: 1
Przykładowe wejście i wyjście: 2
Tutaj zgony wystąpiły (
1914 & later
) po ostatnim roku urodzenia1913
, w ogóle nie zostały policzone, co pozwala uniknąć niepotrzebnych obliczeń.Program obejmował łącznie wszystkie
10 million
dane (narodziny i zgony łącznie) i więcej .1000 years range
3 sec.
Jeśli dane o
100 years range
tym samym rozmiarze z , zajęło1.3 sec
.Wszystkie dane wejściowe są pobierane losowo.
źródło
Uwzględni to możliwość związania roku, a także tego, czy rok śmierci innej osoby nie odpowiada urodzeniu kogoś.
źródło
Jeśli chodzi o pamięć, należy ją zachować
currentPopulation
icurrentYear
obliczyć. Rozpoczynanie od sortowania zarówno tablic, jak$births
i$deaths
tablic, jest bardzo dobrym punktem, ponieważ sortowanie bąbelków nie jest tak trudnym zadaniem, ale pozwala wyciąć niektóre rogi:nie bardzo lubię nurkować w Big O , zostawiłem to tobie.
Ponadto, jeśli ponownie odkryjesz
currentYearComputing
każdą pętlę, możesz zamienić pętle naif
instrukcje i wyjść z tylko jedną pętlą.źródło
Wypełniam bardzo wygodnie to rozwiązanie, złożoność Big O wynosi n + m
źródło
$tmpArray--
być$tmpArray[$death]--
? Proszę również przetestować za pomocą$births=[1997,1997,1998]; $deaths=[];
- Czy zwraca1998
tak, jak powinien?$births = [3,1,2,1,3,3,2]
i$deaths = [2,3,2,3,3,3]
spodziewałbym się, że wrócę2
jako najwyższy rok zaludnienia, ale kod wraca1
. W rzeczywistości Twój kod zakończył się niepowodzeniem 9 z 15 moich testów jednostkowych . I nie tylko nie mogę tego przyjąć jako najbardziej efektywnej odpowiedzi, ale nawet nie mogę zaakceptować to efektywną odpowiedź, ponieważ nie działa w ogóle.Jedno z najprostszych i najbardziej przejrzystych rozwiązań dla twojego problemu.
wyjście :
złożoność :
źródło
29.64 milliseconds