Kiedy to stało się golfem kodowym? Pomyślałem, że wymyślenie najlepszego algorytmu to wyzwanie.
golf-golf
APL, 33 znaki
{r←⍵⋄⍺{1≥⍵⍟⍣⍺⊢r:⍵⋄⍺∇⍵+i}1+i←1e¯6}
Jest to proste wyszukiwanie liniowe, zaczynające się od C = 1 + 10-6 i zwiększające go o 10 -6, aż do
log C log C log C ⋯ A ≤ 1,
gdy funkcja log C jest stosowana rekurencyjnie B razy.
Przykłady
4 {r←⍵⋄⍺{1≥⍵⍟⍣⍺⊢r:⍵⋄⍺∇⍵+i}1+i←1e¯6} 65536
2.0000009999177335
3 {r←⍵⋄⍺{1≥⍵⍟⍣⍺⊢r:⍵⋄⍺∇⍵+i}1+i←1e¯6} 7625597484987
3.0000000000575113
Ten kod jest bardzo wolny, ale dla małych baz, takich jak 2 lub 3, kończy się w kilka sekund. Zobacz poniżej lepszą rzecz.
kod-wyzwanie
APL, złożoność logarytmiczna
Właściwie liniowa złożoność w kolejności pierwiastkowej, logarytmiczna w stosunku do wielkości wyniku i precyzji:
czas = O (B × log (C) + B × log (D))
gdzie B jest porządkiem pierwiastka, C jest pytaną o bazę tetracji, a D jest liczbą zadawanych cyfr precyzji. Ta złożoność jest moim intuicyjnym zrozumieniem, nie przedstawiłem formalnego dowodu.
Algorytm ten nie wymaga dużych liczb całkowitych, używa tylko funkcji logu na zwykłych liczbach zmiennoprzecinkowych, dlatego jest dość wydajny na bardzo dużych liczbach, aż do limitu implementacji liczb zmiennoprzecinkowych (albo podwójna precyzja, albo dowolne duże liczby FP na Implementacje APL, które je oferują).
Precyzja wyniku może być kontrolowana poprzez ustawienie ⎕CT(tolerancji porównania) pożądanego dopuszczalnego błędu (w moim systemie domyślnie jest to 1e¯14, około 14 cyfr dziesiętnych)
sroot←{ ⍝ Compute the ⍺-th order super-root of ⍵:
n←⍺ ⋄ r←⍵ ⍝ n is the order, r is the result of the tetration.
u←{ ⍝ Compute u, the upper bound, a base ≥ the expected result:
1≥⍵⍟⍣n⊢r:⍵ ⍝ apply ⍵⍟ (log base ⍵) n times; if ≤1 then upper bound found
∇2×⍵ ⍝ otherwise double the base and recurse
}2 ⍝ start the search with ⍵=2 as a first guess.
(u÷2){ ⍝ Perform a binary search (bisection) to refine the base:
b←(⍺+⍵)÷2 ⍝ b is the middle point between ⍺ and ⍵
t←b⍟⍣n⊢r ⍝ t is the result of applying b⍟ n times, starting with r;
t=1:b ⍝ if t=1 (under ⎕CT), then b is the super-root wanted;
t<1:⍺∇b ⍝ if t<1, recurse between ⍺ and b
b∇⍵ ⍝ otherwise (t>1) returse between b and ⍵
}u ⍝ begin the search between u as found earlier and its half.
}
Nie jestem pewien, czy 1≥⍵⍟⍣npowyższe może zawieść z błędem domeny (ponieważ dziennik argumentu ujemnego może albo zawieść natychmiast, albo dać złożony wynik, którego nie byłoby w domenie ≥), ale nie byłem w stanie znaleźć przypadek, który zawodzi.
Przykłady
4 sroot 65536
1.9999999999999964
4 sroot 65537
2.000000185530773
3 sroot 7625597484987
3
3 sroot 7625597400000
2.999999999843567
3 sroot 7625597500000
3.000000000027626
„3” pojawia się jako dokładna wartość, ponieważ zdarza się, że jest to jedna z wartości bezpośrednio dotkniętych przez wyszukiwanie binarne (od 2, podwojona do 4, dwusieczna do 3). W ogólnym przypadku tak się nie dzieje, więc wynik aproksymuje wartość pierwiastkową z błędem moreCT (a dokładniej, test logarytmiczny każdej kandydującej bazy przeprowadzany jest z tolerancją ⎕CT).