To jest pytanie z poradami do gry w golfa w Pythonie, dotyczące pytania zła liczba na Anarchy Golf .
Liczba jest zła, jeśli jej rozwinięcie binarne ma parzystą liczbę 1. Wyzwanie polega na wydrukowaniu pierwszych 400 złych liczb 0,3,5,...,795,797,798
, po jednym w wierszu.
Zgłoszenia w języku Python 2 są prowadzone przez llhuii z 42-bajtowym rozwiązaniem. Następny najlepszy to 46 bajtów po mitchu, a następnie pięć 47-bajtowych zgłoszeń. Wygląda na to, że Lhuhu odkrył coś naprawdę magicznego, co wymyka się wielu silnym golfistom Python przez ponad 2 lata. Oszczędność 4 lub 5 bajtów jest ogromna jak na tak krótki golf.
Nadal mam 47 bajtów. Mam nadzieję, że uda nam się złamać tę łamigłówkę jako społeczność. Jeśli otrzymamy odpowiedź wspólnie, prześlę ją pod nazwiskami wszystkich, którzy wnieśli swój wkład. Odpowiedź na to pytanie może być fragmentem kodu, nowym pomysłem lub analizą. Jeśli jesteś llhuii, nie psuj tego jeszcze dla nas.
Chociaż zgłoszenia nie zostały ujawnione, ponieważ ten problem jest nieskończony, otrzymaliśmy kilka wskazówek. Zwycięskie zgłoszenie zajęło 0,1699 sekundy, znacznie dłużej niż jakikolwiek inny, co sugeruje nieefektywną metodę. Ze statystyk bajtów, spośród 42 znaków, 23 to znaki alfanumeryczne, [0-9A-Za-z]
a 19 to symbole ASCII. Oznacza to, że w rozwiązaniu Lhuhu nie ma białych znaków.
Możesz przetestować swój kod na stronie problemu , wybierając Python z menu rozwijanego języka lub przesyłając .py
plik. Uwaga:
- Używany jest Python 2.7
- Twój kod musi być pełnym programem, który drukuje
- Nie ma danych wejściowych dla tego problemu, takich jak złożoność kolmogorowa
- Twój program musi po prostu wydrukować 400 podanych wartości, nawet jeśli spowodowałoby to uszkodzenie większych wartości
- Programy mają 2 sekundy do uruchomienia
- Programy mogą zakończyć się z błędem
- Możesz użyć
exec
; „exec jest odrzucony” odnosi się do powłoki exec
Odpowiedzi:
To nie jest to samo rozwiązanie co llhuii, ale ma również 42 bajty.
Wypróbuj online!
Dzięki @JonathanFrech mamy teraz 40 bajtów.
Wypróbuj online!
Jest jeszcze jeden bajt do zapisania, w sumie 39.
Wypróbuj online!
źródło
print+n
, ich rozwiązanie musi być inne niż moje.-
znak przez przeniesienie zprint~n
czyprint-n
i za pomocą&
lub~
, chociaż nie mam nic do pracy dostał. Jest teżn=0;exec"print n;d=n^n+2;n^=d^-d%3;"*400
ładny, ale 40 bajtów.print-n
wydaje się mało prawdopodobne, ponieważ nie ma łatwego związku między ustawionymi bitamin
i-n
.print~n
brzmi bardziej obiecująco w teorii, ale nie mogę dostać poniżej 40 bajtów przy takim podejściu.Zdobycie 39 bajtów
To jest wyjaśnienie, w jaki sposób otrzymałem 39-bajtowe rozwiązanie, które Dennis i JonathanFrech również znaleźli osobno. A raczej wyjaśnia, w jaki sposób można dojść do odpowiedzi z perspektywy czasu, w sposób o wiele ładniejszy niż moja faktyczna ścieżka do niej, pełna błotnego rozumowania i ślepych zaułków.
Pisząc to trochę mniej golfa i więcej parens, wygląda to tak:
Bit parzystości
Zaczynamy od pomysłu z mojego 47-bajtowego rozwiązania, aby wypisywać wszystkie liczby w postaci, w
n=2*k+b
której sięk
liczy0,1,...,399
ib
jest bitem parzystości, który sprawia, że ogólna liczba jedynek jest parzysta.Napiszmy
par(x)
na bit parzystości zx
, czyli XOR (^
) wszystkich bitówx
. Jest to 0, jeśli liczba parzysta jest 1-bitowa (liczba jest zła), a 1, jeśli liczba parzysta jest 1-bitowa. Ponieważn=2*k+b
mamypar(n) = par(k)^b
, więc aby osiągnąć złopar(n)==0
, potrzebujemyb=par(k)
, tzn. Ostatni bitn
to parzystość bitów poprzednich bitów.Moje pierwsze starania w golfa były wyrażającym
par(k)
, najpierw bezpośrednio zbin(k).count('1')%2
, a następnie bit manipulacji .Aktualizacje parzystości
Wydawało się jednak, że nie było krótkiego wyrazu. Zamiast tego pomógł sobie uświadomić, że jest więcej informacji do pracy. Zamiast obliczać parzystość bitową bieżącej liczby,
możemy zaktualizować bit parzystości jak możemy zwiększyć
k
dok+1
.Oznacza to, że ponieważ zliczamy
k=0,1,2,...
, musimy jedynie zachować bieżącą parzystość bitową, a nie za każdym razem obliczać ją od zera. Aktualizacja parzystości bitówpar(k+1)^par(k)
to parzystość liczby bitów przechodzących odk
dok+1
, to znaczypar((k+1)^k)
.Forma
(k+1)^k
Teraz musimy obliczyć
par((k+1)^k)
. Może się wydawać, że nigdzie nie doszliśmy, ponieważ parzystość bitów to problem, który staramy się rozwiązać. Ale liczby wyrażone jako(k+1)^k
mają postać1,3,7,15,..
, czyli jeden poniżej potęgi 2, fakt często używany w bitowych hackach . Zobaczmy, dlaczego tak jest.Kiedy zwiększamy
k
, efektem przeniesień binarnych jest odwrócenie ostatniego0
i wszystkich1
po jego prawej stronie, tworząc nowe prowadzenie,0
jeśli nie było żadnego. Na przykład weźk=43=0b101011
Kolumny powodujące przenoszenie są oznaczone symbolem
*
. Mają one1
zmieni się0
i przechodzą na kawałku przeniesienia1
, który utrzymuje rozmnożeniowego lewo, aż natrafi0
ink
, która zmienia się1
. Nie ma to wpływu na bity dalej po lewej stronie. Tak więc, gdyk^(k+1)
kontrole, które nieco zmieniają pozycjek
nak+1
to znajdzie pozycje na prawo0
i na1
„s po prawej stronie. Oznacza to, że zmienione bity tworzą przyrostek, więc wynikiem są 0, po których następuje jeden lub więcej 1. Bez wiodących zer istnieją liczby binarne,1, 11, 111, 1111, ...
które są o jeden poniżej potęgi 2.Przetwarzanie danych
par((k+1)^k)
Teraz, gdy rozumiemy, że
(k+1)^k
jest to ograniczone1,3,7,15,...
, znajdźmy sposób na obliczenie parzystości bitowej takich liczb. Przydatnym faktem jest to, że1,2,4,8,16,...
naprzemiennie modulo3
między1
i2
, ponieważ2==-1 mod 3
. Biorąc więc1,3,7,15,31,63...
modulo3
daje1,0,1,0,1,0...
, które są dokładnie ich bitowymi parzystościami. Doskonały!Możemy więc wykonać aktualizację
par(k+1) = par(k) ^ par((k+1)^k)
jakoUżywając
b
jako zmiennej, w której przechowujemy parzystość, wygląda to takPisanie kodu
Łącząc to w kod, zaczynamy
k
i bit parzystościb
zarówno w0
, a następnie wielokrotnie drukujemyn=2*k+b
i aktualizujemyb=b^((k+1)^k)%3
ik=k+1
.46 bajtów
Wypróbuj online!
Usunęliśmy pareny, ponieważ pierwszeństwo
k+1
w Pythonie jest dodawane jako pierwsze, co dziwne, jak się wydaje.((k+1)^k)%3
Ulepszenia kodu
Możemy to zrobić lepiej, pracując bezpośrednio z jedną zmienną
n=2*k+b
i wykonując aktualizacje bezpośrednio na niej. Robieniek+=1
odpowiadan+=2
. I aktualizacjab^=(k+1^k)%3
odpowiadan^=(k+1^k)%3
. Tutajk=n/2
przed aktualizacjąn
.44 bajty
Wypróbuj online!
Możemy skrócić
n/2+1^n/2
(pamiętaj, że jest(n/2+1)^n/2
) poprzez przepisaniePonieważ
/2
usuwa ostatni bit, nie ma znaczenia, czy zrobimy to przed, czy po przeforsowaniu. Więc mamyn^=(n+2^n)/2%3
. Możemy zaoszczędzić kolejny bajt, zauważając, że modulo3
,/2
jest równoważne*2
jest równoważne do-
, zauważając, żen+2^n
nawet tak, że podział jest o połowę bez podłogi. To dajen^=-(n+2^n)%3
41 bajtów
Wypróbuj online!
Wreszcie możemy połączyć operacje
n^=c;n+=2
wn=(n+2)^c
, gdziec
jest trochę. Działa to, ponieważ^c
działa tylko na ostatni bit i+2
nie dba o ostatni bit, więc operacje dojeżdżają do pracy. Ponownie, pierwszeństwo pozwala nam pomijać pareny i pisaćn=n+2^c
.39 bajtów
Wypróbuj online!
źródło
To daje moje (xnor) 47-bajtowe rozwiązanie i sposób myślenia, który mnie do tego doprowadził. Nie czytaj tego, jeśli chcesz to zrozumieć.
Naturalnym pierwszym pomysłem jest iteracja liczb od 0 do 799, wypisywanie tylko tych z parzystą liczbą 1 w postaci binarnej.
52 bajty
Wypróbuj online!
W tym
~
przypadku bit uzupełnia się tak, aby przełączyćeven<->odd
liczenie i podać prawdziwą wartość tylko dla liczb parzystych.Możemy ulepszyć tę metodę, generując wszystkie wartości zamiast filtrować. Zauważ, że wartościami wyjściowymi są liczby od 0 do 399, każda z bitem dołączonym, aby liczba 1 bitów była parzysta.
Tak więc,
n
liczba th jest albo2*n+b
z albob=0
albob=1
. Trochęb
można znaleźć, licząc1
w bitachn
i biorąc zliczanie modulo 2.49 bajtów
Wypróbuj online!
Możemy przeciąć 2 bajty,
2*
wykonując iterację0,2,4,...
, co nie daje szansy na zliczenie1
. Możemy to zrobić za pomocąexec
pętli, która działa 400 razy i rośnien
o 2 w każdej pętli.47 bajtów
Wypróbuj online!
I to jest moje 47-bajtowe rozwiązanie. Podejrzewam, że większość, jeśli nie wszystkie pozostałe 47-bajtowe rozwiązania są takie same.
źródło
exec
dozwolone jest podejście o długości 47 bajtów ?exec
ale do wiersza poleceńexec
.llhuii's Python 3
Oto zgłoszenia Python 3 dotyczące Złych Liczb w momencie pisania:
llhuii prawdopodobnie przeniósł swoją sztuczkę na Python 3 i wymyślił takie rozwiązanie
Przenosząc xnor 47B do Pythona 3 dosłownie, otrzymujemy 50B:
Przesłałem to jako
ppcg(xnor)
. (Dodaje nawiasy doexec
iprint
, które są teraz funkcjami.) Ma inne statystyki kodu niż inne odpowiedzi w Pythonie 3, które zawierają pewne spacje. Ciekawy!Jest jednak krótszy sposób na przepisanie go (
exec
w Pythonie 3 zwykle traci przewagę konkurencyjną):Ma 49 bajtów. Przesłałem to jako
ppcg(xnor,alternative)
. Ma dwa bajty białych znaków, podobnie jak odpowiedź llhui! To prowadzi mnie do przekonania, że odpowiedź llhuii na Python 3 wygląda trochę tak (nowa linia, potemwhile
pętla). Więc llhuii prawdopodobnie używanyexec
w Python 2 iwhile
Python 3, podobnie jak my; wyjaśnia to rozbieżność białych znaków.Nasz 47B stał się 49B w Pythonie 3. Co ciekawe, llhuii 42B nie stał się 44B, stał się 45B! Coś w rozwiązaniu llhuii wymaga dodatkowego bajtu w Pythonie 3. Może to oznaczać różne rzeczy.
Pierwszą rzeczą, która przychodzi mi na myśl, jest podział : może llhuii używa
/
w Pythonie 2, który stał się//
w Pythonie 3. (Jeśli liczą się w dwójkę jak my, ton/2
może być wykorzystany do przesunięcian
o jeden bit w prawo?)Inną rzeczą, która przychodzi mi na myśl, jest jednoargumentowy operator po wydrukowaniu . Nasz
print blah
stał sięprint(blah)
(1 bajt dodatkowy), ale jeśli llhuii napisałby cośprint~-blah
w Pythonie 2, stałby sięprint(~-blah)
w Pythonie 3.Może są inne pomysły. Proszę daj mi znać.
Statystyki kodu dla wszystkich rozwiązań Py3, w tym teraz moje:
źródło
Inne podejścia
1) Korzystanie ze wzoru na A001969
Zamiast konwertować na binarne, może być możliwe skorzystanie z następującej formuły (z OEIS ):
Bardzo źle gram w golfa w Pythonie, więc nawet nie spróbuję. Ale tutaj jest szybka próba w JS.
NB: Nie sądzę, że byłoby to prawidłowe przesłanie JS, ponieważ po prostu wypełnia tablicę bez jej wyświetlania. Mimo to jest o 5 bajtów dłuższy niż obecne najlepsze rozwiązanie JS (45 bajtów). Ale i tak nie o to tutaj chodzi.
Pokaż fragment kodu
Mam nadzieję, że może to dać inspirację.
Korzystanie z tablicy prawdopodobnie nie jest dobrym pomysłem, ponieważ należy ją zainicjować i zaktualizować. Zamiast tego może być bardziej wydajne (jeśli chodzi o rozmiar kodu) użycie funkcji rekurencyjnej , co tłumaczyłoby, dlaczego zwycięskie rozwiązanie zajmuje więcej czasu niż inne.
2) Budowanie sekwencji Thue-Morse z podstawieniami
Teoretycznie ten kod powinien działać:
Wypróbuj online!(dostępna wersja ograniczona do 20 warunków)
Oblicza sekwencję Thue-Morse'a z kolejnymi podstawieniami i szuka pozycji 1 (Liczb Zła) w tej samej pętli.
Ale:
3) Budowanie sekwencji Thue-Morse za pomocą operacji bitowych
Zaczynając od bezpośredniej definicji sekwencji Thue-Morse'a w Wikipedii , doszedłem do tego algorytmu (wracając do JS ... przepraszam):
gdzie śledzimy aktualną zło sekwencji w ei używamy 170 jako maskę bitów nieparzystych bitów w bajcie.
źródło
f=lambda n:_
for n in range(400):print f(n)
zajmuje już 43 bajty. Być może istnieje sposób na symulację rekurencji poprzez zbudowanie tablicy odwołującej się do siebie lub tablicy, która dodaje przyszłe elementy do końca.def
,for
,while
,lambda
(z parametrem przynajmniej), itd.while~0:print~1
nie wymaga spacji.((x=n++^n)^x/2)
wydaje się nieco rozwlekły, aby znaleźć najniższy ustawiony bit. Cały ten bałagan można zastąpić++n&-n
. Wypróbuj online!Zbliża się zagnieżdżone liczniki
Mam pomysł na inne podejście, ale nie mam wystarczającego doświadczenia w golfie w Pythonie, więc zostawię to tutaj, abyście mogli rozważyć inny punkt wyjścia do gry w golfa.
Niegolfowany pomysł:
Wypróbuj online!
Dziewięć poziomów zagnieżdżenia, wszystkie pętle są takie same, więc moim zdaniem powinny je zbudować
exec"something"*9+"deepest stuff"
. W praktyce nie wiem, czy można zrobić coś takiego za pomocą cyklu for.Rzeczy do rozważenia podczas gry w golfa:
może istnieje jakaś inna możliwość dwukrotnego przełączania poza pętlą for (próbowałem podejścia quine z wykonanym łańcuchem przekazywanym do siebie jako argument formatujący dwa razy, ale moja głowa eksplodowała).
może być też lepsza alternatywa
if n<800:
, która jest tutaj potrzebna, ponieważ w przeciwnym razie drukowalibyśmy złe liczby do 2 ^ 10źródło
print
jest instrukcją, a nie funkcją, dlatego nie może pojawić się w rozumieniu.print '\n'.join([[[[[[[[[foo]foo]foo]foo]foo]foo]foo]foo]foo])
str.join
działa tylko na listach zawierających ciągi, a dodatkowe znaki listy nie mogą być drukowane. Samo formatowanie zajęłoby znaczną ilość bajtów.Pomysł: krótsza parzystość bitów
Zajmuje to wiele postaci
bin(n).count('1')%2
Obliczenie parzystości liczby bitów . Być może sposób arytmetyczny jest krótszy, szczególnie biorąc pod uwagę ograniczoną długość bitów.Uroczym sposobem jest
int(bin(n)[2:],3)%2
interpretowanie wartości binarnej jako podstawy3
(lub dowolnej nieparzystej podstawy). Niestety 4 bajty spędzają na usuwaniu0b
prefiksu. Działa to również do zrobieniaint(bin(n)[2:])%9%2
.Kolejny pomysł pochodzi z łączenia bitów za pomocą xor. Jeśli
n
ma reprezentację binarnąabcdefghi
, toZatem
r=n/16^n%16
zło jest wtedy i tylko wtedy, gdyn
jest złe. Możemy powtórzyć, że jakos=r/4^r%4
wartośćs
w0,1,2,3
, z czego1
i2
nie są złe, dostępne do kontroli z0<s<3
.52 bajty
Wypróbuj online!
Okazało się to znacznie dłużej. Istnieje jednak wiele pokręteł do obrócenia, jak podzielić liczbę, jak sprawdzić ostateczną liczbę (być może tablica odnośników oparta na bitach). Podejrzewam, że mogą one pójść tylko tak daleko.
źródło
to_bytes
funkcji liczb całkowitych? Wątpię, ale coś do rozważenia :)0b
:int(bin(n),13)%2
! : Dn=0;exec"print~int(bin(n),13)%2+n;n+=2;"*400
Z
n+n^n
założenia jest zawsze zły, ale moje słabe umiejętności w Pythonie mogły tylko wymyślić 61-bajtowe rozwiązanie:Dzięki @Peilonrayz za zapisanie 5 bajtów i @ Mr.Xcoder za zapisanie 1 bajtu:
źródło
for n in sorted(n^n*2for n in range(512))[:400]:print n
.n+n^n
jest taki sam jakn^n*2
Pomysł: A006068 („a (n) ma szary kod w n”)
Pomysł Neila na uporządkowanie wszystkich
2n XOR n
mnie zaintrygował, więc próbowałem znaleźć wskaźniki tego rodzaju. Napisałem ten kod i ujawnia, że możemy napisać coś takiego:Gdzie
a(n)
jest A006068 (n). Wypróbuj online!Zakłada się jednak, że mamy krótką drogę do obliczenia A006068. To już 38 bajtów, zakładając, że możemy obliczyć go w 4 bajtach (
a(n)
część). Rzeczywista implementacja (w nagłówku TIO) jest znacznie dłuższa. Myślę, że niewiele nadziei na to.źródło
Pomysł: Zmniejsz XOR
Jeśli XOR wszystkie części
n
razem, będzie to0
dla zła i1
dla nie-zła. Możesz to zrobić za pomocą funkcji rekurencyjnej (co mogło zająć więcej czasu?), Na przykład:Zwraca 1 za zło.
To 35 bajtów i sprawdza, czy liczba jest zła. Niestety,
filter
ma już 6 bajtów, więc nie było to optymalne rozwiązanie dosłownie, ale ten pomysł prawdopodobnie można zastosować w golfa.źródło
f=lambda n:n>1and f(n/2^n&1)or-~-n
dla -1 bajtów.f(n/2^n&1)
zwraca 0 ...Metoda podstawienia: {1 -> {1, -1}, -1 -> {-1, 1}}
Możesz również dokonać tej zamiany 10 razy {1 -> {1, -1}, -1 -> {-1, 1}}, a następnie spłaszczyć i sprawdzić pozycje 1
tutaj jest kod matematyczny
źródło