Tính số e bằng Raku


9

Tôi đang cố gắng tính hằng số điện tử ( Số AKA Euler ) bằng cách tính công thức e

Để tính giai thừa và phân chia trong một lần bắn, tôi đã viết điều này:

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

Nhưng nó đã không thành công. Làm thế nào để làm điều đó một cách chính xác?


Theo cách nào nó đã làm: "nó không thành công"?
SherylHohman

1
Phần mẫu số trong mã mẫu không hoạt động vì nó sử dụng biến giá trị trước đó $_ , trong nỗ lực xây dựng giai thừa. Nó rõ ràng là dư thừa. Trong giải pháp chính xác dưới đây, $_đã bị loại bỏ và nó hoạt động hoàn hảo.
Lars Malmsteen

Cảm ơn. Tôi đoán, tôi đã tìm kiếm nhiều hơn về ý nghĩa chính xác của câu nói đó. Giống như có một lỗi, làm thế nào không phù hợp với những gì bạn đang mong đợi, loại điều đó. Tôi đoán, tính toán của bạn không phù hợp với câu trả lời đã biết cho tính toán đó. Vui mừng bạn đã nhận nó làm việc ra !! Ngoài ra, mô tả bài trả lời tuyệt vời về vấn đề thực sự là gì :-)
SherylHohman

Câu trả lời:


11

Tôi phân tích mã của bạn trong phần Phân tích mã của bạn . Trước đó tôi trình bày một vài phần thú vị của tài liệu tiền thưởng.

Một lót Một chữ cái 1

say e; # 2.718281828459045

"Một chuyên luận về nhiều cách" 2

Nhấp vào liên kết ở trên để xem bài viết đặc biệt của Damian Conway về điện toán etrong Raku.

Bài viết rất thú vị (sau tất cả, đó là Damian). Đó là một cuộc thảo luận rất dễ hiểu về điện toán e. Và đó là sự tôn kính đối với sự tái sinh bicarbonate của Raku về triết lý TIMTOWTDI được tán thành bởi Larry Wall. 3

Là một món khai vị, đây là một trích dẫn từ khoảng nửa bài báo:

Cho rằng tất cả các phương thức hiệu quả này đều hoạt động theo cùng một cách bằng cách tóm tắt (một tập hợp con ban đầu) một chuỗi thuật ngữ vô hạn mà có lẽ sẽ tốt hơn nếu chúng ta có một chức năng để làm điều đó cho chúng ta. Và chắc chắn sẽ tốt hơn nếu hàm có thể tự giải quyết chính xác bao nhiêu tập con ban đầu của chuỗi mà nó thực sự cần bao gồm để tạo ra một câu trả lời chính xác ... thay vì yêu cầu chúng tôi tự kết hợp thông qua kết quả của nhiều thử nghiệm để khám phá điều đó.

Và, như thường thấy ở Raku, thật dễ dàng để xây dựng đúng thứ chúng ta cần:

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

Phân tích mã của bạn

Đây là dòng đầu tiên, tạo ra chuỗi:

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

Việc đóng ( { code goes here }) tính một thuật ngữ. Một bao đóng có một chữ ký, ngầm hoặc rõ ràng, xác định có bao nhiêu đối số sẽ chấp nhận. Trong trường hợp này không có chữ ký rõ ràng. Việc sử dụng $_( biến "chủ đề" ) dẫn đến một chữ ký ngầm yêu cầu một đối số ràng buộc $_.

Toán tử chuỗi ( ...) liên tục gọi hàm đóng bên trái của nó, chuyển thuật ngữ trước đó làm đối số của bao đóng, để lười biếng xây dựng một loạt các thuật ngữ cho đến khi điểm cuối bên phải của nó, trong trường hợp này là* viết tắt cho Infvô cực.

Chủ đề trong cuộc gọi đầu tiên đến đóng cửa là 1. Vì vậy, việc đóng cửa tính toán và trả về1 / (1 * 1) hai thuật ngữ đầu tiên trong chuỗi như 1, 1/1.

