昨日の続きです。
さて、プログラムの高速化という話しなのですが、あのプログラムのどこがボトルネックだったのでしょうか。
正解は、数学関連の関数、つまりmath.hで定義されている関数です。
階乗的に増え続けるパターン数、その再帰処理ループの中に時間の架かる計算をさせていては高速化は望めませんね。
例えば、レンジ内に収まっているのかをピタゴラスの定理で距離を求めていました。
z = sqrt(x*x+y*y);
if ( z < r ) {
/* 出力処理 */
}
これね。
sqrt();関数は時間がかかるんですよ。
ならば、レンジのrを2乗したr2と、sqrt();せずにzの2乗したものを比較するほうが早い。
z2 = x*x+y*y;
if ( z2 < r2 ) {
/* 出力処理 */
}
さて、もう一つ、いや二つ、数学関連関数を使っていますよね。
そうです、sin();、cos();です。
x = 0.0;
y = 0.0;
a = 360.0/m;
for (i=0; i<m; i++) {
x += l[i]*cos(a*i*M_PI/180.0);
y -= l[i]*cos(a*i*M_PI/180.0);
}
これこれ。
毎回、同じ値にl[i]を掛けているだけなんですよ。
ならば、事前に配列に格納しておいて、再帰処理ループ内では配列を使えばいいですね。
main()の中で、
a = b = 2.0*M_PI/180.0;
c[0] = 1.0;
s[0] = 0.0;
for (i=1; i<m; i++, b+=a) {
c[i] = cos(b);
s[i] = sin(b);
}
こんな感じで予め配列に値を格納しておいて、再帰処理内で、
x = 1.0;
y = 0.0;
for (i=1; i<m; i++) {
x += l[i]*c[i];
y -= l[i]*s[i];
}
とすれば、前のプログラムよりかなりの速度アップが望めました。
また、x, yの値も、上記のループをやらず、再帰処理でデータとして渡して、各々やれば、もう少し速くなるかもしれませんが、再帰処理にあんまりデータを渡すのは好きじゃないんだけど、まぁ速度アップが見込めるならばやってもいいかな。
さて、mがいくつのとき、解があって、解の個数がどれくらいだと予想されましたでしょうか?
m | pattern | loop | sample |
3 | 0 | 1 | |
4 | 0 | 3 | |
5 | 0 | 12 | NEAR 1 4 3 2 5 |
6 | 2 | 60 | 1 4 5 2 3 6 1 5 3 4 2 6 |
7 | 0 | 360 | NEAR 1 4 7 2 3 5 6 |
8 | 0 | 2520 | NEAR 1 4 7 3 6 2 5 8 |
9 | 0 | 20160 | NEAR 1 5 9 4 2 6 7 3 8 |
10 | 24 | 181440 | 1 8 2 9 5 6 3 7 4 10 1 8 4 7 5 6 3 9 2 10 |
11 | 0 | 1814400 | NEAR 1 8 9 5 2 6 10 7 3 4 11 |
12 | 732 | 19958400 | 1 5 10 3 9 11 4 2 7 6 12 8 |
13 | 0 | 239500800 | NEAR 1 2 7 12 13 4 5 3 8 6 11 9 10 |
14 | 720 | 3113510400 | 1 12 5 4 7 10 13 2 11 6 3 8 9 14 |
15 | 48 | 43589145600 | 1 9 14 4 12 2 7 15 5 10 3 8 13 6 11 |
16 | 解析中 | 653837184000 | |
17 | 解析中 | 11115232128000 | |
18 | 検討中 | 188958946176000 |
私の予想はmが6の倍数ならば必ず解があるだろう。
mが偶数ならば、pがある程度あれば解があるだろう。
mが奇数ならば、偶数よりは難しいかもしれないが、解があったらいいな。
程度でした。
m=10、12、14で解があったこと、m=15で解があったことは、ある程度予想通りでした。
次の記事は、m=16の解析待ちですかね。
ではでは