質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1134閲覧

ビュッホンの針を用いたpiの近似

nanyakanya

総合スコア44

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2020/07/30 15:10

ビュッフォンの針を用いてpiを導くプログラムを作成しようと思います。

y軸上に0~1の範囲をとって、そこにながさ1の針の始点yをランダムでとり、またθを針とx軸とのなす角として、θを0~2piまでの乱数とする。ここで針の終点がy+sinθであるから、その値が0以下か1以上であるならばnumを+1して最終的にm(1000)階の施行からnum/1000として確率を求める
悩んでいること

result1 :2.100000e-002
result2 :4.300000e-002
result3 :7.300000e-002
result4 :9.200000e-002
result5 :1.220000e-001
以上のような結果となってしまった。
あまりにも理論値とかけ離れているのがどこが原因なのかわからない
コード

c

1#include<stdio.h> 2#include<math.h> 3#include<stdlib.h> 4#include<time.h> 5#define pi 3.141592 6 int main(void){ 7 int m = 1000; 8 int i,j,k,num = 0; 9 srand ((unsigned int)time(NULL)); 10 int randam[5][1000] = {0}; 11 for(i = 0; i < 5 ; i++ ){ 12 for(j = 0; j < m; j++){ 13 randam[i][j] =rand(); 14 } 15 } 16 17 for(i = 0; i < 5; i++ ){ 18 for(j = 0 ; j < m ; j++ ){ 19 double x,y; 20 21 x = (float)randam[i][j] / RAND_MAX;//0~1の範囲の小数点乱数 22 y = x + sin( ((float)randam[i][m-j])* 2 * pi); 23 24 if(y <= 0 || 1 <= y){ 25 num++; 26 } 27 28 //printf("%lf %lf\n",x,y); 29 } 30 printf("result%d :%e\n",i+1,(double)num/m); 31 } 32 return 0; 33 } 34

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikadzuchi

2020/07/31 00:55

再現しませんでした。 当該コードを私の環境で実行したところ、一例として以下になりました。 result1 :6.430000e-01 result2 :1.301000e+00 result3 :1.925000e+00 result4 :2.553000e+00 result5 :3.224000e+00 初期化忘れでresult2以降結果が誤っている点を除き、これは想定通りの値です。(線の間隔の違いで)2で割り、逆数をとればπになります。 お示しの2.1e-2などの結果はどうやって出たものですか?
nanyakanya

2020/07/31 01:08

atomを用いてwindowsのコマンドプロンプトでの結果となります。 再現性がないのはなんでなのかわからりません。 いまreple.itで試したところikadzuchiさんのようになりました。
guest

回答3

0

ちょっと気づいたのですが、sin()の引数をラジアンでなく度だと解釈して計算すると、お示しのものによく似た出力が出ます。
考えにくいことではありますが、まさか引数を度で解釈する特殊なsin関数が用意されているということはないでしょうか。
atomは使ったことが無いのですが(そもそもatomはテキストエディタであってコンパイラは別にあるのではないかと思いますが)、その環境でsinに様々な値を入れるとどのような結果が出ますか?


(追記)
上記、表計算で値をいじって確かめていた際の気づきなのですが、この際コードを読み違えており、実際のコードでは以上のような結果は出ません。すみませんでした。(詳細はコメント参照)
ただ、そのような出力が出る理由が分からないことには変わりありません。
sinと、あとrandの出力もどのようになるか気になりますので試していただけると幸いです。

投稿2020/07/31 17:26

編集2020/08/01 08:59
ikadzuchi

総合スコア3047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Penpen7

2020/07/31 20:59 編集

度だと解釈するとというのは、質問のコードの一文を y = x + sin( ((float)randam[i][m-j])* 2 * 180) という風にするという意味ですかね? result1 :6.380000e-01 result2 :1.292000e+00 result3 :1.923000e+00 result4 :2.562000e+00 result5 :3.210000e+00 となってオーダーが一桁違うので、同じとは言えないとおもいます。
ikadzuchi

2020/08/01 06:27

それは「『引数を度と解釈するsin関数』があったとして、それに正しい結果を出させるための入力」ですね。 引数をラジアンと解釈する(普通の)sin関数にその入力を入れてもほぼ正常な出力が出て参考になりません。 「引数を度と解釈するsin関数」を普通のsin関数で再現したいなら、 sin( ((float)randam[i][m-j])* 2 * pi * pi/180) です。
Penpen7

2020/08/01 08:32 編集

なるほど。引数を度ととるために、現在の引数をラジアンに変更するという意味ですね。 しかし、y = x + sin( ((float)randam[i][m-j])* 2 * pi * pi/180);としても、 result1 :6.370000e-01 result2 :1.260000e+00 result3 :1.893000e+00 result4 :2.531000e+00 result5 :3.156000e+00 となりますが...
ikadzuchi

2020/08/01 08:39

おや、本当ですね。 計算は表計算で行っていたのですが何か勘違いしていたかもしれません。すいません、考え直します。
Penpen7

2020/08/01 08:43

承知しました 返信ありがとうございます
ikadzuchi

2020/08/01 08:51

分かりました。sin関数に入れている乱数は0~1に正規化していない非常に大きい数なのを見落としていました。 これではsinの引数が度でもラジアンでも実質的に何の違いも出ません。
Penpen7

2020/08/01 08:56

確かに y = x + sin( ((float)randam[i][m-j])/RAND_MAX* 2 * pi * pi/180); とすれば、 result1 :5.600000e-02 result2 :1.050000e-01 result3 :1.580000e-01 result4 :2.120000e-01 result5 :2.650000e-01 似たようなオーダーになりますね
guest

