Berechnung der e-Zahl mit Raku

9

Ich versuche, die e- Konstante ( AKA Euler's Number ) durch Berechnung der Formel zu berechnen e

Um die Fakultät und die Division in einem Schuss zu berechnen, habe ich Folgendes geschrieben:

my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce  * + * , @e[^10];

Aber es hat nicht geklappt. Wie mache ich das richtig?

Lars Malmsteen
quelle
Inwiefern hat es: "Es hat nicht geklappt"?
SherylHohman
1
Der Nennerteil im Beispielcode hat nicht funktioniert, da er die vorherige $_ Wertvariable verwendet hat, um die Fakultät zu konstruieren. Es war offensichtlich überflüssig. In der richtigen Lösung unten $_wurde fallen gelassen und es hat perfekt geklappt.
Lars Malmsteen
Vielen Dank. Ich schätze, ich habe mehr danach gesucht, was genau mit dieser Aussage gemeint ist. Als ob es einen Fehler gäbe, wie stimmte das nicht mit dem überein, was Sie erwartet hatten, so etwas. Ich denke, Ihre Berechnung stimmte nicht mit bekannten Antworten für diese Berechnung überein. Ich bin froh, dass es geklappt hat !! Außerdem eine großartige Beschreibung nach der Antwort, was das eigentliche Problem war :-)
SherylHohman

Antworten:

11

Ich analysiere Ihren Code im Abschnitt Analysieren Ihres Codes . Vorher präsentiere ich ein paar lustige Abschnitte mit Bonusmaterial.

Ein Liner Ein Buchstabe 1

say e; # 2.718281828459045

"Eine Abhandlung über mehrere Wege" 2

Klicken Sie auf den obigen Link, um Damian Conways außergewöhnlichen Artikel über Computer ein Raku zu lesen.

Der Artikel macht viel Spaß (schließlich ist es Damian). Es ist eine sehr verständliche Diskussion über Computer e. Und es ist eine Hommage an Rakus Bikarbonat-Reinkarnation der von Larry Wall vertretenen TIMTOWTDI-Philosophie. 3

Als Vorspeise hier ein Zitat aus der Mitte des Artikels:

Angesichts der Tatsache, dass diese effizienten Methoden alle auf die gleiche Weise funktionieren - durch Summieren (einer anfänglichen Teilmenge) einer unendlichen Reihe von Begriffen - wäre es vielleicht besser, wenn wir eine Funktion hätten, die dies für uns erledigt. Und es wäre sicherlich besser, wenn die Funktion selbst genau herausfinden könnte, wie viel von dieser anfänglichen Teilmenge der Serie tatsächlich enthalten sein muss, um eine genaue Antwort zu erhalten ... anstatt dass wir die Ergebnisse von manuell durchkämmen müssen mehrere Versuche, um das zu entdecken.

Und wie so oft in Raku ist es überraschend einfach, genau das zu bauen, was wir brauchen:

sub Σ (Unary $block --> Numeric) {
  (0..∞).map($block).produce(&[+]).&converge
}

Analysieren Sie Ihren Code

Hier ist die erste Zeile, in der die Serie generiert wird:

my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;

Der Abschluss ( { code goes here }) berechnet einen Begriff. Ein Abschluss hat eine implizite oder explizite Signatur, die bestimmt, wie viele Argumente er akzeptiert. In diesem Fall gibt es keine explizite Signatur. Die Verwendung von $_( der Variablen "topic" ) führt zu einer impliziten Signatur, für die ein Argument erforderlich ist, an das gebunden ist $_.

Der Sequenzoperator ( ...) ruft wiederholt den Abschluss auf der linken Seite auf und übergibt den vorherigen Begriff als Argument des Abschlusses, um träge eine Reihe von Begriffen bis zum Endpunkt auf der rechten Seite zu erstellen, in diesem Fall *Abkürzung für Infaka Infinity.

Das Thema im ersten Aufruf zur Schließung ist 1. Der Abschluss berechnet und gibt also 1 / (1 * 1)die ersten beiden Terme der Reihe als zurück 1, 1/1.

Das Thema im zweiten Aufruf ist der Wert des vorherigen 1/1, dh 1erneut. Der Abschluss berechnet und kehrt zurück 1 / (1 * 2)und erweitert die Serie auf 1, 1/1, 1/2. Es sieht alles gut aus.

Der nächste Abschluss berechnet, 1 / (1/2 * 3)welches ist 0.666667. Dieser Begriff sollte sein 1 / (1 * 2 * 3). Hoppla.

