Otrzymujesz maszynę z dwoma 16-bitowymi rejestrami x
i y
. Rejestry są inicjowane x=1
i y=0
. Jedyną operacją, jaką może wykonać maszyna, jest dodanie modułu 65536. To znaczy:
x+=y
-x
zastępuje się przez(x + y) mod 65536
;y
pozostaje niezmienionyy+=x
- podobnie dlay
x+=x
-x
zastępuje się przez2x mod 65536
; legalne tylko, jeślix
jest parzystey+=y
- podobnie dlay
Celem jest uzyskanie z góry określonej liczby w jednym z rejestrów (albo x
albo y
).
Napisz program lub podprogram, który odbiera liczbę (in stdin
, argv
parametr funkcji, top stosu lub inne konwencjonalne miejsce) i wysyła program, aby uzyskać ten numer. Dane wyjściowe powinny przejść do stdout
, lub (jeśli twój język nie ma stdout
), na inne konwencjonalne urządzenie wyjściowe.
Program wyjściowy może wynosić do 100% plus 2 kroki daleko od optymalnego. Oznacza to, że jeśli najkrótszy program do uzyskania numeru docelowego ma n
kroki, twoje rozwiązanie nie może być dłuższe niż 2n+2
. To ograniczenie ma na celu uniknięcie „zbyt łatwych” rozwiązań (np. Liczenie 1, 2, 3, ...), ale nie wymaga pełnej optymalizacji; Oczekuję, że najkrótszy program jest najłatwiejszy do znalezienia, ale nie jestem pewien ...
Na przykład: Input = 25. Output:
y+=x
x+=y
x+=y
x+=x
x+=x
x+=x
y+=x
Kolejny przykład: dla dowolnej liczby Fibonacciego, wyjście ma ten naprzemienny wzór. Dla Input = 21 wyjście to
y+=x
x+=y
y+=x
x+=y
y+=x
x+=y
y+=x
Najkrótszy kod (mierzony w bajtach) wygrywa.
(ta łamigłówka została zainspirowana kodem 16-bitowego procesora, który musiałem ostatnio wygenerować)
PS Zastanawiam się - dla jakiej liczby optymalny program jest najdłuższy?
źródło
x+=x
legalne tylko wtedy, gdyx
jest parzyste? Myślę też, że w przypadku najkrótszego programu coś takiego jak BFS może działać.x+=x
działa tylko na parzystex
, to dlaczego przykład dla 25 podwójnych 3?Odpowiedzi:
CJam, 31
Podobnie jak odpowiedź @Tobia , mój algorytm jest również bezwstydnie
skradzionyinspirowany odpowiedzią @CChak . Ale posługując się czarną magią, jaką jest CJam, udało mi się wprowadzić jeszcze mniejszą implementację algorytmu.Wypróbuj tutaj.
Gra w golfa:
Nie golfowany:
Proszę mnie poprawić, jeśli się mylę, ale uważałem, że operacja modulo 65536 stosowana w odpowiedziach z podobnym algorytmem jest niepotrzebna. Zinterpretowałem to pytanie w taki sposób, że możemy założyć, że wejście będzie prawidłową 16-bitową liczbą całkowitą bez znaku, a także wszelkie wartości pośrednie lub wyniki tego algorytmu.
źródło
Perl
10797Pierwszy post, więc proszę bardzo.
Który pasuje do wszystkich kryteriów dodawania rejestru, ale nie przeprowadziłem wyczerpującego sprawdzenia, aby sprawdzić, czy moja odpowiedź zawsze mieści się w granicach 2n + 2 od optymalnej liczby kroków. Jest jednak w granicach limitu dla każdej liczby Fibonacciego.
Oto bardziej szczegółowy podział
Jak wspomniałem, to moja pierwsza gra w golfa, więc jestem pewien, że można to poprawić. Nie jestem też pewien, czy początkowe wywołanie podprogramu musi zostać policzone w wywołaniu rekurencyjnym, czy nie, co może zwiększyć liczbę znaków.
Co ciekawe, możemy zmniejszyć kod o 11 bajtów * i poprawić naszą „wydajność” pod względem liczby operacji rejestru, zmniejszając wymóg, że tylko parzyste wartości mogą być „podwojone”. Dołączyłem to dla zabawy:
Rozpocznij uzupełnienie:
Naprawdę podobał mi się ten problem i od kilku tygodni bawię się nim z przerwami. Myślałem, że opublikuję swoje wyniki.
Niektóre liczby:
Korzystając z algorytmu BFS w celu znalezienia optymalnego rozwiązania, w pierwszych 2 ^ 16 liczbach jest tylko 18 liczb, które wymagają 23 kroków. Są to: 58558, 59894, 60110, 61182, 61278, 62295, 62430, 62910, 63422, 63462, 63979, 64230, 64314, 4486, 64510, 64698, 64854, 65295.
Przy użyciu opisanego powyżej algorytmu rekurencyjnego „Najtrudniejszym” numerem jest 65535 przy 45 operacjach. (65534 zajmuje 44, a jest 14 liczb, które wykonują 43 kroki) 65535 jest również największym odstępstwem od optymalnego, 45 vs 22. Różnica 23 kroków wynosi 2n + 1. (Tylko trzy liczby trafiły w 2n: 65534, 32767, 32751.) Z wyjątkiem przypadków trywialnych (z krokiem zerowym), w zdefiniowanym zakresie, metoda rekurencyjna wynosi średnio około 1,4 razy optymalne rozwiązanie.
Konkluzja: Dla liczb 1-2 ^ 16 algorytm rekurencyjny nigdy nie przekracza zdefiniowanego progu 2n + 2, więc odpowiedź jest poprawna. Podejrzewam jednak, że zacząłby odbiegać zbyt daleko od optymalnego rozwiązania dla większych rejestrów / większej liczby bitów.
Kod, którego użyłem do stworzenia BFS, był niechlujny, wymagał dużej ilości pamięci, nie skomentował i całkiem celowo nie został uwzględniony. Więc ... nie musisz ufać moim wynikom, ale jestem całkiem pewny ich.
źródło
Python 3, 202 bajty
(Podziękowania dla @rationalis za kilka bajtów)
Oto niezwykle podstawowe rozwiązanie. Chciałbym móc lepiej grać w ostatnią linię, ale obecnie brakuje mi pomysłów. Zadzwoń z
S(25)
.Program wykonuje prosty BFS bez buforowania, więc działa bardzo wolno. Oto
S(97)
przykładowe dane wyjściowe:źródło
Dyalog APL, 49 znaków / bajtów *
Algorytm bezwstydnie inspirowany @CChak .
Przykład:
Nie golfowany:
* Aplikacja APL Dyalog obsługuje starszy zestaw znaków, w którym symbole APL są odwzorowane na górne 128 bajtów. Dlatego program APL, który używa tylko znaków ASCII i symboli APL, można uznać za bajty == znaków.
źródło
Python, 183
Nie mogę zagwarantować, że pozostanie w 2x optymalnym programie dla liczb parzystych, ale jest wydajny. Dla wszystkich prawidłowych danych wejściowych jest
0 <= n < 65536
to w zasadzie natychmiastowe i generuje program składający się maksymalnie z 33 instrukcji. W przypadku dowolnego rozmiaru rejestrun
(po ustaleniu tej stałej) zajęłoby toO(n)
najwyżej trochę czasu2n+1
instrukcje.Trochę logiki binarnej
Dowolną liczbę nieparzystą
n
można osiągnąć w ciągu 31 kroków: wykonajy+=x
, otrzymajx,y = 1,1
, a następnie podwojaj zax
pomocąx+=x
(dla pierwszego podwojeniax+=y
, ponieważ początkowox
jest nieparzysta).x
osiągnie w ten sposób każdą potęgę 2 (to tylko przesunięcie w lewo), więc możesz ustawić dowolną wartośćy
1, dodając odpowiednią potęgę 2. Ponieważ używamy rejestrów 16-bitowych i każdy bit oprócz dla pierwszego potrzeba jednego podwojenia, aby osiągnąć, a drugiegoy+=x
do ustawienia, otrzymujemy maksymalnie 31 operacji.Dowolna liczba parzysta
n
to tylko potęga 2, zadzwońa
, razy liczbę nieparzystą, zadzwońm
; tj.n = 2^a * m
lub równoważnien = m << a
. Użyj powyższego procesu, aby uzyskaćm
, a następnie zresetujx
, przesuwając go w lewo, aż osiągnie wartość 0. Wykonaj a,x+=y
aby ustawićx = m
, a następnie kontynuuj podwojeniex
, przy pierwszym użyciu,x+=y
a następnie przy użyciux+=x
.Cokolwiek
a
to jest, potrzeba16-a
zmian,x
aby uzyskaćy=m
i dodatkowycha
zmian, aby zresetowaćx=0
. Kolejnea
zmianyx
nastąpią pox=m
.16+a
Wykorzystano więc całkowitą liczbę zmian. Istnieje kilka16-a
bitów, które należy ustawić, aby je zdobyćm
, a każdy z nich zajmie jedeny+=x
. Wreszcie potrzebujemy dodatkowego kroku,x=0
aby ustawić go na m,x+=y
. Aby uzyskać dowolną liczbę parzystą, potrzeba maksymalnie 33 kroków.Możesz oczywiście uogólnić to na dowolny rejestr wielkości, w którym to przypadku zawsze zajmuje najwyżej
2n-1
i2n+1
operuje na parzyste i nieparzysten
operuje odpowiednio na liczbach .Optymalność
Ten algorytm tworzy program, który jest prawie optymalny (tzn.
2n+2
Jeśli ifn
jest minimalną liczbą kroków) dla liczb nieparzystych. Dla danej liczby nieparzystejn
, jeśli tenm
bit jest wiodącym 1, to dowolny program podejmuje przynajmniejm
kroki, aby dostać się dox=n
luby=n
, ponieważ operacja, która najszybciej zwiększa wartości rejestrów, tox+=x
luby+=y
(tj. Podwojenie) i potrzebam
podwojenia, aby dostać się do częśćm
z 1. Ponieważ ten algorytm wykonuje co najwyżej2m
kilka kroków (maksymalnie dwa na podwojenie, jeden dla zmiany i jedeny+=x
), każda liczba nieparzysta jest reprezentowana prawie optymalnie.Liczby parzyste nie są tak dobre, ponieważ zawsze resetuje 16 operacji
x
zanim cokolwiek innego, a na przykład 8 można osiągnąć w ciągu 5 kroków.Co ciekawe, powyższy algorytm nigdy nie używa
y+=y
w ogóley
zawsze jest nieparzysty. W takim przypadku może faktycznie znaleźć najkrótszy program dla ograniczonego zestawu tylko 3 operacji.Testowanie
Napisałem prosty test, aby sprawdzić, czy moje rozwiązanie rzeczywiście daje poprawne wyniki i nigdy nie przekracza 33 kroków, dla wszystkich prawidłowych danych wejściowych (
0 <= n < 65536
).Ponadto próbowałem przeprowadzić analizę empiryczną, aby porównać wyniki mojego rozwiązania z wynikami optymalnymi - okazuje się jednak, że wyszukiwanie z szerokością pierwszego rzędu jest zbyt mało wydajne, aby uzyskać minimalną długość wyjściową dla każdego ważnego wejścia
n
. Na przykład użycie BFS do znalezienia wyjścian = 65535
nie kończy się w rozsądnym czasie. Niemniej jednak wyszedłembfs()
i jestem otwarty na sugestie.Testowałem jednak swoje własne rozwiązanie pod kątem @ CChak (zaimplementowane w Pythonie tutaj jako
U
). Spodziewałem się, że moja będzie gorzej, ponieważ jest drastycznie nieefektywna dla mniejszych liczb parzystych, ale uśredniona dla całego zakresu na dwa sposoby, kopalnia produkuje średnio o 10,8% do 12,3% mniej. Pomyślałem, że może to wynikało z lepszej wydajności z mojego własnego rozwiązania dla liczb nieparzystych, więcV
używa moich na liczbach nieparzystych i @ CChak na liczbach parzystych, aleV
jest pomiędzy (około 10% krótszy niżU
, 3% dłuższy niżS
).źródło
x,y='xy'
było możliwe do tej pory. Niestety nie mogę wymyślić sposobuc*b+e*2
zwięzłego przepisania przy użyciu%
formatowania.S(2)
wydruki są naprawdę długie?S(2)
najkrótsza przy 19). Nie śledzęx
i niey
wyraźnie, więc mimo żex
po drugim kroku osiąga 2, to nadal resetuje sięx
do 0. Czuję, że musi być lepsze rozwiązanie, ale na razie nie mogę wymyślić jeden.