Raku kullanarak e sayısının hesaplanması


9

Ben hesaplamak çalışıyorum e sabiti ( AKA Euler sayısı formülü hesaplayarak) e

Faktöriyel ve bölünmeyi tek bir çekimde hesaplamak için şunu yazdım:

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

Ama işe yaramadı. Nasıl doğru yapılır?


Ne şekilde: "işe yaramadı"?
SherylHohman

1
Örnek koddaki payda kısmı işe yaramadı çünkü faktöriyel yapıyı denemek için önceki değer $_ değişkenini kullanıyordu . Açıkçası gereksizdi. Aşağıdaki doğru çözümde $_bırakıldı ve mükemmel bir şekilde çalıştı.
Lars Malmsteen

Teşekkürler. Sanırım, bu ifadenin tam olarak ne anlama geldiğini daha çok arıyordum. Bir hata varmış gibi, ne beklediğinizle, bu tür bir şeyle nasıl tutarlı değildi. Sanırım, hesaplamanız bu hesaplama için bilinen cevaplarla eşleşmedi. Çalıştığına sevindim !! Ayrıca, asıl sorunun ne olduğuna dair harika bir cevap sonrası açıklaması :-)
SherylHohman

Yanıtlar:


11

Ben bölümünde kodunuzu analiz kodunuzu analiz . Bundan önce bonus malzemelerin birkaç eğlenceli bölümünü sunuyorum.

Bir astar Bir harf 1

say e; # 2.718281828459045

"Çok yönlü bir inceleme" 2

Damian Conway'in eRaku'da bilgi işlem hakkındaki olağanüstü makalesini görmek için yukarıdaki bağlantıyı tıklayın .

Makale çok eğlenceli (sonuçta Damian). Bu, hesaplamanın çok anlaşılır bir tartışması e. Raku'nun Larry Wall tarafından benimsenen TIMTOWTDI felsefesinin bikarbonat reenkarnasyonu için bir saygı. 3

Meze olarak, makalenin yaklaşık yarısından bir alıntı:

Bu etkili yöntemlerin hepsinin aynı şekilde çalıştığı göz önüne alındığında - sonsuz bir terim dizisini (ilk alt kümesini) toplayarak - belki bunu bizim için yapacak bir fonksiyonumuz olsaydı daha iyi olurdu. Ve işlevin, sonuçların manuel olarak taranmasını istemek yerine, doğru bir cevap üretmek için aslında serinin ilk alt kümesinin tam olarak ne kadarını içermesi gerektiği daha iyi olurdu. bunu keşfetmek için birden fazla deneme.

Raku'da olduğu gibi, tam da ihtiyacımız olanı inşa etmek şaşırtıcı derecede kolaydır:

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

Kodunuzu analiz etme

İşte diziyi oluşturan ilk satır:

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

Closure ( { code goes here }) bir terimi hesaplar. Bir kapanışın, kaç argümanı kabul edeceğini belirleyen kapalı veya açık bir imzası vardır. Bu durumda açık bir imza yoktur. $_( "Topic" değişkeni ) kullanımı, bağlı bir argüman gerektiren örtük bir imza ile sonuçlanır $_.

Sekans operatörü ( ...) , sağdaki uç noktaya kadar tembel bir şekilde bir dizi terim oluşturmak için önceki terimi kapanışın argümanı olarak geçirerek soldaki kapağı tekrar tekrar çağırır; bu durumda aka sonsuzluk *için Infkısaydı.

İlk kapanış çağrısında yer alan konu 1. Böylece kapanış 1 / (1 * 1)hesaplanır ve dizideki ilk iki terimi verir 1, 1/1.

İkinci çağrıdaki konu, bir öncekinin değeridir 1/1, yani 1tekrar. Böylece kapanış hesaplar ve geri döner 1 / (1 * 2), seriyi genişletir 1, 1/1, 1/2. Her şey iyi görünüyor.

Bir sonraki kapanış 1 / (1/2 * 3)olanı hesaplar 0.666667. Bu terim olmalı 1 / (1 * 2 * 3). Hata.

Kodunuzu formülle eşleştirme

Kodunuzun aşağıdaki formüle uyması gerekiyor:
e

Bu formülde, her terim, serideki konumuna göre hesaplanır . K (seri terimi inci k için ilk = 0 1) sadece faktörlüdür k 'in karşılıklı.

(Dolayısıyla , önceki dönemin değeri ile ilgisi yoktur . Bu nedenle $_, önceki dönemin değerini alan kapanışta kullanılmamalıdır.)

Faktöriyel bir postfix operatörü oluşturalım:

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

