Rakuを使用してe番号を計算する


9

式を計算してe定数(別名オイラー数) を計算しようとしていますe

階乗と除算を一度に計算するために、私はこれを書きました:

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

しかし、うまくいきませんでした。それを正しく行う方法は?


「うまくいかなかった」とはどういう意味ですか。
SherylHohman

1
サンプルコードの分母部分は 、階乗を構築するために以前の値 $_変数を利用していたため、機能しませんでした。それは明らかに冗長でした。以下の正しいソリューションでは、$_が削除され、完全に機能しました。
Lars Malmsteen

ありがとう。たぶん、私はその発言が正確に何を意味しているかをもっと探していたのだろう。エラーがあったのと同じように、あなたが期待していたものとどのように一貫していなかったか、そのようなことです。あなたの計算はその計算の既知の答えと一致しなかったと思います。うまくいきました!また、実際の問題は何だったのかについての回答後のすばらしい説明:-)
SherylHohman

回答:


11

「コードの分析」のセクションでコードを分析します。その前に、ボーナス素材の楽しいセクションをいくつか紹介します。

1ライナー 1文字1

say e; # 2.718281828459045

「複数の方法に関する論文」2

上記のリンクをクリックして、Damian ConwayのeRaku でのコンピューティングに関する臨時の記事をご覧ください。

記事はとても楽しいです(結局のところ、それはDamianです)。これは、コンピューティングについて非常に理解しやすい議論ですe。そして、それはラリー・ウォールによって支持されたTIMTOWTDI哲学のラクの重炭酸塩の生まれ変わりへのオマージュです。

前菜として、ここに記事のほぼ半分からの引用があります:

これらの効率的な方法はすべて、無限の一連の項(の最初のサブセット)を合計することによって同じように機能するため、それを行う関数があるとよいでしょう。そして、正確な回答を生成するために関数がシリーズの最初のサブセットの実際にどれだけ含める必要があるかを関数自体が正確に計算できれば、確かに良いでしょう...それを発見するための複数の試験。

そして、Rakuでよくあることですが、必要なものだけを構築するのは驚くほど簡単です。

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

コードを分析する

シリーズを生成する最初の行は次のとおりです。

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

クロージャ({ code goes here })は項を計算します。クロージャーには、暗黙的または明示的なシグニチャーがあり、受け入れるシグニチャーの数を決定します。この場合、明示的な署名はありません。$_「トピック」変数)を使用すると、にバインドされている1つの引数を必要とする暗黙の署名が生成され$_ます。

シーケンス演算子(...)は、左側のクロージャを繰り返し呼び出し、前の項をクロージャの引数として渡し、一連の項をその右側のエンドポイント(この場合は無限大の省略形)まで遅延構築します。*Inf

クロージャの最初の呼び出しのトピックは1です。したがって、クロージャ1 / (1 * 1)は、系列の最初の2つの項をとして計算して返します1, 1/1

2番目の呼び出しのトピックは、前の呼び出しの値1/1、つまり、1再びです。したがって、クロージャはを計算して返し1 / (1 * 2)、系列をに拡張し1, 1/1, 1/2ます。それはすべてよさそうだ。

次のクロージャはを計算1 / (1/2 * 3)します0.666667。その用語はそうあるべきです1 / (1 * 2 * 3)。おっとっと。

コードを数式に一致させる

あなたのコードは式に一致するはずです:
e

この式では、各用語は系列内での位置に基づいて計算されます。系列のk番目の項(最初のはk = 0 1)は、階乗kの逆数です。

(したがって、前の期間のとは関係ありません。したがって$_、前の期間のを受け取るをクロージャーで使用しないでください。)

階乗後置演算子を作成しましょう:

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

×は、中置乗算演算子であり、通常のASCII中置の見栄えの良いUnicodeエイリアスです*。)

それは以下の略記です:

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

