円周率
id:Jagdpanther さんが Gauche で円周率を計算しているのをみて、私もやってみる。
- ガウチェ・モンテール円周率 - Jagdpantherの日記
- http://d.hatena.ne.jp/Jagdpanther/20090126/1232921485
id:Jagdpanther さんは Gauche に標準で付いてくる Mersenne Twister を使ったモンテカルロ法で円周率を計算していたけど、こちらは別な方法で。
Gauche には Mersenne Twister も標準で付いているけれど、gcd
という手続きも標準で付いてくるのだ。最大公約数。これは使える。なぜかというと、Wikipedia で、こんな記述を見つけてしまったから。
自然数から無作為に2つを取り出した時、その2つが互いに素である確率は 6/π2 となる。
ま、まじで?
と思ってしまったので、早速 Scheme で確認してみましょう。まずは手始めに、こんなのを。(まだ gcd
は出てこない):
(define (rc lim) (let loop-n ((m 1)) (let loop-m ((n 1)) (format #t "m=~d\tn=~d\n" m n) (when (< n m) (loop-m (+ n 1)))) (when (< m lim) (loop-n (+ m 1)))))
これを、たとえば (rc 4)
と呼び出してやる。実行するとこうなる。
m=1 n=1 m=2 n=1 m=2 n=2 m=3 n=1 m=3 n=2 m=3 n=3 m=4 n=1 m=4 n=2 m=4 n=3 m=4 n=4
やっていることは、集合 {1, 2, 3, 4}
から、2 つを選び出しているだけ。「4 つのものから重複を許して 2 つを選ぶ」という重複組合せ(repeated combination)だ。出力の中に {1, 1}
とか {3, 3}
っていう行があるので、ちゃんと重複を許しているのがわかる。実際に動かすときには 4
ではなくて、もっと大きな数を与えてやる。
これを使って、「数のペアをひとつ決めるたびに、その 2 つの数が互いに素であるかどうか」を調べたい。そして、「互いに素だった場合」と「互いに素でなかった場合」の数を記録する。互いに素かどうか、を調べるには gcd(最大公約数)を調べればよい。ふたつの数が互いに素であるということは、最大公約数が 1 だ、ということだ。
でも、まず、gcd
を使う前にちょっとリファクタリング。
さっき書いたコードを修正して、「数のペアをひとつ決めるたびに、何かする」という形に書き換える。その「何か」の部分に、さっきと同じ m と n を表示する部分を入れてやる。
#! /usr/local/bin/gosh (define (rc proc lim) ; lim だけじゃなく、手続きもひとつもらうようにした。 (let loop-n ((m 1)) (let loop-m ((n 1)) (proc m n) ; 手続きを m n のペアに適用する。 (when (< n m) (loop-m (+ n 1)))) (when (< m lim) (loop-n (+ m 1))))) (define (main args) (define (print-m-n m n) (format #t "m=~d\tn=~d\n" m n)) (rc print-m-n 4) ; print-m-n という手続きと、4 を引数にして rc を呼ぶ。 0)
これが、さっきのとまったく同じ出力になることを確認してから、gcd
を使って「互いに素かどうか」を調べる部分を書く。ついでに、πも計算。
#! /usr/local/bin/gosh (define (rc proc lim) (let loop-n ((m 1)) (let loop-m ((n 1)) (proc m n) (when (< n m) (loop-m (+ n 1)))) (when (< m lim) (loop-n (+ m 1))))) (define (main args) (define coprime 0) ; 互いに素だった時に 1 増やす。 (define non-coprime 0) ; 互いに素ではなかった時に 1 増やす。 (define (calc-pi coprime non-coprime) (sqrt (/ 6 (/ coprime (+ coprime non-coprime))))) ; √(6/(互いに素が占める割合)) (define (count m n) (if (= 1 (gcd m n)) (set! coprime (+ coprime 1)) (set! non-coprime (+ non-coprime 1))) (format #t "m=~d n=~d gcd(~d, ~d)=~d" m n m n (gcd m n)) (format #t " coprime=~d non-coprime=~d" coprime non-coprime) (format #t " pi=~d\n" (calc-pi coprime non-coprime))) (rc count 4) 0)
実行する。
m=1 n=1 gcd(1, 1)=1 coprime=1 non-coprime=0 pi=2.449489742783178 m=2 n=1 gcd(2, 1)=1 coprime=2 non-coprime=0 pi=2.449489742783178 m=2 n=2 gcd(2, 2)=2 coprime=2 non-coprime=1 pi=3.0 m=3 n=1 gcd(3, 1)=1 coprime=3 non-coprime=1 pi=2.8284271247461903 m=3 n=2 gcd(3, 2)=1 coprime=4 non-coprime=1 pi=2.7386127875258306 m=3 n=3 gcd(3, 3)=3 coprime=4 non-coprime=2 pi=3.0 m=4 n=1 gcd(4, 1)=1 coprime=5 non-coprime=2 pi=2.898275349237888 m=4 n=2 gcd(4, 2)=2 coprime=5 non-coprime=3 pi=3.0983866769659336 m=4 n=3 gcd(4, 3)=1 coprime=6 non-coprime=3 pi=3.0 m=4 n=4 gcd(4, 4)=4 coprime=6 non-coprime=4 pi=3.1622776601683795
たった 10 個のペアを与えただけで、もう 3.1
になってる。(rc count 4)
の 4 を大きな数にしてあげると、じわじわとπに近づいていくのがわかる。
そのへんにいくらでも転がっている自然数を 2 つもってきて、「互いに素」かどうかを調べていくだけで円周率が現れてくれるってのが驚き。
参考
- ガウチェ・モンテール円周率 - Jagdpantherの日記
- http://d.hatena.ne.jp/Jagdpanther/20090126/1232921485
- 円周率 - Wikipedia
- http://ja.wikipedia.org/wiki/%E5%86%86%E5%91%A8%E7%8E%87
- 互いに素 - Wikipedia
- http://ja.wikipedia.org/wiki/%E4%BA%92%E3%81%84%E3%81%AB%E7%B4%A0
- 組合せ (数学) - Wikipedia
- http://ja.wikipedia.org/wiki/%E7%B5%84%E5%90%88%E3%81%9B_%28%E6%95%B0%E5%AD%A6%29
- 二つの自然数が互いに素となる確率 - ひとり予備校@上石神井
- http://beitasaka.exblog.jp/8256318/
- 作者: Alfred S. Posamentier,Ingmar Lehmann,松浦俊輔
- 出版社/メーカー: 日経BP社
- 発売日: 2005/11/03
- メディア: 単行本
- 購入: 2人 クリック: 32回
- この商品を含むブログ (19件) を見る
追記
『ふしぎな数πの伝記』には、昔の数学者が円周率を求めるために行なった、いろんな方法が書いてあった。
- 円を描いて、それを内側と外側から多角形で挟み込んだり。
- 逆に、多角形を描いてそれを内側と外側から円で挟み込んだり。
- 平行線を等間隔でいっぱい描いて、そこに針を落としたり。
そのほかの話ですごく面白かったのが、これ:
「地球を、赤道の長さがきっかり 4 万キロメートルの球であると仮定する。赤道に沿って、4 万キロメートルの紐を巻きつけると、紐はぴったり地面に貼りつくことになる。その状態から紐の長さを 1 メートルだけ長くしてやって、全体的に紐を宙に浮かせた場合、紐は地面からどれ位の高さまで浮くだろうか?」
周の長さが 40,000,000 メートルの円と、周の長さが 40,000,001 メートルの円の、それぞれの半径を求めてあげればいいのだけど、これにはちょっとびっくりした。