最近は、ちょっとプログラミングの話題が多いかな。
今回は、枝刈りについて考えてみようかと思う。
昨日の互いに素の記事では、画期的な方法で枝刈りをしてみました。
今日の題材は、素数とします。
素数も私のブログではよく出てくるものですが、今一度説明しましょう。
素数とは、1と自分自身しか約数を持たない数です。
例を挙げると、2、3、5、7、11、13、17、19、…と無限に存在することが証明されています。
ある自然数nが素数であるか判定するには、nより小さい自然数で割り切れなければ良いということ。
今回はサンプルが多くなるので、プログラム全体ではなく、素数判定ルーチンだけを考えます。
int check(unsigned int n)
で、判定したいnを与え、素数なら1、素数でないなら0を返すサブルーチンとします。
【サブルーチン1】
int check(unsigned int n)
{
unsigned int i;
for (i=2; i<n; i++) {
if ( n%i == 0 ) return 0;
}
return 1;
}
まったく枝刈りをしていない状態です。
これをベースに、どの様な枝刈りを思いつくでしょうか?
終了判定のi<nはかなり無駄で、i<=sqrt(n)に相当するものでよい。
【サブルーチン2】
int check(unsigned int n)
{
unsigned int i, j;
j = sqrt((double)n)+1;
for (i=2; i<j; i++) {
if ( n%i == 0 ) return 0;
}
return 1;
}
かなり枝刈りされましたが、まだまだ枝刈りの余地はあります。
因みに、
for (i=3; i<=sqrt((double)n);p n+=2) { ... }
のようにしてはダメです。
何故かと言うと、終了判定で毎回sqrtを計算しなくてはならないからです。
2以外の素数は全て奇数。
【サブルーチン3】
int check(unsigned int n)
{
unsigned int i, j;
j = sqrt((double)n)+1;
if ( n%2 == 0 ) return 0;
for (i=3; i<j; i+=2) {
if ( n%i == 0 ) return 0;
}
return 1;
}
大抵の人は、このくらいまで枝刈りをします。
もっと枝刈りを増やしてみる。
【サブルーチン4】
int check(unsigned int n)
{
unsigned int i, j;
j = sqrt((double)n)+1;
if ( n%2 == 0 || n%3 == 0 ) return 0;
for (i=5; i<j; i+=6) {
if ( n%i == 0 || n%(i+2) == 0 ) return 0;
}
return 1;
}
【サブルーチン5】
int check(unsigned int n)
{
unsigned int i, j;
j = sqrt((double)n)+1;
if ( n%2 == 0 || n%3 == 0 || n%5 == 0 ) return 0;
for (i=7; i<j; i+=30) {
if ( n%i == 0 || /* 7 37 67 97 ... */
n%(i+4) == 0 || /* 11 41 71 101 ... */
n%(i+6) == 0 || /* 13 43 73 103 ... */
n%(i+10) == 0 || /* 17 47 77 107 ... */
n%(i+12) == 0 || /* 19 49 79 109 ... */
n%(i+16) == 0 || /* 23 53 83 113 ... */
n%(i+22) == 0 || /* 29 59 89 119 ... */
n%(i+24) == 0 ) { /* 31 61 91 121 ... */
return 0;
}
}
return 1;
}
サブルーチン3、4、5は、for分でi+=2、i+=6、i+=30と増えていき、if文の項数は1、2、8個と変化している。
表にまとめると、
if文の項数 | for文の追加数 | 枝刈り率 |
1 | 2 | 50.0000000000% |
2 | 6 | 66.6666666667% |
8 | 30 | 73.3333333333% |
48 | 210 | 77.1428571429% |
480 | 2310 | 79.2207792208% |
5760 | 30030 | 80.8191808192% |
92160 | 510510 | 81.9474643004% |
1658880 | 9699690 | 82.8975977583% |
36495360 | 223092870 | 83.6411804644% |
1021870080 | 6469693230 | 84.2052776898% |
30656102400 | 200560490130 | 84.7147848611% |
1103619686400 | 7420738134810 | 85.1278987838% |
44144787456000 | 304250263527210 | 85.4906329598% |
1854081073152000 | 13082761331670030 | 85.8280601003% |
85287729364992000 | 614889782588491410 | 86.1295907364% |
4434961926979584000 | 32589158477190044730 | 86.3912965716% |
257227791764815872000 | 1922760350154212639070 | 86.6219525619% |
15433667505888952320000 | 117288381359406970983270 | 86.8412648150% |
1018622055388670853120000 | 7858321551080267055879090 | 87.0376638476% |
71303543877206959718400000 | 557940830126698960967415390 | 87.2202319624% |
こんな表が出来上がる。
いくらでも増やしていけるが、どこまで現実的かは、それぞれの判断に委ねることにする。
この表の規則性が解ると面白いかもしれません。
それこそ、この表を作らせる課題とかもいいかもしれません。
15行までならば、64ビットで計算出来ますね。
もっとやるなばら、多倍長演算を実装するとかしないとね。
ではでは