Próbuję obliczyć stałą e ( AKA Euler's Number ), obliczając wzór
Aby obliczyć silnię i podział w jednym ujęciu, napisałem to:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce * + * , @e[^10];
Ale to nie wyszło. Jak to zrobić poprawnie?
code-generation
lazy-evaluation
raku
eulers-number
Lars Malmsteen
źródło
źródło
$_
zmienną wartości , próbując skonstruować silnię. To było oczywiście zbędne. W poprawnym rozwiązaniu poniżej$_
został upuszczony i działał idealnie.Odpowiedzi:
Analizuję twój kod w sekcji Analiza twojego kodu . Wcześniej przedstawiam kilka zabawnych sekcji materiałów bonusowych.
Jedna wkładkaJedna litera 1„Traktat na wiele sposobów” 2
Kliknij powyższy link, aby zobaczyć niezwykły artykuł Damiana Conwaya na temat komputerów
e
w Raku.Artykuł jest świetną zabawą (w końcu to Damian). To bardzo zrozumiała dyskusja na temat komputerów
e
. I jest hołdem dla wodorowęglanowej reinkarnacji Raku w filozofii TIMTOWTDI, popieranej przez Larry'ego Walla. 3)Jako przystawkę, oto cytat z około połowy artykułu:
Analizując twój kod
Oto pierwsza linia, generująca serię:
Funkcja closure (
{ code goes here }
) oblicza termin. Zamknięcie ma podpis, jawny lub jawny, który określa, ile argumentów zostanie zaakceptowanych. W tym przypadku nie ma wyraźnego podpisu. Użycie$_
( zmiennej „topic” ) skutkuje niejawną sygnaturą, która wymaga jednego związanego z nią argumentu$_
.Operator sekwencji (
...
) wielokrotnie wywołuje zamknięcie po lewej stronie, mijając poprzedni termin jako argument zamknięcia, aby leniwie zbudować serię terminów aż do punktu końcowego po prawej stronie, który w tym przypadku jest*
skrótemInf
aka nieskończoności.Tematem pierwszego wezwania do zamknięcia jest
1
. Zatem zamknięcie oblicza i zwraca,1 / (1 * 1)
uzyskując pierwsze dwa warunki w szeregu jako1, 1/1
.Tematem drugiego wezwania jest wartość poprzedniego
1/1
, tj1
. Ponownie. Więc zamknięcie oblicza i zwraca1 / (1 * 2)
, rozszerzając serię na1, 1/1, 1/2
. Wszystko wygląda dobrze.Następne zamknięcie oblicza,
1 / (1/2 * 3)
co jest0.666667
. Ten termin powinien być1 / (1 * 2 * 3)
. UpsDopasowanie kodu do formuły
Twój kod powinien pasować do wzoru:
W tym wzorze każdy termin jest obliczany na podstawie jego pozycji w szeregu. K p terminu w serii (gdzie k = 0, w pierwszym
1
) jest tak silnia K jest wzajemne.(Więc nie ma to nic wspólnego z wartością wcześniejszego terminu. Dlatego też
$_
, która otrzymuje wartość z poprzedniego terminu, nie powinna być używana w zamknięciu).Utwórzmy czynnikowego operatora Postfiksa:
(
×
jest operatorem mnożenia infekcji, ładniejszym aliasem Unicode zwykłej infiksu ASCII*
.)To jest skrót od:
(Użyłem pseudometazntaktycznej notacji w nawiasach klamrowych, aby wskazać pomysł dodania lub odjęcia dowolnej liczby terminów, zgodnie z wymaganiami.
Mówiąc bardziej ogólnie, umieszczenie operatora
op
przedrostka w nawiasach kwadratowych na początku wyrażenia tworzy złożony operator przedrostka, który jest równoważnyreduce with => &[op],
. Zobacz Metaoperator redukcji więcej informacji, .Teraz możemy przepisać zamknięcie, aby użyć nowego operatora czynnikowego porostku:
Bingo To daje właściwą serię.
... dopóki tak się nie stanie, z innego powodu. Kolejnym problemem jest dokładność numeryczna. Ale zajmijmy się tym w następnym rozdziale.
Jedna linijka wyprowadzona z twojego kodu
Może skompresuj trzy linie do jednego:
.[^10]
dotyczy tematu ustawionego przezgiven
. (^10
jest skrótem0..9
od powyższego, więc powyższy kod oblicza sumę pierwszych dziesięciu terminów w serii).Wyeliminowałem
$a
z obliczeń zamknięcia następnego terminu. Samotny$
to taki sam, jak(state $)
anonimowy skalar stanu. Zrobiłem mu preinkrementuj zamiast post-przyrostu, aby osiągnąć ten sam efekt, jak to było przez inicjowanie$a
do1
.Pozostaje nam ostatni problem (duży!), O którym wspomniałeś w komentarzu poniżej.
Pod warunkiem, że żaden z jego argumentów nie jest
Num
(liczba zmiennoprzecinkowa, a zatem przybliżona),/
operator zwykle zwraca 100% dokładnościRat
(racjonalna ograniczona precyzja). Ale jeśli mianownik wyniku przekracza 64 bity, wynik ten jest konwertowany naNum
- co wymienia wydajność pod względem dokładności - kompromis, którego nie chcemy robić. Musimy to wziąć pod uwagę.Aby określić nieograniczoną precyzję, a także 100% dokładności, po prostu wymusz operację, aby użyć
FatRat
s. Aby zrobić to poprawnie, po prostu ustaw (przynajmniej) jeden z operandów na aFatRat
(i żaden inny nie będzieNum
):Zweryfikowałem to do 500 cyfr dziesiętnych. Oczekuję, że pozostanie dokładny, dopóki program się nie zawiesi z powodu przekroczenia pewnego limitu języka Raku lub kompilatora Rakudo. (Zobacz moją odpowiedź na Nie można rozpakować biginta o szerokości 65536 bitów na natywną liczbę całkowitą, aby o tym porozmawiać.)
Przypisy
1 Raku ma kilka ważnych stałych matematycznych wbudowanych w tym
e
,i
orazpi
(i jego pseudonimπ
). W ten sposób można napisać Tożsamość Eulera w Raku, podobnie jak w książkach matematycznych. Z kredytu do wpisu Raku RosettaCode za Tożsamość Eulera :2 Artykuł Damiana należy przeczytać. Ale to tylko jeden z kilku godnych podziwu zabiegów, które są wśród ponad 100 dopasowań dla google dla „raku” numeru eulera ” .
3 Zobacz TIMTOWTDI vs TSBO-APOO-OWTDI, aby zapoznać się z jednym z bardziej zrównoważonych widoków TIMTOWTDI napisanych przez fanów Pythona. Ale są też wady zbyt daleko idącego TIMTOWTDI. Aby odzwierciedlić to ostatnie „niebezpieczeństwo”, społeczność Perla wymyśliła humorystycznie długi, nieczytelny i zaniżony TIMTOWTDIBSCINABTE - Istnieje więcej niż jeden sposób, ale czasami spójność nie jest złą rzeczą, wymawianą jako „Tim Toady Biwęglan”. O dziwo , Larry zastosował wodorowęglan do projektu Raku, a Damian zastosował go do obliczeń
e
w Raku.źródło
$
jest skrótemstate $
, jest całkiem przydatna.e
dla trzeciego rozwiązania (zatytułowanego Moja droga na podstawie Twojej drogi )? Próbowałem dodać FatRat (500) obok 1 w:... given 1.FatRat(500), ...
aby liczby były 500- cyfrowe , ale to nie zadziałało.FatRat
ostatnim rozdziale odniosłem się do twojego bardzo ważnego pytania. Dopracowałem też całą odpowiedź, chociaż jedyną poważną zmianą sąFatRat
rzeczy. (Przy okazji, zdaję sobie sprawę, że duża część mojej odpowiedzi jest naprawdę styczna do twojego pierwotnego pytania; ufam, że nie masz nic przeciwko, że napisałem cały dodatkowy puch, aby się bawić i być może być interesujący dla późniejszych czytelników.).FatRat
rozszerzenie należy umieścić w generatorze kodu. Teraz wypróbowałem to zFatRat
dodanym w ten sposób i obliczyło e z dokładnością do 1000 cyfr. Dodatkowy puch dodany jest w tym czasie. Na przykład nie wiedziałem, żesay
skraca długie tablice / sekwencje. Takie fragmenty informacji są dobrze znane..FatRat
rozszerzenie musi być umieszczone w generatorze kodu.”. Tak. Mówiąc bardziej ogólnie, jeśli wyrażenie obejmujące podział zostało już ocenione, jest już za późno, aby cofnąć uszkodzenia spowodowane przekroczeniemRat
precyzji. Jeśli tak, będzie oceniać naNum
(zmiennoprzecinkowe), a to z kolei zabezpieczy wszelkie dalsze obliczenia z tym związane, czyniąc je równieżNum
. Jedynym sposobem, aby upewnić się, że rzeczy pozostaną,FatRat
jest ich uruchomienieFatRat
i uniknięcie jakichkolwiekNum
s.Int
s iRat
s są w porządku, pod warunkiem, że jest co najmniej jeden,FatRat
który pozwoli Raku wiedzieć, aby się trzymałFatRat
.Jest ułamek w
$_
. Zatem potrzebujesz,1 / (1/$_ * $a++)
a raczej$_ /$a++
.Przez Raku możesz wykonać te obliczenia krok po kroku
źródło
andthen
.