Mój przyjaciel przeprowadza rozmowę kwalifikacyjną o pracę. Jedno z pytań podczas rozmowy kwalifikacyjnej sprawiło, że pomyślałem, po prostu chciałem uzyskać opinię.
Istnieją 2 nieujemne liczby całkowite: i oraz j. Biorąc pod uwagę następujące równanie, znajdź (optymalne) rozwiązanie, aby wykonać iterację po i i j w taki sposób, aby wyniki były sortowane.
2^i * 5^j
Tak więc pierwsze kilka rund wyglądałoby tak:
2^0 * 5^0 = 1
2^1 * 5^0 = 2
2^2 * 5^0 = 4
2^0 * 5^1 = 5
2^3 * 5^0 = 8
2^1 * 5^1 = 10
2^4 * 5^0 = 16
2^2 * 5^1 = 20
2^0 * 5^2 = 25
Chociaż się staram, nie widzę wzoru. Twoje myśli?
algorithm
optimization
hamming-numbers
smooth-numbers
Chris Eberle
źródło
źródło
2^2 < 5
ale2^3 > 5
w tym momencie zwiększasz j. Myślę, że możesz wygenerować wynik w O (n) zamiast O (nlgn). @ tom-zynch dwie zagnieżdżone pętle to O (n ^ 2). To pytanie jest bardzo ważneOdpowiedzi:
Dijkstra wywodzi wymowne rozwiązanie w „Dyscyplinie programowania”. Przypisuje problem Hammingowi. Oto moja implementacja rozwiązania Dijkstry.
źródło
tutaj jest bardziej wyrafinowany sposób na zrobienie tego (bardziej wyrafinowany niż moja poprzednia odpowiedź, to znaczy):
wyobraź sobie, że liczby są umieszczone w macierzy:
co musisz zrobić, to „przejść” po tej macierzy, zaczynając od
(0,0)
. Musisz także śledzić swoje możliwe następne ruchy. Kiedy zaczynasz od,(0,0)
masz tylko dwie opcje: albo(0,1)
albo(1,0)
: ponieważ wartość(0,1)
jest mniejsza, wybierasz to. następnie zrób to samo przy następnym wyborze(0,2)
lub(1,0)
. Do tej pory, masz następującą listę:1, 2, 4
. Twój następny ruch jest(1,0)
taki, że wartość jest mniejsza niż(0,3)
. Jednak masz teraz trzy możliwości wyboru następnego ruchu: albo(0,3)
, albo(1,1)
, albo(2,0)
.Nie potrzebujesz matrycy, aby otrzymać listę, ale musisz śledzić wszystkie swoje wybory (tj. Kiedy osiągniesz 125+, będziesz miał 4 opcje).
źródło
j
sprawdza każde 1 wyjściej ~ n^0.5
dla n-tej wartości w sekwencji, ponieważn
wartości wypełniają obszar nai x j
płaszczyźnie. Zatem tym algo jestO(n^1.5)
czas zO(n^0.5)
przestrzenią. Ale istnieje liniowy algo czasu z tą samą przestrzeniąn^0.5
, a algo mini-sterty z odpowiedzi poniżej toO(n*log(n))
czas w tej samejn^0.5
przestrzeni.Użyj min-sterty.
Umieść 1.
wyciąg-min. Powiedz, że masz x.
Wepchnij 2x i 5x do stosu.
Powtarzać.
Zamiast przechowywać x = 2 ^ i * 5 ^ j, możesz zapisać (i, j) i użyć niestandardowej funkcji porównywania.
źródło
Rozwiązanie oparte na FIFO wymaga mniejszej pojemności. Kod Pythona.
wynik:
źródło
W
O(n)
językach funkcjonalnych jest to bardzo łatwe . Listal
z2^i*5^j
numerami może być po prostu zdefiniowane jako1
, a następnie2*l
i5*l
połączyły. Oto jak to wygląda w Haskell:merge
Funkcja daje nową wartość w czasie stałym. Tak robimap
i dlatego tak robil
.źródło
union
zamiast tego nazwijmy tę funkcję „merge” , ponieważ usuwa ona duplikaty.merge
, jako częśćmergesort
, musi zachować duplikaty pochodzące z obu jego sekwencji wejściowych. ZobaczData.List.Ordered
pakiet dla powiązanych rzeczy.Data.List.Ordered.union
. To daje jedną linię:xs = 1 : union (map (2*) xs) (map (5*) xs)
[1, 2, 4, 5,...]
więc zawiera5*4
.Data.List.Ordered.union
funkcja. Nie należy tego mylićData.List.union
.Musisz śledzić ich poszczególne wykładniki i jakie będą ich sumy
więc zaczynasz od
f(0,0) --> 1
teraz, musisz zwiększyć jeden z nich:więc wiemy, że następna jest 2 - wiemy również, że możemy zwiększać wykładnik i aż do momentu, gdy suma przekroczy 5.
Idziesz w tę iz powrotem w ten sposób, aż osiągniesz określoną liczbę rund.
źródło
f(*,2)
tylko dlatego, że to znalazłeśf(a1,b+1)>f(a2,b)
. Podejście przyrostowe ostatecznie wygeneruje nieograniczoną liczbę par sąsiadujących z regionem, który już wygenerowałeś.Używając programowania dynamicznego możesz to zrobić w O (n). Podstawowa prawda jest taka, że żadne wartości i i j nie mogą dać nam 0, a aby otrzymać 1, obie wartości muszą wynosić 0;
Za każdym razem, gdy wywołujesz tę funkcję, sprawdź, czy i i j są ustawione, jeśli nie są puste, a następnie wypełnij
TwoCount
iFiveCount
Odpowiedź w C ++. Przepraszam za zły styl kodowania, ale śpieszę się :(
Oczywiście możesz użyć innych struktur danych niż tablica, aby dynamicznie zwiększyć pamięć itp. To jest tylko szkic, aby udowodnić, że to działa.
źródło
O(exp(sqrt(n)))
jest utworzenien
liczb ciągu. Istnieje algorytm liniowy , np. Podany przez ThomasAhle.O(n)
oznaczałon
to ostatnią wartość, a nie liczbę wydrukowanych pozycji, co nie jest poprawne. Nie wiem, jak działają języki funkcjonalne ani jak działa scalanie w ciągłym czasie, ale jego odpowiedź spotkała się z moim uprzejmościąSpróbuj spojrzeć na to z innej strony. Użyj licznika, aby porównać możliwe odpowiedzi z oryginalną formułą. Przepraszamy za pseudo kod.
źródło
O(4^sqrt(n))
ponieważnth
liczba sekwencji jest w przybliżeniu taka sama.To jest odpowiedni wpis w OEIS.
Wydaje się, że możliwe jest uzyskanie uporządkowanej sekwencji poprzez wygenerowanie, powiedzmy, pierwszych kilku wyrazów
a następnie, zaczynając od drugiego członu, mnożąc przez 4 i 5, aby uzyskać następne dwa
i tak dalej...
Intuicyjnie wydaje się to poprawne, ale oczywiście brakuje dowodu.
źródło
Wiesz, że log_2 (5) = 2,32. Z tego zauważamy, że 2 ^ 2 <5 i 2 ^ 3> 5.
Teraz spójrz na macierz możliwych odpowiedzi:
Teraz, w tym przykładzie, wybierz liczby w kolejności. Tam kolejność będzie:
Zauważ, że każdy wiersz zaczyna się 2 kolumny za wierszem, który go rozpoczyna. Na przykład i = 0 j = 1 występuje bezpośrednio po i = 2 j = 0.
Algorytm, który możemy wyprowadzić z tego wzorca to zatem (załóżmy j> i):
UWAGA: Kod tutaj ogranicza wartości wykładników i i j tak, aby były mniejsze niż 10. Możesz łatwo rozszerzyć ten algorytm, aby pasował do dowolnych innych dowolnych granic.
UWAGA: Czas działania tego algorytmu wynosi O (n) dla pierwszych n odpowiedzi.
UWAGA: Złożoność przestrzeni dla tego algorytmu wynosi O (1)
źródło
Moja realizacja opiera się na następujących pomysłach:
Przykład:
Kod w Javie:
źródło
obliczyć wyniki i umieścić je na posortowanej liście wraz z wartościami
i
ij
źródło
2^n*5^n
ale nie będziesz mieć2^(n+1)*5^(n-1)
mniejszego.i
ij
, prawda? W przeciwnym razie nigdy nie przejdziesz do stanu sortowania, a zatem nigdy nie zwrócisz pojedynczej wartości. Jednak dla dowolnego wybranego limitun
lista będzie błędna.i
ij
.2^i*5^j
wartości, a następnie sortujesz je. Jeśli nie masz ograniczonej liczby „wyników”, jak kiedykolwiek dojdziesz do etapu sortowania?Algorytm zaimplementowany przez user515430 autorstwa Edsgera Dijkstry (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) jest prawdopodobnie tak szybki, jak to tylko możliwe. Dzwonię pod każdy numer będący formą
2^i * 5^j
„numeru specjalnego”. Teraz odpowiedź vlada byłabyO(i*j)
ale z podwójnym algorytmem, jeden do generowania numerów specjalnych,O(i*j)
a drugi do ich sortowania (zgodnie z połączonym artykułem równieżO(i*j)
.Ale sprawdźmy algorytm Dijkstry (patrz poniżej). W tym przypadku
n
jest to ilość generowanych przez nas liczb specjalnych, czyli równai*j
. Pętlimy się raz1 -> n
iw każdej pętli wykonujemy stałą akcję. Więc ten algorytm też jestO(i*j)
. A także z całkiem błyskawiczną stałą.Moja implementacja w C ++ z GMP (opakowanie C ++) i zależność od tego
boost::lexical_cast
, chociaż można to łatwo usunąć (jestem leniwy i kto nie używa Boost?). Skompilowane zg++ -O3 test.cpp -lgmpxx -o test
. Na Q6600time ./test 1000000
daje Ubuntu 10.101145ms
.źródło
Jeśli narysujesz macierz z i jako wierszem i j jako kolumną, zobaczysz wzór. Zacznij od i = 0, a następnie po prostu przejdź przez macierz, przechodząc o 2 wiersze w górę i 1 kolumnę w prawo, aż osiągniesz szczyt macierzy (j> = 0). Następnie idź i + 1 itd ...
Więc dla i = 7 podróżujesz w ten sposób:
A dla i = 8:
Tutaj jest w Javie idąc do i = 9. Wyświetla pozycję macierzy (i, j) i wartość.
źródło
Moja intuicja :
Jeśli przyjmę wartość początkową jako 1, gdzie i = 0, j = 0, wtedy mogę utworzyć następne liczby jako (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... czyli 2,4,5 ..
Powiedzmy, że w dowolnym momencie moja liczba to x. następnie mogę utworzyć kolejne liczby w następujący sposób:
Wyjaśnienie :
Testowe uruchomienie
Zacznijmy od x = 1.
Następne trzy liczby to 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Arr [1, 2, 4, 5]
Teraz x = 2
Następne trzy liczby to [4,8,10] {Ponieważ 4 już wystąpiło, zignorujemy je} [8,10]; Arr [1, 2, 4, 5, 8, 10]
Teraz x = 4
Następne trzy liczby [8,16,20] {8 już wystąpiły, zignoruj to} [16,20] Arr [1,2,4,5,8,10,16,20]
x = 5
Kolejne trzy liczby [10,20,25] {10,20} już więc [25] zostało dodane Arr [1,2,4,5,8,10,16,20,25]
Warunek zakończenia
Analiza
źródło
Byłem tylko ciekawy, czego się spodziewać w przyszłym tygodniu i znalazłem to pytanie.
Myślę, że idea 2 ^ i nie wzrasta w tak dużych krokach jak 5 ^ j. Więc zwiększ i, o ile następny krok j nie będzie większy.
Przykład w C ++ (Qt jest opcjonalne):
Wyjście:
źródło
Oto moje rozwiązanie
Wynik:
źródło
Wiem, że prawdopodobnie się mylę, ale jest tu bardzo prosta heurystyka, ponieważ nie obejmuje ona wielu liczb, takich jak 2,3,5. Wiemy, że dla każdego i, j 2 ^ i * 5 ^ j następną sekwencją będzie 2 ^ (i-2) * 5 ^ (j + 1). Będąc google q musi mieć proste rozwiązanie.
Daje to wynik jako:
źródło
Jeśli przejdziesz przez to, co naprawdę się dzieje, gdy zwiększamy i lub j w wyrażeniu
2^i * 5^j
, to albo mnożymy przez kolejne 2 lub kolejne 5. Jeśli powtórzymy problem jako - biorąc pod uwagę określoną wartość i i j, jak znaleźć następną większa wartość, rozwiązanie staje się oczywiste.Oto zasady, które możemy dość intuicyjnie wyliczyć:
i > 1
w wyrażeniu występuje para 2s ( ), powinniśmy zastąpić je 5, aby otrzymać następną największą liczbę. Tak więci -= 2
ij += 1
.j > 0
), musimy zastąpić ją trzema 2s. Więcj -= 1
ii += 3
.i += 1
.Oto program w Rubim:
źródło
Jeśli możemy korzystać z kolekcji java, możemy mieć te liczby w O (n ^ 2)
Tutaj powerLimit musi zostać zainicjowany bardzo ostrożnie !! W zależności od tego, ile liczb chcesz.
źródło
Oto moja próba ze Scalą:
Wynik:
źródło