Calcolo del numero elettronico tramite Raku


9

Sto cercando di calcolare la costante e ( il numero di Eulero dell'AKA ) calcolando la formula e

Per calcolare il fattoriale e la divisione in un colpo solo, ho scritto questo:

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

Ma non ha funzionato. Come farlo correttamente?


In che modo: "non ha funzionato"?
SherylHohman,

1
La parte denominatore nel codice di esempio non ha funzionato perché utilizzava la variabile di valore precedente $_ , nel tentativo di costruire il fattoriale. Era ovviamente ridondante. Nella soluzione corretta di seguito, è $_stato eliminato e ha funzionato perfettamente.
Lars Malmsteen,

Grazie. Immagino che stavo cercando di più per cosa intendesse esattamente quella frase. Come se ci fosse un errore, come non era coerente con quello che ti aspettavi, quel genere di cose. Suppongo che il tuo calcolo non corrispondesse alle risposte note per quel calcolo. Sono contento che tu l'abbia risolto !! Inoltre, ottima descrizione post-risposta su quale fosse il vero problema :-)
SherylHohman,

Risposte:


11

Analizzo il tuo codice nella sezione Analizzare il tuo codice . Prima di allora vi presento un paio di sezioni divertenti di materiale bonus.

Una lettera Una lettera 1

say e; # 2.718281828459045

"Un trattato su più vie" 2

Fai clic sul link sopra per vedere l'articolo straordinario di Damian Conway esull'informatica a Raku.

L'articolo è molto divertente (dopo tutto, è Damian). È una discussione molto comprensibile sull'informatica e. Ed è un omaggio alla reincarnazione bicarbonato di Raku della filosofia TIMTOWTDI promossa da Larry Wall. 3

Come antipasto, ecco una citazione da circa la metà dell'articolo:

Dato che questi metodi efficienti funzionano tutti allo stesso modo - sommando (un sottoinsieme iniziale di) una serie infinita di termini - forse sarebbe meglio se avessimo una funzione per farlo per noi. E sarebbe certamente meglio se la funzione potesse capire da sola esattamente la quantità di quel sottoinsieme iniziale della serie che deve effettivamente includere per produrre una risposta accurata ... piuttosto che richiederci di esaminare manualmente i risultati di prove multiple per scoprirlo.

E, come spesso accade a Raku, è sorprendentemente facile costruire esattamente ciò di cui abbiamo bisogno:

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

Analizzando il tuo codice

Ecco la prima riga, generando la serie:

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

La chiusura ( { code goes here }) calcola un termine. Una chiusura ha una firma, implicita o esplicita, che determina quanti argomenti accetterà. In questo caso non esiste una firma esplicita. L'uso della $_( variabile "topic" ) genera una firma implicita che richiede un argomento a cui è associato $_.

L'operatore sequenza ( ...) chiama ripetutamente la chiusura alla sua sinistra, passando il termine precedente come argomento della chiusura, per costruire pigramente una serie di termini fino all'endpoint alla sua destra, che in questo caso è *, abbreviazione di Infaka infinito.

L'argomento nella prima chiamata alla chiusura è 1. Quindi la chiusura calcola e ritorna 1 / (1 * 1)dando i primi due termini della serie come 1, 1/1.

L'argomento nella seconda chiamata è il valore di quello precedente 1/1, ovvero di 1nuovo. Quindi la chiusura calcola e ritorna 1 / (1 * 2), estendendo la serie a 1, 1/1, 1/2. Sembra tutto a posto.

La prossima chiusura calcola 1 / (1/2 * 3)quale è 0.666667. Quel termine dovrebbe essere 1 / (1 * 2 * 3). Ops.

Rendere il codice corrispondente alla formula

Il tuo codice dovrebbe corrispondere alla formula:
e

In questa formula, ogni termine viene calcolato in base alla sua posizione nella serie. Il k esimo termine della serie (dove k = 0 per il primo 1) è semplicemente il reciproco di K fattoriale .

(Quindi non ha nulla a che fare con il valore del termine precedente. Pertanto $_, che riceve il valore del termine precedente, non dovrebbe essere usato nella chiusura.)

Creiamo un operatore fattoriale postfix:

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

( ×è un operatore di moltiplicazione di infissi, un alias Unicode dall'aspetto più gradevole della solita infissione ASCII *.)

Questa è una scorciatoia per:

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

(Ho usato la notazione pseudo metasintattica all'interno delle parentesi graffe per indicare l'idea di aggiungere o sottrarre tutti i termini richiesti.

Più in generale, mettere un operatore infisso optra parentesi quadre all'inizio di un'espressione costituisce un operatore prefisso composito che è l'equivalente di reduce with => &[op],. Vedi Metaoperatore di riduzione per maggiori informazioni.

Ora possiamo riscrivere la chiusura per utilizzare il nuovo operatore fattoriale postfix:

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

Bingo. Questo produce le serie giuste.

... fino a quando non lo fa, per un motivo diverso. Il prossimo problema è l'accuratezza numerica. Ma affrontiamolo nella prossima sezione.

Una fodera derivata dal tuo codice

Forse comprimi le tre righe in una:

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

.[^10]si applica all'argomento, che è impostato da given. ( ^10è una scorciatoia per 0..9, quindi il codice sopra riportato calcola la somma dei primi dieci termini della serie.)

Ho eliminato il $acalcolo dalla chiusura il prossimo termine. Un solitario $è lo stesso di (state $)uno scalare anonimo di stato. Ho fatto un pre-incremento, invece di post-incremento per ottenere lo stesso effetto, come avete fatto per l'inizializzazione $aa 1.

Ora abbiamo lasciato il problema finale (grande!), Sottolineato da te in un commento qui sotto.

A condizione che nessuno dei suoi operandi sia un Num(a virgola mobile, e quindi approssimativo), l' /operatore restituisce normalmente una precisione del 100% Rat(una precisione razionale limitata). Ma se il denominatore del risultato supera i 64 bit, quel risultato viene convertito in un Num- che scambia le prestazioni per accuratezza, un compromesso che non vogliamo fare. Dobbiamo tenerne conto.

Per specificare una precisione illimitata e un'accuratezza del 100%, è sufficiente forzare l'operazione per usare FatRats. Per farlo correttamente, basta fare (almeno) uno degli operandi come un FatRat(e nessun altro essere un Num):

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

Ho verificato questo con 500 cifre decimali. Mi aspetto che rimanga preciso fino a quando il programma non si arresta in modo anomalo a causa del superamento di alcuni limiti del linguaggio Raku o del compilatore Rakudo. (Vedi la mia risposta a Impossibile decomprimere bigint a 65536 bit in un intero nativo per alcune discussioni su questo.)

Le note

1 Raku ha alcuni importanti costanti matematiche integrate, tra cui e, ie pi(e il suo alias π). Così si può scrivere l'identità di Eulero in Raku un po 'come appare nei libri di matematica. Con credito alla voce Raku di RosettaCode per l'identità di Euler :

# 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 L'articolo di Damian è assolutamente da leggere. Ma è solo uno dei tanti trattamenti ammirevoli che sono tra le oltre 100 partite di un google per "raku" numero di eulero "' .

3 Vedi TIMTOWTDI vs TSBO-APOO-OWTDI per una delle viste più equilibrate di TIMTOWTDI scritta da un fan di Python. Ma ci sono aspetti negativi nel portare troppo TIMTOWTDI. Per riflettere quest'ultimo "pericolo", la comunità del Perl ha coniato il TIMTOWTDIBSCINABTE , ironicamente lungo, illeggibile e discreto - Esiste più di un modo per farlo, ma a volte la coerenza non è una cosa negativa, ha pronunciato "Tim Toady Bicarbonate". Stranamente , Larry ha applicato il bicarbonato al design di Raku e Damian lo applica al computing ein Raku.


Grazie per la risposta. La sezione intitolata My way based on your way lo risolve abbastanza bene. Devo cercare le complessità però. Non sapevo che una pianura $fosse una scorciatoia per state $, è abbastanza utile.
Lars Malmsteen, il

C'è un modo per specificare il numero di cifre di eper la terza soluzione (intitolata La mia strada in base alla tua strada )? Ho provato ad aggiungere FatRat (500) accanto a 1 in: ... given 1.FatRat(500), ...per rendere i numeri una precisione di 500 cifre, ma non ha funzionato.
Lars Malmsteen, il

@LarsMalmsteen Ho affrontato la tua FatRatdomanda molto importante nell'ultima sezione. Ho anche affinato l'intera risposta, anche se l'unico grande cambiamento è la FatRatroba. (A proposito, mi rendo conto che gran parte della mia risposta è davvero tangenziale alla tua domanda originale; confido che non ti dispiaccia che scriva tutta la lanugine in più per divertirmi e forse essere interessante per i lettori successivi.)
Raiph

Grazie per lo sforzo extra. Quindi l' .FatRatestensione deve essere inserita nel generatore di codice. Ora l'ho provato con l' FatRataggiunta in questo modo e ha calcolato l' e con una precisione di oltre 1000 cifre. La lanugine aggiuntiva aggiunta è molto utile. Ad esempio, non sapevo che il saytroncamento delle lunghe matrici / sequenze. Tali informazioni sono utili da sapere.
Lars Malmsteen,

@LarsMalmsteen :) "Quindi l' .FatRatestensione deve essere inserita nel generatore di codice.". Sì. Più in generale, se un'espressione che coinvolge la divisione è già stata valutata, è troppo tardi per annullare il danno causato se trabocca Ratla precisione. Se lo è, valuterà un Num(float) e che a sua volta ridurrà eventuali ulteriori calcoli che lo coinvolgono, rendendoli anche Num . L'unico modo per rendere il soggiorno le cose FatRatè quello di iniziare a loro FatRated evitare eventuali Nums. Intse Ratsono a posto, a condizione che ce ne sia almeno uno FatRata far sapere a Raku di attenersi a FatRats.
Raiph,

9

Ci sono delle frazioni dentro $_. Quindi hai bisogno 1 / (1/$_ * $a++)o meglio $_ /$a++.

Con Raku potresti fare questo calcolo passo dopo passo

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

Bello. Non ne avevo idea andthen.
Holli il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.