Saya mencoba menghitung konstanta e ( AKA Euler's Number ) dengan menghitung rumus
Untuk menghitung faktorial dan pembagian dalam satu kesempatan, saya menulis ini:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce * + * , @e[^10];
Tapi itu tidak berhasil. Bagaimana cara melakukannya dengan benar?
code-generation
lazy-evaluation
raku
eulers-number
Lars Malmsteen
sumber
sumber
$_
, dalam upaya untuk membangun faktorial. Itu jelas berlebihan. Dalam solusi yang benar di bawah ini,$_
dijatuhkan dan bekerja dengan sempurna.Jawaban:
Saya menganalisis kode Anda di bagian Menganalisis kode Anda . Sebelum itu saya menyajikan beberapa bagian menyenangkan dari materi bonus.
Satu linerSatu huruf 1"Sebuah risalah tentang berbagai cara" 2
Klik tautan di atas untuk melihat artikel luar biasa Damian Conway tentang komputasi
e
di Raku.Artikel ini sangat menyenangkan (lagipula Damian). Ini adalah diskusi komputasi yang sangat dimengerti
e
. Dan itu adalah penghormatan terhadap reinkarnasi bikarbonat Raku terhadap filosofi TIMTOWTDI yang didukung oleh Larry Wall. 3Sebagai hidangan pembuka, inilah kutipan dari sekitar setengah artikel:
Menganalisis kode Anda
Inilah baris pertama, menghasilkan seri:
Penutupan (
{ code goes here }
) menghitung suatu istilah. Penutupan memiliki tanda tangan, baik implisit atau eksplisit, yang menentukan berapa banyak argumen yang akan diterima. Dalam hal ini tidak ada tanda tangan eksplisit. Penggunaan$_
( variabel "topik" ) menghasilkan tanda tangan implisit yang membutuhkan satu argumen yang terikat$_
.Operator urutan (
...
) berulang kali memanggil penutupan di sebelah kirinya, melewati istilah sebelumnya sebagai argumen penutupan, untuk dengan malas membangun serangkaian istilah sampai titik akhir di sebelah kanannya, yang dalam hal ini adalah*
, singkatan untukInf
alias tak terhingga.Topik dalam panggilan pertama ke penutupan adalah
1
. Jadi penutupan menghitung dan mengembalikan1 / (1 * 1)
menghasilkan dua istilah pertama dalam seri sebagai1, 1/1
.Topik dalam panggilan kedua adalah nilai dari panggilan sebelumnya
1/1
, yaitu1
sekali lagi. Jadi penutupan menghitung dan mengembalikan1 / (1 * 2)
, memperluas seri ke1, 1/1, 1/2
. Semuanya terlihat bagus.Penutupan selanjutnya menghitung
1 / (1/2 * 3)
yaitu0.666667
. Istilah itu seharusnya1 / (1 * 2 * 3)
. Ups.Membuat kode Anda sesuai dengan formula
Kode Anda seharusnya cocok dengan rumus:
Dalam rumus ini, setiap istilah dihitung berdasarkan posisinya dalam rangkaian. Istilah k th dalam seri (di mana k = 0 untuk yang pertama
1
) hanyalah faktorial k timbal balik.(Jadi itu tidak ada hubungannya dengan nilai dari istilah sebelumnya. Dengan demikian
$_
, yang menerima nilai dari istilah sebelumnya, tidak boleh digunakan dalam penutupan.)Mari kita buat operator postfix faktorial:
(
×
Adalah operator multiplikasi infiks, alias Unicode yang tampak lebih bagus dari infiks ASCII biasa*
.)Itu singkatan untuk:
(Saya telah menggunakan notasi metasyntactic pseudo di dalam kurung kurawal untuk menyatakan gagasan menambah atau mengurangi sebanyak istilah yang diperlukan.
Lebih umum, menempatkan operator infiks
op
dalam tanda kurung siku pada awal ekspresi membentuk operator awalan komposit yang setara denganreduce with => &[op],
. Lihat Pengurangan metaoperator untuk info lebih lanjut.Sekarang kita dapat menulis ulang penutupan untuk menggunakan operator postfix faktorial baru:
Bingo. Ini menghasilkan seri yang tepat.
... sampai tidak, untuk alasan yang berbeda. Masalah selanjutnya adalah akurasi numerik. Tapi mari kita hadapi itu di bagian selanjutnya.
Satu liner berasal dari kode Anda
Mungkin kompres ketiga garis menjadi satu:
.[^10]
berlaku untuk topik, yang diatur olehgiven
. (^10
adalah singkatan0..9
, jadi kode di atas menghitung jumlah dari sepuluh istilah pertama dalam seri.)Saya telah menghilangkan
$a
dari penutupan komputasi istilah berikutnya. Satu-satunya$
adalah sama dengan(state $)
, skalar keadaan anonynous. Saya membuat pra-increment bukan pasca-kenaikan untuk mencapai efek yang sama seperti yang Anda lakukan dengan menginisialisasi$a
untuk1
.Kami sekarang memiliki masalah (besar!) Final, yang ditunjukkan oleh Anda dalam komentar di bawah.
Asalkan operandnya tidak merupakan
Num
(float, dan dengan demikian perkiraan),/
operator biasanya mengembalikan 100% akuratRat
(rasional presisi terbatas). Tetapi jika penyebut dari hasil melebihi 64 bit maka hasil itu dikonversi ke aNum
- yang memperdagangkan kinerja untuk akurasi, tradeoff yang tidak ingin kita buat. Kita perlu memperhitungkannya.Untuk menentukan presisi tak terbatas serta akurasi 100%, cukup paksakan operasi untuk menggunakan
FatRat
s. Untuk melakukan ini dengan benar, buat saja (setidaknya) salah satu operan menjadiFatRat
(dan tidak ada yang lain menjadiNum
):Saya telah memverifikasi ini hingga 500 digit desimal. Saya berharap itu tetap akurat sampai program crash karena melebihi batas bahasa Raku atau kompilator Rakudo. (Lihat jawaban saya untuk Tidak dapat membuka kotak bigint 65536 bit ke dalam integer asli untuk beberapa diskusi tentang itu.)
Catatan kaki
1 Raku memiliki beberapa konstanta matematika penting dibangun, termasuk
e
,i
danpi
(dan alias-nyaπ
). Dengan demikian orang dapat menulis Identitas Euler dalam Raku seperti yang terlihat di buku matematika. Dengan kredit ke entri Raku RosettaCode untuk Identitas Euler :2 Artikel Damian harus dibaca. Tapi itu hanya salah satu dari beberapa perawatan mengagumkan yang ada di antara 100+ kecocokan untuk google untuk 'raku "nomor euler"' .
3 Lihat TIMTOWTDI vs TSBO-APOO-OWTDI untuk salah satu pandangan TIMTOWTDI yang lebih seimbang yang ditulis oleh penggemar python. Tapi ada yang downsides untuk mengambil TIMTOWTDI terlalu jauh. Untuk merefleksikan "bahaya" yang terakhir ini, komunitas Perl menciptakan TIMTOWTDIBSCINABTE yang panjang, penuh humor, dan tidak dimengerti - Ada Lebih Dari Satu Cara Untuk Melakukannya Tetapi Terkadang Konsistensi Bukanlah Hal yang Buruk, diucapkan "Tim Toady Bicarbonate". Anehnya , Larry menerapkan bikarbonat pada desain Raku dan Damian menerapkannya pada komputasi
e
di Raku.sumber
$
adalah singkatanstate $
, itu cukup berguna.e
untuk solusi ke-3 (berjudul Cara saya berdasarkan cara Anda )? Saya sudah mencoba menambahkan FatRat (500) di sebelah 1 in:... given 1.FatRat(500), ...
untuk membuat angka 500-digit presisi, tetapi tidak berhasil.FatRat
pertanyaan Anda yang sangat penting di bagian terakhir. Saya juga mengasah seluruh jawaban, meskipun satu-satunya perubahan besar adalahFatRat
hal - hal itu. (Btw, saya menyadari bahwa sebagian besar jawaban saya benar-benar bersinggungan dengan pertanyaan awal Anda; saya percaya Anda tidak keberatan saya menulis semua bulu ekstra untuk menghibur diri sendiri dan mungkin menarik bagi pembaca kemudian.).FatRat
ekstensi harus diletakkan di dalam pembuat kode. Sekarang saya mencobanya denganFatRat
menambahkan cara ini dan menghitung e ke 1000 digit presisi. Bulu ekstra yang ditambahkan sementara waktu. Sebagai contoh saya tidak tahu bahwasay
itu memotong array panjang / urutan. Informasi seperti itu baik untuk diketahui..FatRat
ekstensi harus dimasukkan ke dalam pembuat kode." Iya. Lebih umum, jika ekspresi yang melibatkan divisi telah dievaluasi, sudah terlambat untuk membatalkan kerusakan yang disebabkan jika meluapRat
presisi. Jika sudah, ia akan mengevaluasi keNum
(float) dan pada gilirannya mencemari setiap perhitungan lebih lanjut yang melibatkannya, membuatnya jugaNum
. Satu-satunya cara untuk memastikan hal tinggalFatRat
adalah untuk memulai merekaFatRat
dan menghindariNum
s.Int
s danRat
s OK, asalkan setidaknya ada satuFatRat
untuk membiarkan Raku tahu untuk tetap padaFatRat
s.Ada pecahan di
$_
. Jadi, Anda perlu1 / (1/$_ * $a++)
atau lebih tepatnya$_ /$a++
.Dengan Raku Anda bisa melakukan perhitungan ini langkah demi langkah
sumber
andthen
.