Passen Sie Ihren Code an die Formel an

Ihr Code soll mit der Formel übereinstimmen:
e

In dieser Formel wird jeder Term basierend auf seiner Position in der Reihe berechnet . Der k- te Term in der Reihe (wobei k = 0 für den ersten ist 1) ist nur der Kehrwert von Fakultät k .

(Es hat also nichts mit dem Wert des vorherigen Terms zu tun . Daher $_sollte der Wert , der den Wert des vorherigen Terms erhält , nicht für den Abschluss verwendet werden.)

Erstellen wir einen faktoriellen Postfix-Operator:

sub postfix:<!> (\k) { [×] 1 .. k }

( ×ist ein Infix-Multiplikationsoperator, ein besser aussehender Unicode-Alias des üblichen ASCII-Infix *.)

Das ist eine Abkürzung für:

sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }

(Ich habe die pseudometasyntaktische Notation in geschweiften Klammern verwendet, um die Idee zu kennzeichnen, so viele Begriffe wie erforderlich zu addieren oder zu subtrahieren.

Im Allgemeinen bildet das Setzen eines Infix-Operators opin eckige Klammern am Anfang eines Ausdrucks einen zusammengesetzten Präfix-Operator, der dem entspricht reduce with => &[op],. Weitere Informationen finden Sie unter Reduktions-Metaoperator .

Jetzt können wir den Abschluss neu schreiben, um den neuen faktoriellen Postfix-Operator zu verwenden:

my @e = 1, { state $a=1; 1 / $a++! } ... *;

Bingo. Dies ergibt die richtige Serie.

... bis es aus einem anderen Grund nicht mehr geht. Das nächste Problem ist die numerische Genauigkeit. Aber lassen Sie uns das im nächsten Abschnitt behandeln.

Ein Einzeiler, der von Ihrem Code abgeleitet ist

Vielleicht komprimieren Sie die drei Zeilen auf eine:

say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf

.[^10]gilt für das Thema, das von der festgelegt wird given. ( ^10ist eine Abkürzung für 0..9, daher berechnet der obige Code die Summe der ersten zehn Terme in der Reihe.)

Ich habe $adas nächste Semester aus dem Closure Computing gestrichen . Ein einsamer $ist der gleiche wie (state $)ein anonymer Zustandsskalar. Ich habe es zu einem Pre-Inkrement anstatt zu einem Post-Inkrement gemacht, um den gleichen Effekt wie bei der Initialisierung $avon zu erzielen 1.

Wir haben jetzt das letzte (große!) Problem, auf das Sie in einem Kommentar unten hingewiesen haben.

Vorausgesetzt, keiner seiner Operanden ist ein Num(ein Float und somit ungefähr), gibt der /Operator normalerweise eine 100% ige Genauigkeit zurück Rat(ein rationales Rational mit begrenzter Genauigkeit). Wenn der Nenner des Ergebnisses jedoch 64 Bit überschreitet, wird dieses Ergebnis in a umgewandelt, Numdas die Leistung gegen Genauigkeit eintauscht, ein Kompromiss, den wir nicht eingehen möchten. Das müssen wir berücksichtigen.

Um eine unbegrenzte Genauigkeit sowie eine 100% ige Genauigkeit festzulegen , zwingen Sie die Operation einfach zur Verwendung von FatRats. Um dies richtig zu machen, machen Sie einfach (mindestens) einen der Operanden zu einem FatRat(und keinen anderen zu einem Num):

say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf

Ich habe dies auf 500 Dezimalstellen überprüft. Ich erwarte, dass es genau bleibt, bis das Programm abstürzt, weil ein Limit der Raku-Sprache oder des Rakudo-Compilers überschritten wird. (Siehe meine Antwort auf Kann 65536 Bit breites Bigint nicht in native Ganzzahl entpacken, um eine Diskussion darüber zu erhalten.)

Fußnoten

1 Raku hat einige wichtige mathematische Konstanten eingebaut, einschließlich e, iund pi(und dessen Alias π). So kann man Eulers Identität in Raku so schreiben, wie es in Mathematikbüchern aussieht. Mit Anerkennung für RosettaCodes Raku-Eintrag für Eulers Identität :

# There's an invisible character between <> and i⁢π character pairs!
sub infix:<⁢> (\left, \right) is tighter(&infix:<**>) { left * right };

# Raku doesn't have built in symbolic math so use approximate equal 
say e**i⁢π + 1 ≅ 0; # True

2 Damians Artikel ist ein Muss. Aber es ist nur eine von mehreren bewundernswerten Behandlungen, die zu den über 100 Übereinstimmungen für ein Google für "Raku" Eulernummer "gehören .

3 Siehe TIMTOWTDI vs TSBO-Apoo-OWTDI für eine der ausgeglicheneren Blick auf TIMTOWTDI von einem Fan von Python geschrieben. Aber es gibt Nachteile, TIMTOWTDI zu weit zu bringen. Um diese letztere "Gefahr" widerzuspiegeln, prägte die Perl-Community die humorvoll lange, unlesbare und untertriebene TIMTOWTDIBSCINABTE - Es gibt mehr als einen Weg, dies zu tun, aber manchmal ist Konsistenz auch keine schlechte Sache, ausgesprochen "Tim Toady Bicarbonate". Seltsamerweise verwendete Larry Bicarbonat auf Rakus Design und Damian wendete es auf das Rechnen ein Raku an.

Raiph
quelle
Danke für die Antwort. Der Abschnitt mit dem Titel Mein Weg basierend auf Ihrem Weg löst es ziemlich gut. Ich muss allerdings die Feinheiten nachschlagen. Ich wusste nicht, dass eine Ebene $eine Abkürzung ist state $, es ist ziemlich praktisch.
Lars Malmsteen
Gibt es eine Möglichkeit, die Anzahl der Stellen efür die 3. Lösung anzugeben (mit dem Titel Mein Weg basierend auf Ihrem Weg )? Ich habe versucht, FatRat (500) neben 1 in: hinzuzufügen ... given 1.FatRat(500), ..., um die Zahlen auf 500 Stellen genau zu machen, aber es hat nicht funktioniert.
Lars Malmsteen
@LarsMalmsteen Ich habe Ihre sehr wichtige FatRatFrage im letzten Abschnitt angesprochen . Ich habe auch die ganze Antwort verfeinert, obwohl die einzige große Änderung das FatRatZeug ist. (Übrigens ist mir klar, dass ein Großteil meiner Antwort wirklich tangential zu Ihrer ursprünglichen Frage ist. Ich vertraue darauf, dass es Ihnen nichts ausgemacht hat, all die zusätzlichen Flusen zu schreiben, um mich selbst zu unterhalten und vielleicht für spätere Leser interessant zu sein.)
Raiph
Vielen Dank für den zusätzlichen Aufwand. Die .FatRatErweiterung muss also in den Codegenerator eingefügt werden. Jetzt habe ich es mit FatRatdieser Methode versucht und es hat das e auf die Genauigkeit von über 1000 Stellen berechnet . Der zusätzliche Flaum ist währenddessen wert. Zum Beispiel wusste ich nicht, dass das saydie langen Arrays / Sequenzen abschneidet. Solche Informationen sind gut zu wissen.
Lars Malmsteen
@LarsMalmsteen :) "Die .FatRatErweiterung muss also in den Codegenerator eingefügt werden." Ja. Wenn ein Ausdruck, der eine Teilung beinhaltet, bereits ausgewertet wurde, ist es im Allgemeinen zu spät, um den Schaden rückgängig zu machen, der verursacht wurde, wenn die RatGenauigkeit des Überlaufs überschritten wurde. Wenn dies der Fall ist, wird es zu einem Num(float) ausgewertet, was wiederum alle weiteren damit verbundenen Berechnungen beeinträchtigt und diese ebenfalls ergibt Num. Der einzige Weg, um sicherzustellen, dass die Dinge bleiben, FatRatbesteht darin , sie zu startenFatRat und Nums zu vermeiden . Ints und Rats sind in Ordnung, vorausgesetzt, es gibt mindestens eine FatRat, die Raku wissen lässt, dass sie sich an FatRats halten soll.
Raiph
9

Es gibt Brüche in $_. Also brauchst du 1 / (1/$_ * $a++)oder eher $_ /$a++.

Mit Raku können Sie diese Berechnung Schritt für Schritt durchführen

1.FatRat,1,2,3 ... *   #1 1 2 3 4 5 6 7 8 9 ...
andthen .produce: &[*] #1 1 2 6 24 120 720 5040 40320 362880
andthen .map: 1/*      #1 1 1/2 1/6 1/24 1/120 1/720 1/5040 1/40320 1/362880 ...
andthen .produce: &[+] #1 2 2.5 2.666667 2.708333 2.716667 2.718056 2.718254 2.718279 2.718282 ...
andthen .[50].say      #2.71828182845904523536028747135266249775724709369995957496696762772
Wamba
quelle
Nett. Ich hatte keine Ahnung von andthen.
Holli