(私は中括弧内の疑似メタ構文記法を使用して、必要なだけ用語を追加または減算するという考えを示しました。

より一般的にopは、式の先頭で角括弧内に中置演算子を置くと、と同等の複合前置演算子が形成されreduce with => &[op],ます。詳細については、削減メタオペレーターを参照してください。

これで、新しい階乗後置演算子を使用するようにクロージャーを書き直すことができます。

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

ビンゴ。これにより、適切なシリーズが作成されます。

...異なる理由で、それができないまで。次の問題は数値の精度です。しかし、次のセクションでそれを扱いましょう。

コードから派生したワンライナー

多分3行を1つに圧縮します:

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

.[^10]によって設定されるトピックに適用されますgiven。(^10はの省略形な0..9ので、上記のコードはシリーズの最初の10項の合計を計算します。)

私は$a次期の計算から閉鎖から除外しました。孤独$はと同じ(state $)で、匿名の状態スカラーです。に初期化$aすることで行ったのと同じ効果を実現するために、後置インクリメントではなく前置インクリメントにしました1

以下のコメントで指摘されているように、最後の(大きな!)問題が残ります。

いずれのオペランドもa Num(浮動小数点数、したがって概数)でない場合、/演算子は通常100%の正確さRat(限られた精度の有理数)を返します。ただし、結果の分母が64ビットを超える場合、その結果はaに変換されます。Numこれは、パフォーマンスと正確さのトレードオフですが、トレードオフは行いません。それを考慮する必要があります。

無制限の精度と100%の精度を指定するには、FatRats を使用するように操作を強制します。これを正しく行うには、(少なくとも)オペランドの1つをaにしますFatRat(他のオペランドをaにしないでくださいNum)。

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

これを10進数500桁で確認しました。Raku言語またはRakudoコンパイラーの制限を超えてプログラムがクラッシュするまでは、正確であることが期待されます。(それについての議論については、65536ビット幅のbigintをネイティブ整数にアンボックスできないという私の答えを参照しください。)

脚注

1楽に含むに建てられたいくつかの重要な数学定数を、持っているeipi(とそのエイリアスπ)。したがって、数学の本にあるように、オイラーのアイデンティティをラクで書くことができます。オイラーのアイデンティティのためのRosettaCodeのRakuエントリへのクレジット付き

# 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ダミアンの記事は必読です。しかし、それはグーグルの 'raku "euler's number"'の 100以上のマッチの中にあるいくつかの見事な扱いの1つにすぎません。

3 pythonのファンによって書かれたTIMTOWTDIのよりバランスの取れたビューの1つについては、TIMTOWTDITSBO-APOO-OWTDIを参照してください。しかし、TIMTOWTDIを使いすぎてしまうことに欠点があります。この後者の「危険」、ユーモラスに長い、読めない、と控えめ造語Perlコミュニティ反映するためTIMTOWTDIBSCINABTEを -そこですつ以上の方法するために行うことが、時には「ティム・口のうまい人重炭酸塩」と発音一貫性ではありませんA悪いことどちらか、。不思議なことに、ラリーは重炭酸塩をラクの設計に適用し、ダミアンはそれをeラクのコンピューティングに適用しました。


答えてくれてありがとう。あなたのやり方に基づく私のやり方というタイトルのセクションは、それを非常にうまく解決します。しかし、複雑さを調べる必要があります。プレーン$がの省略形でstate $あることを知りませんでした。非常に便利です。
Lars Malmsteen

e3番目のソリューションの桁数を指定する方法はありますか?私は1の隣にFatRat(500)を追加しようとしました:... given 1.FatRat(500), ...数値を500桁の精度にするために、それは機能しませんでした。
Lars Malmsteen

@LarsMalmsteen FatRat最後のセクションであなたの非常に重要な質問に答えました。唯一の大きな変更はFatRatものですが、私はまた、全体の答えを磨きました。(ところで、私の回答の多くは元の質問に非常に接していることに気づきます。私自身を楽しませるために余分な綿毛をすべて書いて、後の読者にとって興味深いかもしれないと気にしていないと信じています。)
レイフ

余分な努力をありがとう。したがって、.FatRat拡張機能はコードジェネレーター内に配置する必要があります。今私はFatRatこの方法を追加して試してみました、そしてそれはeを1000+桁の精度で計算しました。追加された余分な綿毛はワートワーフです。たとえばsay、長い配列/シーケンスが切り捨てられていることを知りませんでした。このような情報は知っておくと役に立ちます。
Lars Malmsteen

@LarsMalmsteen :)「.FatRat拡張機能はコードジェネレーター内に配置する必要があります。」はい。より一般的には、除算を含む式が既に評価されている場合、Rat精度がオーバーフローした場合に生じた損傷を元に戻すには遅すぎます。持っている場合、それはNum(float)に評価され、それはそれを含むさらなる計算を汚染し、それらもにし Numます。物事が確実にとどまる唯一の方法は、物事FatRat開始FatRatNums を回避することです。IntsとRatsは問題ありFatRatませんが、FatRats に固執することをRakuに通知するために少なくとも1つが必要です。
レイフ

9

には端数があり$_ます。したがって、あなたは1 / (1/$_ * $a++)むしろ必要$_ /$a++です。

楽では、この計算を段階的に行うことができます

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

いいね。私は知りませんでしたandthen
Holli
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.