原点を中心とする半径nの円周または円内にある整数格子点の数を数えます。
そののち、整数格子点の数/(n^2)を計算しπに近づいているかを確かめる、というものです。
一応計算はできますが、計算時間が遅いです。
1億までを3秒で計算しなければいけません。
なにか良い方法があれば、教えてください。
#include<stdio.h>
#include<math.h>
int i=0,j;
long check;
double answer;
double count(int m){
for(i=1;i<m;i++){
for(j=1;j<m;j++){
if(ii+jj<=mm){
check++;
}
}
}
answer=((double)(check4+m4+1)/(double)(mm)); //long型とint型の計算
return answer;
}
int main(void){
int n; scanf("%d",&n); printf("%d:%.15f\n",n,count(n)); return 0;
}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答7件
0
ベストアンサー
投稿2015/11/17 05:24
編集2015/11/17 05:53総合スコア13521
0
すでに解決済みではありますが、、
とても興味深いテーマでしたので、私なりに考えてみました。
まず、以下の図をご覧ください。
青い扇形は半径R
(Rは整数)の円の第一象限です。
この図に収まる、辺の長さが1の正方形(以下、単位正方形と記載します)の個数を考えます。
赤い部分(①)は、上の図に収まる、各辺の長さがもっとも大きな整数を持つ正方形です。
②、③、④は、①の正方形の辺を伸ばして第一象限を4分割した、残りの部分です。
このうち、②と③は合同になります。
④に収まる単位正方形は存在しません。
つまり、第一象限に収まる単位正方形の個数は
① + 2 * ②
で求めることができます。
これを4倍すると、半径R
の円に収まる単位正方形の個数となります。
さらに、円に収まる格子点の数は、単位正方形の個数にX軸、Y軸上の格子点の数を足せばよいので、
半径`R`の円に収まる格子点の数 = 4 * (① + 2 * ②) + 4 * R + 1 // 最後の"+ 1"は原点
となります。
①の面積は
floor(R / √2) ^ 2
ですので、これが①に収まる単位正方形の数です。
②についてはozwk様に紹介いただいた方法で求めることができますが、x
の値は
floor(R / √2) + 1 <= x < R
の範囲で考えれば十分なので、
② = Σ floor(√(R^2 * x^2)) : floor(R / √2) + 1 <= x < R
と、なります。
まとめると、
半径`R`の円に収まる格子点の数 = 4 * (floor(R / √2) ^ 2 + 2 * (Σ floor(√(R^2 * x^2)))) + 4 * R + 1
です。
式で書くと意味不明ですねw
以下が、上の式をJavaで実装したものです。
(すみません、C言語は不勉強です。。)
java
1public class Main { 2 3 private static final long R = 100000000; 4 5 private static final long SQUARED_R = R * R; 6 private static final long R_DEVIDED_BY_SQRT2 = (long) (R / Math.sqrt(2)); 7 private static final long LARGEST_SQUARE = R_DEVIDED_BY_SQRT2 * R_DEVIDED_BY_SQRT2; // ① 8 9 /** 10 * @param args 11 */ 12 public static void main(String[] args) { 13 14 System.out.println("R\t= " + R); 15 System.out.println("R * R\t= " + SQUARED_R); 16 System.out.println("R / √2\t= " + R_DEVIDED_BY_SQRT2); 17 System.out.println("第一象限に収まる最も大きな正方形の面積\t= " + LARGEST_SQUARE); 18 System.out.println(); 19 20 long temp = 0; // ② 21 22 for (long x = R_DEVIDED_BY_SQRT2 + 1; x < R; x++) { 23 long y = (long) Math.sqrt(SQUARED_R - x * x); 24 25 temp += y; 26 } 27 28 long result = 4 * (2 * temp + LARGEST_SQUARE) + 4 * R + 1; 29 30 System.out.println(result); 31 } 32 33}
実行結果
R = 100000000 R * R = 10000000000000000 R / √2 = 70710678 第一象限に収まる最も大きな正方形の面積 = 4999999983219684 31415926535867961
以下の環境で実行したところ、1秒弱で結果を得ることができました。
Windows7 Home Premium Intel Core i7-2600 3.40GHz JDK 1.6
もっとも、丸め誤差などを一切、考慮していないので、
この計算が合っているかはちょっと、自信がありませんが。。
投稿2015/11/17 15:24
編集2015/11/17 15:35総合スコア4791
0
ozwkさんのように三平方の定理などでループ回数を減らせば現実的な時間でできるかもしれませんが、それをやるなら円の面積の公式を使えばループしないで計算できるということになってしまいます……。
この手の仕事にはモンテカルロ法が真っ先に思い浮かぶのですが、全ての点を1点1点数えることが重要なのであれば、1億×1億のループを現実的な時間で処理するのは不可能ですね。
仮にCPUクロックが10GHzの超高速マシンをお使いになっているとして、ループ1回分に10クロックかかるとして、1億×1億の処理時間を計算すると、
100,000,000 * 100,000,000 / (10,000,000,000 / 10) = 10,000,000 [秒]
約115日と17時間ですね。
追記
ちょっと勘違いしていたみたいです。面目ない。
投稿2015/11/17 09:03
編集2015/11/17 10:49総合スコア5938
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/11/17 09:57
2015/11/17 10:15
2015/11/17 10:23
2015/11/17 10:45
2015/11/17 12:16
0
count(), count8(), count_x() の 3 つのメソッドをかいてみました。 (最終敵はコードは count_x() です)
counx_x() は m = 一億で 0.9 秒でした。
実行環境は以下です。
MacBook Pro (13-inch, Mid 2012), OSX 10.11.1, gcc 4.2.1)
(もっと性能のよいマシンなら,さらに短くなるはず。最新の上位機種の MacBook が欲しいなぁ ...)
c
1// See https://teratail.com/questions/20348 2 3#include<stdio.h> 4#include<math.h> 5 6double count_x(long m) { 7 long count = 0; 8 long mm = m * m; 9 long i = 0; 10 long j = 0; 11 long out_j = m; 12 for (i = 1; i < m; i++) { 13 for (j = out_j; i < j; j--) { 14 if (i * i + j * j <= mm) { 15 count = count + j - i; 16 out_j = j + 1; 17 break; 18 } 19 } 20 } 21 22 long d = 0; 23 for (d = m - 1; d > 0; d--) { 24 if (d * d * 2 < mm) { 25 break; 26 } 27 } 28 return ((double)(count * 8 + m * 4 + d * 4 + 1) / (double)(mm)); 29} 30 31double count(long m) { 32 long count = 0; 33 long mm = m * m; 34 for (long i = 1; i < m; i++) { 35 for (long j = 1; j < m; j++) { 36 if (i * i + j * j <= mm) { 37 count++; 38 } 39 } 40 } 41 return ((double)(count * 4 + m * 4 + 1) / (double)(mm)); 42} 43 44double count8(long m) { 45 long count = 0; 46 long mm = m * m; 47 long i = 0; 48 for (i = 1; i < m; i++) { 49 for (long j = i + 1; j < m; j++) { 50 if (i * i + j * j <= mm) { 51 count++; 52 } 53 } 54 } 55 56 long d = 0; 57 for (d = m - 1; d > 0; d--) { 58 if (d * d * 2 < mm) { 59 break; 60 } 61 } 62 return ((double)(count * 8 + m * 4 + d * 4 + 1) / (double)(mm)); 63} 64 65int main(void) { 66 // int n; 67 //scanf("%d",&n); 68 long n = 10000 * 10000 * 4; 69 70 //printf("%ld: %.15f\n", n, count(n)); 71 //printf("%ld: %.15f\n", n, count8(n)); 72 printf("%ld: %.15f\n", n, count_x(n)); 73 return 0; 74}
それぞれの書き換え経緯を書いていきます。
- count()
==============
まず、質問文のコードを int でなく、long で書き換えました。
このコードは 円の 1/4 のついてだけ計算しています。
( $ rime ./a.out として実行)
m = 100000, 200000 での結果
100000: 3.141592545700000
real 0m28.971s
200000: 3.141592610525000
real 1m55.339s
m が 2 倍になったとき、処理時間が 4 倍となっています。
このことは後で対処することにします。
- count8()
=============
まずは、円の 1/8 についてだけ計算するように書き換えました。
(これで処理時間は半分になるはずです)
m = 100000, 200000 での結果
00000: 3.141592545700000
real 0m14.891s
200000: 3.141592610525000
real 1m0.558s
処理時間は半分になりました。
- count_x()
=============
次は m が 2 倍になったとき、処理時間が 4 倍になっていることに対処します。
処理時間が 2 倍に抑えられていないのは、j のループが無駄なのです。
i のループで 円周上を左から右に移動していきます。2 つのとなりあった 縦線上の円周上の j は
同じか、-1 変化するだけのはずです。
したがって、j を見つけのは、2 回だけの計算で済むのです。
(左隣りの j の値か、 j -1 だけが候補であり、大量にループを回すのは無駄)
この方針で作成したものの処理時間を計測します。
100000: 3.141592545700000
real 0m0.008s
時間が短くなって計測誤差程度になってしまったので、m を増やします。
10000000: 3.141592653505890
real 0m0.128s
20000000: 3.141592653565913
real 0m0.209s
m が 2 倍になったとき、処理時間も 2 倍程度におさえられました。
いよいよ m = 1 億で計測します。
100000000: 3.141592653586796
real 0m0.861s
200000000: 3.141592653589560
real 0m1.709s
400000000: 3.141592653589088
real 0m3.406s
投稿2015/11/22 05:02
編集2015/11/22 06:03総合スコア22324
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
何点か確認したいことと、アドバイスがあります。
- 「1億まで」というのは、1辺が1億で間違いないでしょうか(1万×1万で点のほうが1億、ということではないですよね?)。
- 1億×1億の1兆個の点をまじめに調べていては、3秒では終わりません。うまく工夫して、全点を1つ1つ調べずに数える必要があります。
- コンピューターの環境にもよりますが、1億×1億=1兆は、int型に入りきらないことがあります。環境によってはlongに入りますが、そうでない場合はさらに対策が必要です。
投稿2015/11/17 05:19
総合スコア145183
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。