( ×bir infix çarpma operatörüdür, normal ASCII infix'in daha hoş görünen Unicode diğer adıdır* .)

Bu kısayol için:

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

(Gerektiği kadar terim ekleme veya çıkarma fikrini belirtmek için parantez içindeki sahte metasyntaktik notasyonu kullandım.

Daha genel olarak, opbir ifadenin başlangıcında bir infix operatörünün köşeli parantez içine alınması, eşdeğeri olan bileşik bir önek operatörü oluşturur reduce with => &[op],. Daha fazla bilgi için İndirgeme metaoperatörü bölümüne bakın .

Şimdi yeni faktöriyel postfix operatörünü kullanmak için kapatmayı yeniden yazabiliriz:

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

Bingo. Bu doğru seriyi üretir.

... farklı bir sebepten ötürü gelene kadar. Bir sonraki sorun sayısal doğruluktur. Ama bir sonraki bölümde bununla başa çıkalım.

Kodunuzdan türetilen tek bir astar

Belki üç çizgiyi bire sıkıştırın:

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

.[^10]tarafından belirlenen konuya uygulanır given. ( ^10kısayol 0..9, bu nedenle yukarıdaki kod serideki ilk on terimin toplamını hesaplar.)

$aGelecek dönem bilgisayar kapanıştan elimine ettik . Yalnız $, (state $)anonim bir durum skaleriyle aynıdır . Sana başlatarak yaptığı gibi aynı etkiyi elde etmek için bu artırma öncesi yerine artırma sonrası yapılan $aiçin 1.

Şimdi, aşağıdaki yorumda belirttiğiniz son (büyük!) Sorundan ayrıldık.

Operandlarının hiçbirinin bir Num(şamandıra ve dolayısıyla yaklaşık) olmaması şartıyla , /operatör normal olarak% 100 doğruluk Rat(sınırlı bir hassas rasyonel) döndürür . Ancak sonucun paydası 64 bit'i aşarsa, bu sonuç bir Numperformansa dönüştürülür - ki bu performans için doğruluk için işlemek, yapmak istemediğimiz bir ödünleşmedir. Bunu hesaba katmalıyız.

Sınırsız hassasiyet ve% 100 doğruluk belirtmek için , FatRats'yi kullanmak için işlemi zorlayın . Bunu doğru bir şekilde yapmak için, (en azından) işlenenlerden birini a FatRat(ve hiçbiri a değil Num) yapın:

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

Bunu 500 ondalık basamağa kadar doğruladım. Raku dili veya Rakudo derleyicisinin bazı sınırlarını aşması nedeniyle program çökene kadar doğru kalmasını bekliyorum. (Bkz cevabım değil Unbox 65536 bit genişliğinde yerli tamsayı içine bigint Can'ın ondan biraz tartışma için.)

Dipnotlar

1 Raku dahil yerleşik birkaç önemli matematiksel sabitleri vardır e, ive pi(ve takma π). Böylece Raku'da Euler'in Kimliğini matematik kitaplarında göründüğü gibi yazabiliriz. Kredi ile Euler Kimlik için RosettaCode en Raku girişi :

# 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 Damian'ın makalesi mutlaka okunmalı. Ancak , 'raku "euler sayısı' ' için bir google için 100'den fazla maç arasında yer alan takdire şayan tedavilerden sadece bir tanesi .

3 Bkz TSBO-apoo-OWTDI vs TIMTOWTDI piton bir fan tarafından yazılan TIMTOWTDI daha dengeli görünümlerden biri için. Ama olan çok uzakta TIMTOWTDI alarak kötü yanları. Bu son "tehlikeyi" yansıtmak için, Perl topluluğu mizahi bir şekilde uzun, okunamayan ve sade olan TIMTOWTDIBSCINABTE'ı icat etti - Bunu yapmak için birden fazla yol var, ancak bazen Tutarlılık Kötü Bir Şey Değil, "Tim Toady Bikarbonat" olarak telaffuz edildi. Garip bir şekilde Larry Raku'nun tasarımına bikarbonat uyguladı ve Damian eda Raku'daki bilgisayarlara uyguladı .


Cevap için teşekkür ederim. Yolunuza dayalı Yolum başlıklı bölüm bunu oldukça iyi çözüyor. Gerçi inceliklere bakmam gerek. Bir ovanın $bir stenografi olduğunu bilmiyordum state $, oldukça kullanışlı.
Lars Malmsteen

e3. çözüm için basamak sayısını belirtmenin bir yolu var mı (Yolunuza göre yolum başlıklı )? 1 in yanında FatRat (500) eklemeyi denedim: ... given 1.FatRat(500), ...sayılar 500 basamaklı hassas yapmak için, ama işe yaramadı.
Lars Malmsteen

@LarsMalmsteen FatRatSon bölümde çok önemli sorunuza değindim . Ben de tek cevabı buldum, ama tek büyük değişiklik bu FatRat. (Btw, cevabımın çoğunun orijinal sorunuza gerçekten teğet olduğunu anlıyorum; kendimi eğlendirmek ve belki de daha sonraki okuyucular için ilginç olmak için tüm ekstra
tüyleri yazmama aldırmamanıza güveniyorum

Ekstra çaba için teşekkürler. Bu yüzden .FatRatuzantı kod üretecinin içine konulmalıdır. İle Şimdi denedim FatRatbu şekilde katma ve hesaplanan e 1000+ basamak hassasiyetle için. Eklenen ekstra tüy wortwhited. Mesela saybunun uzun dizileri / dizileri kırptığını bilmiyordum . Bu tür bilgi parçalarını bilmek iyidir.
Lars Malmsteen

@LarsMalmsteen :) "Yani .FatRatuzantı kod üretecinin içine konmalı ." Evet. Daha genel olarak, bölünmeyi içeren bir ifade zaten değerlendirilmişse, Rathassasiyeti taşması durumunda ortaya çıkan hasarı geri almak için çok geç . Varsa, bir Num(şamandıra) olarak değerlendirilir ve bununla ilgili diğer hesaplamaları da lekeleyerek bunları da yapar Num . Şeyler konaklama sağlamak için tek yol FatRatolduğunu başlatmak onları FatRatve herhangi kaçınmak Nums. Raku'nun s'ye bağlı kalmasını bilmesini sağlayan en az bir tane varsa, Ints ve Rats tamam . FatRatFatRat
raiph

9

İçinde kesirler var $_. Böylece ihtiyacınız var 1 / (1/$_ * $a++)ya da daha doğrusu $_ /$a++.

Raku ile bu hesaplamayı adım adım yapabilirsiniz

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

Güzel. Hiçbir fikrim yoktu andthen.
Holli
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.