Chủ đề trong cuộc gọi thứ hai là giá trị của cuộc gọi trước 1/1, tức là 1một lần nữa. Vì vậy, việc đóng cửa tính toán và trả về 1 / (1 * 2), mở rộng chuỗi tới 1, 1/1, 1/2. Tất cả có vẻ tốt.

Các đóng cửa tiếp theo tính toán 1 / (1/2 * 3)đó là 0.666667. Thuật ngữ đó nên được 1 / (1 * 2 * 3). Giáo sư.

Làm cho mã của bạn khớp với công thức

Mã của bạn được cho là phù hợp với công thức:
e

Trong công thức này, mỗi thuật ngữ được tính dựa trên vị trí của nó trong chuỗi. Các k hạn thứ trong loạt (trong đó k = 0 cho là người đầu tiên 1) chỉ là thừa k 's đối ứng.

(Vì vậy, không có gì để làm với giá trị của thuật ngữ trước. Do đó $_, nhận giá trị của thuật ngữ trước, không nên được sử dụng trong bao đóng.)

Hãy tạo một toán tử hậu tố giai thừa:

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

( ×là một toán tử nhân infix, một bí danh Unicode trông đẹp hơn của infix ASCII thông thường *.)

Đó là viết tắt của:

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

(Tôi đã sử dụng ký hiệu siêu thực giả trong niềng răng để biểu thị ý tưởng thêm hoặc bớt bao nhiêu thuật ngữ theo yêu cầu.

Tổng quát hơn, việc đặt một toán tử infix optrong dấu ngoặc vuông ở đầu biểu thức tạo thành một toán tử tiền tố tổng hợp tương đương với reduce with => &[op],. Xem Giảm metaoperoper để biết thêm.

Bây giờ chúng ta có thể viết lại bao đóng để sử dụng toán tử postfix giai thừa mới:

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

Chơi lô tô. Điều này tạo ra loạt đúng.

... cho đến khi nó không, vì một lý do khác. Vấn đề tiếp theo là độ chính xác số. Nhưng hãy giải quyết điều đó trong phần tiếp theo.

Một lớp lót có nguồn gốc từ mã của bạn

Có thể nén ba dòng xuống một:

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

.[^10]áp dụng cho chủ đề, được thiết lập bởi given. ( ^10là viết tắt của 0..9, vì vậy đoạn mã trên sẽ tính tổng của mười thuật ngữ đầu tiên trong chuỗi.)

Tôi đã loại bỏ $akhỏi máy tính đóng cửa nhiệm kỳ tiếp theo. Một đơn độc $giống như (state $), một vô hướng trạng thái vô định. Tôi đã làm cho nó một pre-increment thay vì hậu increment để đạt được tác dụng tương tự như bạn đã làm bằng cách khởi tạo $ađể1 .

Bây giờ chúng ta còn lại với vấn đề cuối cùng (lớn!), Được bạn chỉ ra trong một bình luận dưới đây.

Với điều kiện không phải toán hạng của nó là Num(một số float và do đó gần đúng), /toán tử thường trả về độ chính xác 100% Rat(một tỷ lệ chính xác có giới hạn). Nhưng nếu mẫu số của kết quả vượt quá 64 bit thì kết quả đó được chuyển đổi thành một Num- giao dịch hiệu suất cho chính xác, một sự đánh đổi mà chúng ta không muốn thực hiện. Chúng ta cần phải tính đến điều đó.

Để chỉ định độ chính xác không giới hạn cũng như độ chính xác 100%, chỉ cần ép buộc thao tác sử dụng FatRats. Để làm điều này một cách chính xác, chỉ cần làm cho (ít nhất) một trong các toán hạng là một FatRat(và không ai khác là a Num):

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

Tôi đã xác minh điều này đến 500 chữ số thập phân. Tôi hy vọng nó vẫn chính xác cho đến khi chương trình gặp sự cố do vượt quá giới hạn của ngôn ngữ Raku hoặc trình biên dịch Rakudo. (Xem câu trả lời của tôi về Không thể bỏ hộp lớn 65536 bit thành số nguyên gốc để thảo luận về điều đó.)

Chú thích

1 Raku có một vài hằng số toán học quan trọng được xây dựng trong, bao gồm e, ipi(và bí danh của nó π). Do đó, người ta có thể viết Danh tính của Euler trong Raku giống như trong sách toán học. Với khoản tín dụng cho mục nhập Raku của RosettaCode cho Danh tính của 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 bài viết của Damian là phải đọc. Nhưng đó chỉ là một trong một số phương pháp điều trị đáng ngưỡng mộ nằm trong số hơn 100 trận đấu cho một google cho 'raku "số euler"' .

3 Xem TIMTOWTDI vs TSBO-APOO-OWTDI để biết một trong những quan điểm cân bằng hơn về TIMTOWTDI được viết bởi một người hâm mộ của trăn. Nhưng có những nhược điểm khi dùng TIMTOWTDI quá xa. Để phản ánh "mối nguy hiểm" sau này, cộng đồng Perl đã tạo ra TIMTOWTDIBSCINABTE dài một cách hài hước - Có nhiều cách để làm điều đó nhưng đôi khi sự nhất quán không phải là điều xấu, hay được phát âm là "Tim Toady bicarbonate". Thật kỳ lạ , Larry đã áp dụng bicarbonate vào thiết kế của Raku và Damian áp dụng nó vào điện toán etrong Raku.


Cảm ơn bạn đã trả lời. Phần có tiêu đề Cách của tôi dựa trên cách của bạn giải quyết nó khá tốt. Tôi cần phải tìm kiếm những phức tạp mặc dù. Tôi không biết một đồng bằng $là một tốc ký state $, nó khá tiện dụng.
Lars Malmsteen

Có cách nào để chỉ định số chữ số echo giải pháp thứ 3 (có tiêu đề Cách của tôi dựa trên cách của bạn ) không? Tôi đã thử thêm FatRat (500) bên cạnh 1 in: ... given 1.FatRat(500), ...để làm cho các số chính xác 500 chữ số, nhưng nó không hoạt động.
Lars Malmsteen

@LarsMalmsteen Tôi đã giải quyết FatRatcâu hỏi rất quan trọng của bạn trong phần cuối cùng. Tôi cũng đã mài giũa toàn bộ câu trả lời, mặc dù thay đổi lớn duy nhất là FatRatthứ. (Btw, tôi nhận ra rằng có rất nhiều câu trả lời của tôi là thực sự tiếp tuyến với câu hỏi ban đầu của bạn, tôi tin tưởng bạn không quan tâm tôi viết tất cả các lông tơ thêm để giải trí bản thân mình và có lẽ rất thú vị cho độc giả sau này.)
raiph

Cảm ơn bạn đã nỗ lực thêm. Vì vậy, .FatRatphần mở rộng phải được đặt bên trong trình tạo mã. Bây giờ tôi đã thử nó với cách FatRatthêm này và nó đã tính toán e với độ chính xác hơn 1000 chữ số. Các lông tơ thêm vào là trong thời gian dài. Chẳng hạn, tôi không biết rằng việc saycắt các mảng / chuỗi dài. Những thông tin như vậy là tốt để biết.
Lars Malmsteen

@LarsMalmsteen :) "Vì vậy, .FatRatphần mở rộng phải được đặt bên trong trình tạo mã.". Đúng. Tổng quát hơn, nếu một biểu thức liên quan đến phân chia đã được đánh giá, thì đã quá muộn để hoàn tác thiệt hại gây ra nếu nó tràn ra Ratđộ chính xác. Nếu nó có, nó sẽ đánh giá một Num(float) và đến lượt nó làm mờ đi bất kỳ tính toán nào khác liên quan đến nó, làm cho chúng cũng được Num . Cách duy nhất để đảm bảo mọi thứ ở lại FatRatbắt đầu chúng FatRatvà tránh mọi Nums. Ints và Rats đều ổn, miễn là có ít nhất một cái FatRatđể cho Raku biết bám vào FatRats.
raiph

9

Có phân số trong $_. Vì vậy, bạn cần 1 / (1/$_ * $a++)hoặc đúng hơn $_ /$a++.

Bởi Raku bạn có thể thực hiện phép tính này từng bước

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

Đẹp. Tôi không có ý tưởng về andthen.
Holli
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.