0

ベストアンサー

気になったところ

  • 配列は必要ありません。冗長です。0-1の値さえ出てくればいいので関数を作りました。
  • 棒の長さなどが与えられていないです。(こちらで陽に与えました)
  • math.hをインクルードしているのであれば、円周率は自分で定義せずM_PIを使いましょう。
  • 答えの分数は逆数にしなければなりません。
  • numは試行するたびに0に初期化しなければなりません。

コード

C

1#define _USE_MATH_DEFINES 2#include <math.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <time.h> 6// 0から1の一様分布に従う乱数を返す 7double calculateUniformRandom() 8{ 9 return rand() / (double)(RAND_MAX + 1.0); 10} 11 12int main(void) 13{ 14 // 棒の長さ 15 const double l = 1.0; 16 // 平行線の距離 17 const double d = 2 * l; 18 // 試行回数 19 const int m = 1000; 20 21 srand((unsigned int)time(NULL)); 22 23 for (int i = 0; i < 5; i++) { 24 int num = 0; 25 for (int j = 0; j < m; j++) { 26 double x = calculateUniformRandom() * 0.5 * d; // [0:d/2]の乱数 27 double y = l * 0.5 * sin(calculateUniformRandom() * M_PI * 0.5); 28 29 if (x - y <= 0 || x + y >= d) { 30 num++; 31 } 32 } 33 printf("result%d :%lf\n", i + 1, m / (double)num); 34 } 35 return 0; 36} 37

結果

text

1result1 :3.067485 2result2 :3.215434 3result3 :3.154574 4result4 :3.215434 5result5 :3.012048

円周率を使用しない例

C

1#define _USE_MATH_DEFINES 2#include <math.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <time.h> 6 7// [min, max]の範囲の一様分布に従う乱数を返す 8double calculateUniformRandom(double min, double max) 9{ 10 return min + (double)(rand() * (max - min + 1.0) / (1.0 + RAND_MAX)); 11} 12 13// 円周率を使用せず大きさrのベクトルを計算する(一様分布) 14void calculeteUniformRandomVectorNoPI(double* x, double* y, double r) 15{ 16 double xtemp, ytemp, rtemp2; 17 18 // 正方形の中から, 半径1の円内部の座標を得るまで繰り返す。 19 do { 20 xtemp = calculateUniformRandom(0, 1.0), ytemp = calculateUniformRandom(0, 1.0); 21 rtemp2 = xtemp * xtemp + ytemp * ytemp; 22 } while (rtemp2 > 1.0); 23 24 // 正規化し, 大きさrにする。 25 double rtemp = sqrt(rtemp2); 26 *x = xtemp / rtemp * r; 27 *y = xtemp / rtemp * r; 28} 29 30// 円周率を使用して大きさrのベクトルを計算(一様分布) 31void calculeteUniformRandomVectorPI(double* x, double* y, double r) 32{ 33 double theta = M_PI * 0.5 * calculateUniformRandom(0, 1.0); 34 *x = r * cos(theta); 35 *y = r * sin(theta); 36} 37 38double calculatePi(int tryNumber, double l) 39{ 40 int num = 0; 41 double d = l * 2; 42 43 for (int i = 0; i < tryNumber; i++) { 44 double centerY = calculateUniformRandom(0, d * 0.5); 45 double directionX, directionY; 46 47 calculeteUniformRandomVectorNoPI(&directionX, &directionY, l * 0.5); 48 if (centerY - directionY <= 0.0 || centerY + directionY >= d) 49 num++; 50 } 51 52 return tryNumber / (double)num; 53} 54 55int main(void) 56{ 57 srand((unsigned int)time(NULL)); 58 59 for (int i = 0; i < 5; i++) { 60 printf("result%d :%lf\n", i + 1, calculatePi(1000, 1.0)); 61 } 62 return 0; 63} 64

追記(最小限の修正)

ご質問のコードだと、平行線の間隔が1, 棒の長さが0.5になりますね。

C

1#include <math.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5#define pi 3.141592 6int main(void) 7{ 8 int m = 1000; 9 int i, j; 10 srand((unsigned int)time(NULL)); 11 int randam[5][1000] = { 0 }; 12 for (i = 0; i < 5; i++) { 13 for (j = 0; j < m; j++) { 14 randam[i][j] = rand(); 15 } 16 } 17 18 for (i = 0; i < 5; i++) { 19 int num = 0; 20 for (j = 0; j < m; j++) { 21 double x, y; 22 x = (float)randam[i][j] / (RAND_MAX + 1.0); //0~1の範囲の小数点乱数 23 y = x + 0.5 * sin(((float)randam[i][m - j]) / (RAND_MAX + 1.0) * 2 * pi); 24 25 if (y <= 0 || 1 <= y) { 26 num++; 27 } 28 } 29 printf("result%d :%e\n", i + 1, m / (double)num); 30 } 31 return 0; 32}

投稿2020/07/30 20:56

編集2020/07/31 21:12
Penpen7

総合スコア698

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanyakanya

2020/07/31 00:15

ビュッフォンの針の理解自体が甘いことに気づきました。 別海まで示していただきありがとうございました!
guest

0

そもそも円周率を求めたいのに

θを0~2piまでの乱数とする

と、前提に円周率を使っては意味がありません。

投稿2020/07/30 16:57

編集2020/07/30 17:02
Bearded-Ockham

総合スコア430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanyakanya

2020/07/31 00:15

ご指摘ありがとうございます! 改善します
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問