Zasady są proste:
- Pierwszych n liczb pierwszych (nie liczb pierwszych poniżej n ), należy wydrukować na standardowe wyjście oddzielone znakami nowej linii (liczby pierwsze należy wygenerować w kodzie)
- liczby pierwsze nie mogą być generowane przez wbudowaną funkcję lub przez bibliotekę , tj. użycie wbudowanej funkcji bibliotecznej, takiej jak: prime = get_nth_prime (n), is_a_prime (liczba) lub factorlist = lista_wszystkie_faktory (liczba) nie będzie bardzo kreatywna.
Punktacja - Powiedzmy, że definiujemy wynik = f ([liczba znaków w kodzie]), O ( f (n)) jest złożonością twojego algorytmu, gdzie n jest liczbą liczb pierwszych, które znajdzie. Na przykład, jeśli masz kod 300 znaków ze złożonością O (n ^ 2), wynik to 300 ^ 2 = 90000 , dla 300 znaków z O (n * ln (n)) wynik wynosi 300 * 5,7 = 1711.13 ( dla uproszczenia załóżmy, że wszystkie logi są logami naturalnymi)
Użyj dowolnego języka programowania, wygrywa najniższy wynik
Edycja: Problem został zmieniony z znajdowania „pierwszych 1000000 liczb pierwszych” na „pierwsze n liczb pierwszych” z powodu niejasności co do tego, czym jest „n” w O (f (n)), n oznacza liczbę znalezionych liczb pierwszych (znalezienie liczb pierwszych jest problem tutaj, a więc złożoność problemu zależy od liczby znalezionych liczb pierwszych)
Uwaga: aby wyjaśnić pewne nieporozumienia dotyczące złożoności, jeśli „n” oznacza liczbę liczb pierwszych, a „N” jest n-tą znalezioną liczbą pierwszą, złożoność pod względem n jest, a N nie jest równoważna, tj. O (f (n))! = O (f (N)) as, f (N)! = Stała * f (n) i N! = Stała * n, ponieważ wiemy, że n-ta funkcja pierwsza nie jest liniowa, chociaż odkąd znaleźliśmy „n” złożoność liczb pierwszych powinna być łatwo wyrażalna w kategoriach „n”.
Jak zauważył Kibbee, możesz odwiedzić tę stronę, aby zweryfikować swoje rozwiązania ( tutaj jest stara lista dokumentów Google)
Uwzględnij je w swoim rozwiązaniu -
jaki stopień złożoności ma Twój program (uwzględnij podstawową analizę, jeśli nie banalną)
długość znaku w kodzie
końcowy obliczony wynik
To jest moje pierwsze pytanie CodeGolf, więc jeśli w powyższych zasadach występuje błąd lub luka, proszę je wskazać.
źródło
1[\p:i.78498
moją odpowiedzią na to pytanie1[\p:i.1000000
. Nawet zakładając, że wewnętrznym algorytmem podstawowym J jest O (n ^ 2), mój wynik wciąż wynosiłby tylko 196.n
jest liczbą pierwszą, czy maksymalną liczbą pierwszą, i wszyscy ignorują fakt, że dodawanie liczb w zakresie0..n
jestO(logn)
, a mnożenie i dzielenie są jeszcze droższe. Proponuję podać kilka przykładowych algorytmów wraz z ich poprawną złożonością.O-tilde(k^6)
. Prowadzi to do wniosku, że każdy, kto twierdzi, że czas pracy jest lepszy, niżO-tilde(n ln n (ln(n ln n))^6)
źle zrozumiał część problemu; oraz na pytanie, jak należy sobie radzić zeO-tilde
złożonością w punktacji.Odpowiedzi:
Python (129 znaków, O (n * log log n), wynik 203,948)
Powiedziałbym, że Sito Eratostenesa jest właściwą drogą. Bardzo prosty i stosunkowo szybki.
Ulepszony kod wcześniej.
Python (
191 156152 znaków, O (n * log log n) (?), Wynik 252,620 (?))Nie mogę w ogóle obliczyć złożoności, to najlepsze przybliżenie, jakie mogę podać.
n*int(l(n)+l(l(n)))
jest górną granicą trzeciej liczbyn
pierwszej.źródło
n
ale nie na liczbie liczb pierwszych. Zakładam więc, że wynik musi być wyższy. Zobacz mój komentarz powyżej.n
? Co to jest?N=15485864
. W przypadku obliczeń złożoności opartych nan=1000000
, możesz powiedziećN=n*log(n)
(ze względu na gęstość liczb pierwszych).Haskell, n ^ 1,1 empiryczna stopa wzrostu, 89 znaków, wynik 139 (?)
Poniższe działa w wierszu poleceń GHCi, gdy biblioteka ogólna, której używa, została wcześniej załadowana. Drukuj n-tą pierwszą, opartą na 1:
let s=3:minus[5,7..](unionAll[[p*p,p*p+2*p..]|p<-s])in getLine>>=(print.((0:2:s)!!).read)
To jest nieograniczone sito Eratostenesa, wykorzystujące bibliotekę ogólnego użytku do uporządkowanych list. Empiryczna złożoność między 100 000 a 200 000 liczb pierwszych
O(n^1.1)
. Pasuje doO(n*log(n)*log(log n))
.O oszacowaniu złożoności
Zmierzyłem czas pracy dla liczb pierwszych 100 000 i 200 000, a następnie obliczyłem
logBase 2 (t2/t1)
, który wyprodukowałn^1.09
. Definiowanieg n = n*log n*log(log n)
, obliczanielogBase 2 (g 200000 / g 100000)
dajen^1.12
.Ale
89**1.1 = 139
mimo tog(89) = 600
. --- (?)Wydaje się, że do oceny należy zastosować szacowaną stopę wzrostu zamiast samej funkcji złożoności. Na przykład,
g2 n = n*((log n)**2)*log(log n)
jest dużo lepszy niżn**1.5
, ale z 100 znaków SCORE dwa produkty z3239
i1000
, odpowiednio. To nie może być prawda. Szacowanie dla zasięgu 200k / 100k dajelogBase 2 (g2 200000 / g2 100000) = 1.2
i tym samym wynik100**1.2 = 251
.Nie próbuję też drukować wszystkich liczb pierwszych, tylko n-ta liczba pierwsza.
Bez importu, 240 znaków. n ^ 1,15 empiryczna stopa wzrostu, wynik 546.
źródło
Haskell,
7289 znaków, O (n ^ 2), wynik 7921Najwyższy wynik na liczbę znaków wygrywa, prawda? Zmodyfikowany dla pierwszego N. Również najwyraźniej nie mogę użyć kalkulatora, więc mój wynik nie jest tak fatalnie zły, jak myślałem. (używając złożoności do podstawowego podziału próby, jak podano w poniższym źródle).
Zgodnie z Willem Nessem poniżej nie jest pełnym programem Haskell (w rzeczywistości opiera się na REPL). Poniżej znajduje się bardziej kompletny program z pseudo-sitem (import faktycznie oszczędza znak, ale nie lubię importu w kodzie golfowym).
Ta wersja jest bez wątpienia (n ^ 2). Algorytm jest po prostu golfową wersją naiwnego `` sita '', jak widać tutaj Old ghci 1 liner
Pozostawiając starą, zdradzającą odpowiedź, ponieważ biblioteka, do której prowadzi łącze, jest całkiem niezła.
Zobacz tutaj implementację i linki do złożoności czasu. Niestety koła mają czas wyszukiwania log (n), co spowalnia nas o czynnik.
źródło
C #, 447 Postaci, bajty 452, wynik?
skrypt, wariant, 381 znaków, 385 bajtów, wynik?
Jeśli zainstalujesz skrypty, możesz je uruchomić.
PS Napisałem to w Vimie
:D
źródło
=
i<
. Poza tym nie sądzę, aby w tym kodzie istniała różnica w bajtach i znakach - jest to 548 znaków i 548 bajtów.GolfScript (45 znaków, wynik deklarowany ~ 7708)
To robi prosty podział próbny według liczb pierwszych. Jeśli w pobliżu krawędzi Ruby (tj. Używając 1.9.3.0) arytmetyka wykorzystuje mnożenie Toom-Cook 3, więc podział próbny wynosi O (n ^ 1,465), a całkowity koszt podziałów wynosi
O((n ln n)^1.5 ln (n ln n)^0.465) = O(n^1.5 (ln n)^1.965)
†. Jednak w GolfScript dodanie elementu do tablicy wymaga skopiowania tablicy. Zoptymalizowałem to, aby skopiować listę liczb pierwszych tylko wtedy, gdy znajdzie nowąn
liczbę pierwszą, więc łącznie tylko razy. Każda operacja kopiowania maO(n)
wielkośćO(ln(n ln n)) = O(ln n)
†, co dajeO(n^2 ln n)
.I właśnie dlatego, chłopcy i dziewczęta, GolfScript jest używany raczej do gry w golfa niż do poważnego programowania.
†
O(ln (n ln n)) = O(ln n + ln ln n) = O(ln n)
. Powinienem to zauważyć, zanim skomentuję różne posty ...źródło
To takie proste, nawet mój edytor tekstu może to zrobić!
Vim: 143 naciśnięcia klawiszy (115 akcji): O (n ^ 2 * log (n)): Wynik: 101485.21
Uległość:
Dane wejściowe: N powinno znajdować się w pierwszym wierszu pustego dokumentu. Po zakończeniu każda liczba pierwsza od 2 do N będzie oddzielną linią.
Uruchamianie poleceń:
Po pierwsze, zauważ, że wszelkie polecenia z karetką przed nimi oznaczają, że musisz przytrzymać Ctrl i wpisać następną literę (np. ^ V to Ctrl-vi ^ R to Ctrl-r).
Spowoduje to zastąpienie wszystkiego w rejestrach @a, @b, @d i @p.
Ponieważ używa qpoleceń, nie można go po prostu umieścić w makrze. Oto jednak kilka wskazówek dotyczących jego uruchamiania.
qpqqdq
po prostu kasuje rejestryA^V^Ayyp<Esc>3h"aC@a<Esc>0C1<Esc>@"dd
utworzy listę liczb od 2 do N + 1. Jest to przerwa między dwiema głównymi częściami, więc kiedy to zrobisz, nie będziesz musiał tego robić ponowniempqdmd"bywo^Ra ^Rb 0 0 `pj@p ^Ra 0 ^R=@a%@b<Enter> `pdd@p 0 `dj@d<Esc>0*w*wyiWdd@0qqpmp"aywgg@dqgg@p
musi być wpisany za jednym razem. Staraj się unikać cofania, ponieważ może to coś popsuć.qdqqpq
a następnie spróbuj ponownie użyć tego wiersza.W przypadku dużych N jest to bardzo wolne. Uruchomienie N = 5000 zajęło około 27 minut; uważaj się za ostrzeżonego.
Algorytm:
Wykorzystuje podstawowy algorytm rekurencyjny do znajdowania liczb pierwszych. Biorąc pod uwagę listę wszystkich liczb pierwszych od 1 do A, A + 1 jest liczbą pierwszą, jeśli nie jest podzielna przez żadną z liczb na liście liczb pierwszych. Zacznij od A = 2 i dodaj liczby pierwsze do listy, gdy zostaną znalezione. Po N rekurencjach lista będzie zawierać wszystkie liczby pierwsze do N.
Złożoność
Algorytm ten ma złożoność O (nN), gdzie N jest liczbą wejściową, a n jest liczbą liczb pierwszych do N. Każda rekurencja bada n liczb i wykonuje się N rekurencji, dając O (nN).
Jednak N ~ n * log (n), co daje ostateczną złożoność jako O (n 2 * log (n)) ( https://en.wikipedia.org/wiki/Prime_number_theorem#Approximations_for_the_nth_prime_number )
Wyjaśnienie
Nie jest łatwo rozpoznać przepływ programu na podstawie poleceń vim, więc przepisałem go w Pythonie, wykonując ten sam przepływ. Podobnie jak kod Vima, kod python nie będzie wyświetlany, gdy osiągnie koniec. Python nie lubi zbyt dużej rekurencji; jeśli spróbujesz tego kodu z N> 150, osiągnie maksymalną głębokość rekurencji
Teraz, aby rozbić faktyczne naciśnięcia klawiszy!
qpqqdq
Czyści rejestry @d i @p. Zapewni to, że nic nie uruchomi się podczas konfigurowania tych rekurencyjnych makr.A^V^Ayyp<Esc>3h"aC@a<Esc>0C1<Esc>@"dd
Zamienia dane wejściowe na listę liczb od 2 do N + 1. Wpis N + 1 jest usuwany jako efekt uboczny konfiguracji makra @d.mpqdmd"bywo^Ra ^Rb 0 0 `pj@p ^Ra 0 ^R=@a%@b<Enter> `pdd@p 0 `dj@d<Esc>0*w*wyiWdd@0q
zapisuje makro @d, które implementuje powyższą funkcję d (). Instrukcje „If” są interesujące do wdrożenia w Vimie. Korzystając z operatora wyszukiwania *, możesz wybrać określoną ścieżkę, którą chcesz podążać. Dalej łamiemy poleceniempqd
Ustaw tutaj znak p i rozpocznij rejestrowanie makra @d. Znak p musi być ustawiony, aby istniał znany punkt, do którego można przejść podczas wykonywaniao^Ra ^Rb 0 0 `pj@p ^Ra 0 ^R=@a%@b<Enter> `pdd@p 0 `dj@d<Esc>
Zapisuje tekst instrukcji if / else0*w*wyiWdd@0
faktycznie wykonuje instrukcję if.@a @b 0 0 `pj@p @a 0 (@a%@b) `pdd@p 0 `dj@d
0
przesuwa kursor na początek linii*w*w
Przesuwa kursor do kodu, aby wykonać następny`pj@p
, który przechodzi do następnego numeru dla @a i uruchamia na nim @p.`pdd@p
, że usuwa bieżący numer @a, a następnie uruchamia @p na następnym.`dj@d
sprawdza następną liczbę dla @b, aby sprawdzić, czy jest to współczynnik @ayiWdd@0
wciąga polecenie do rejestru 0, usuwa wiersz i wykonuje polecenieq
kończy nagrywanie makra @dGdy jest to pierwsze uruchomienie,
`pdd@p
polecenie jest uruchamiane, usuwając linię N + 1.qpmp"aywgg@dq
zapisuje makro @p, które zapisuje liczbę pod kursorem, następnie przechodzi do pierwszego wpisu i uruchamia na nim @d.gg@p
faktycznie wykonuje @p, aby iterować cały plik.źródło
QBASIC, 98 znaków, Złożoność N Sqrt (N), wynik 970
źródło
IF K=
(więc długość programu nie zawiera cyfry). W tej chwili program wypisuje pierwsze n liczb pierwszych, nie licząc 2, które można naprawić, dodając?2
na początku i zmieniającK=...
naK=...-1
. Program może być również grałem trochę biorąc przestrzenie OUTJ=2 TO
,J=0 THEN
,K=...-1 THEN
, i usuwając wcięcia. Wierzę, że skutkuje to programem złożonym z 96 znaków.Scala 263 znaków
Zaktualizowano w celu dopasowania do nowych wymagań. 25% kodu dotyczy znalezienia rozsądnej górnej granicy do obliczenia liczb pierwszych poniżej.
Ja też mam sito.
Oto test empiryczny kosztów obliczeniowych niepoddany analizie:
prowadzi do następujących liczb:
Nie jestem pewien, jak obliczyć wynik. Czy warto napisać jeszcze 5 znaków?
Dla większego n zmniejsza obliczenia o około 16% w tym zakresie, ale czy w przypadku formuły punktacji nie bierzemy pod uwagę stałych czynników?
nowe uwagi Big-O:
Aby znaleźć 1 000, 10 000, 100 000 liczb pierwszych i tak dalej, używam wzoru o gęstości liczb pierwszych x => (math.log (x) * x * 1.3, który określa zewnętrzną pętlę, z której korzystam.
Tak więc dla wartości i w 1 do 6 => NPrimes (10 ^ i) działa 9399, 133768 ... razy pętla zewnętrzna.
Znalazłem tę funkcję O iteracyjnie z pomocą komentarza Petera Taylora, który zasugerował znacznie wyższą wartość potęgowania, zamiast 1,01 zasugerował 1,5:
O: (n: Int) Long
ns: List [Long] = List (102, 4152, 91532, 1612894, 25192460, 364664351)
To iloraz, jeśli użyję 1.01 jako wykładnika. Oto, co licznik znajduje empirycznie:
Pierwsze dwie wartości są wartościami odstającymi, ponieważ wprowadziłem stałą korektę dla mojego oszacowania formalnego dla małych wartości (do 1000).
Z sugestią Petera Taylorsa o wersji 1.5 wyglądałoby to tak:
Teraz z moją wartością przechodzę do:
Ale nie jestem pewien, jak blisko mojej funkcji O mogę się zbliżyć do obserwowanych wartości.
źródło
O(M^1.5 / ln M)
i za każdym razem wykonujO(ln M)
pracę (dodawanie), więc ogólnie jestO(M^1.5) = O((n ln n)^1.5)
.def O(n:Int) = (math.pow((n * math.log (n)), 1.02)).toLong
zbliżam się znacznie do wartości, które empirycznie znalazłem w moim liczniku. Wstawiam moje ustalenia do mojego postu.Ruby 66 znaków, O (n ^ 2) Wynik - 4356
lazy
jest dostępny od Rubiego 2.0 i1.0/0
jest fajną sztuczką, aby uzyskać nieskończony zasięg:źródło
(2..(1.0/0)).lazy.select{|i|!(2...i).any?{|j|i%j<1}}.take(n).to_a
(2..(1.0/0)).lazy.select{|i|(2..i).one?{|j|i%j<1}}.take(n).to_a
. To goli jeszcze dwie postacie.(2..(1.0/0)).lazy.select{|i|!(2...i).any?{|j|i%j<1}}.first(n)
spowoduje 61 znaków.Ruby, 84 znaki, 84 bajty, wynik?
Ten jest prawdopodobnie trochę za nowicjuszem na te części, ale dobrze się bawiłem. Po prostu zapętla się, aż
f
(znalezione liczby pierwsze) będzie równan
pożądanej liczbie liczb pierwszych do znalezienia.Zabawne jest to, że dla każdej pętli tworzy tablicę od 2 do jeden mniejszą niż liczba sprawdzana. Następnie mapuje każdy element w tablicy, aby był modułem pierwotnej liczby i elementu, i sprawdza, czy którykolwiek z wyników jest równy zero.
Nie mam też pojęcia, jak to zdobyć.
Aktualizacja
Kod skompresowany i zawierał (całkowicie dowolną) wartość dla
n
Oryginalny
i += 1
Bit iuntil
pętla są swego rodzaju skoki na mnie jako obszary do poprawy, ale na tym torze Jestem rodzaj zakleszczony. W każdym razie fajnie było o tym myśleć.źródło
Scala, 124 znaki
Prosty podział próbny do pierwiastka kwadratowego. Złożoność powinna zatem wynosić O (n ^ (1,5 + epsilon))
124 ^ 1,5 <1381, więc to byłby mój wynik?
źródło
Perl - 94 znaków, O (n log (n)) - Wynik: 427
Python - 113 znaków
źródło
AWK,
9686 bajtówPodtytuł: Look Mom! Tylko dodawanie i księgowość!
Plik
fsoe3.awk
:Biegać:
BASH, 133 bajty
Plik
x.bash
:Biegać:
Liczby pierwsze oblicza się, pozwalając znalezionym liczbom pierwszym przeskakiwać na „taśmie liczb całkowitych dodatnich”. Zasadniczo jest to serializowane sito Eratostenesa.
... jest tym samym algorytmem w Pythonie i wypisuje czas, w którym
l
znaleziono -tą liczbę pierwszą zamiast samej liczby pierwszej.Dane wyjściowe wykreślone za pomocą
gnuplot
dają następujące wyniki:Skoki prawdopodobnie mają coś wspólnego z opóźnieniami we / wy plików z powodu zapisywania buforowanych danych na dysku ...
Wykorzystanie znacznie większej liczby liczb pierwszych do wprowadzenia spowoduje dodatkowe opóźnienia zależne od systemu, np. Tablica reprezentująca „taśmę dodatnich liczb całkowitych” rośnie stale i prędzej czy później sprawi, że każdy komputer będzie żądał więcej pamięci RAM (lub późniejszej wymiany).
... więc zrozumienie złożoności poprzez spojrzenie na dane eksperymentalne nie bardzo pomaga ... :-(
Teraz licząc dodatki potrzebne do znalezienia
n
liczb pierwszych:źródło
Gnuplot
z,set term xterm
a następnie zrzut ekranuxterm
okna graficznego (prawdopodobnie prawie zapomniana funkcja). ;-)Scala 121 (99 bez płyty głównej klasy Main)
źródło
Python 3,
117106 bajtówTo rozwiązanie jest nieco trywialne, ponieważ zwraca 0, gdzie liczba nie jest liczbą pierwszą, ale mimo to opublikuję:
Ponadto nie jestem pewien, jak obliczyć złożoność algorytmu. Proszę nie głosować z tego powodu. Zamiast tego bądź miły i komentuj, jak mógłbym to wypracować. Powiedz mi też, jak mogę to skrócić.
źródło
print(i)
na tej samej linii, co do pętli i usunąć spacje win [2]
,0 if
,0 in [i%j
i+1,2)] else
.Haskell (52² = 2704)
źródło
Perl 6, 152 bajty, O (n log n log (n log n) log (log (n log n))) (?), 9594.79 punktów
Według tej strony złożoność bitowa znalezienia wszystkich liczb pierwszych do n wynosi O (n log n log log n); powyższa złożoność wykorzystuje fakt, że n-ta liczba pierwsza jest proporcjonalna do n log n.
źródło
Groovy (50 bajtów) - O (n * sqrt (n)) - wynik 353,553390593
Pobiera in i wyświetla wszystkie liczby od 1 do n, które są liczbą pierwszą.
Algorytm, który wybrałem, wyprowadza tylko liczby pierwsze n> 2, więc wymagane jest dodanie 1,2 na początku.
Awaria
x%it
- Implikowana prawda, jeśli nie jest podzielna, fałsz, jeśli tak jest.(2..x**0.5).every{...}
- Dla wszystkich wartości między 2 a sqrt (x) upewnij się, że nie są podzielne, aby to zwróciło true, musi zwrócić true dla każdego .(1..it).findAll{x->...}
- Dla wszystkich wartości od 1 do n znajdź wszystkie, które spełniają kryteria niepodzielności między 2 a sqrt (n).{[1,2]+...}
- Dodaj 1 i 2, ponieważ są zawsze pierwsze i nigdy nie są objęte algorytmem.źródło
Rakieta 155 bajtów
Przechowuje listę znalezionych liczb pierwszych i sprawdza podzielność każdej kolejnej liczby przez już znalezione liczby pierwsze. Co więcej, sprawdza tylko pierwiastek kwadratowy z testowanej liczby, ponieważ to wystarczy.
Nie golfowany:
Testowanie:
Wydajność:
źródło
awk 45 (złożoność N ^ 2)
inny
awk
, dla liczb pierwszych do 100 takich jak tenKod liczony część golfa jest
które można umieścić w pliku skryptu i uruchomić jako
awk -f prime.awk <(seq 100)
źródło
JavaScript, 61 znaków
Nieco gorzej niż O (n ^ 2), zabraknie miejsca na stosie dla dużego n.
źródło