![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
素数を求めるプログラムを作りました。
素数か、そうでないかを1か0で区別していたのですが、
よりメモリを効率よく使いたいため、booleanを使ったらどうだという案をいただきやってみたのですが、エラーが出てしまいました。
このプログラムの何がいけないのですか?
#include<stdio.h>
#include<stdbool.h>
#define n 250000
main(){
int i,p,k,w,np,s;
bool pn[n];
np=0;
for(i=0;i<n;i++){
pn[i]=false;
}
for(i=0;i<=n;i++){
if(pn[i]==false){
p=3*i+5-(i%2);
w=2*p;
for(k=i+w;k<=n;k+=w){
pn[k]=true;
}
s=5*i+7-2*(i%2);
for(k=s;k<n;k+=w){
pn[k]=true;
}
np++;
}
}
printf("%10d",np+2);
}
No.5ベストアンサー
- 回答日時:
ご説明ありがとうございます。
どういう意図で組まれたのかようやく納得できました。
が、当方そこまでの理解力が無かったので、純粋にp=iの関係と考えて素数が求まっているように見えなかったというだけです。2の倍数・3の倍数を抜いて計算するという考え方は正しいと思います。(私も以前に2の倍数を除いたエラトステネスの篩なら組んだ事があります。懸賞問題向けだったのですがエラトステネスの篩での応募多数のため予め2の倍数を除いたものは「素数の計算を一部省略している」という理由で不正解とされた不本意な結果でしたが。)
残念ながら、作成されたプログラムのアルゴリズムが正しいか否かは私にはすぐには判断できません。
ただ、少なくとも配列pnの範囲をオーバーしたアクセスがあるのは確かなのでpnを1つ大きく作成するか、for 終端条件の見直しは必要かと思います。
No.4
- 回答日時:
まず、他の方も答えていますが bool は一般的なコンパイラは int 等で実装されているため余り記憶領域の節約にはなりません。
No.2の方のようにビットマップを使うべきでしょう。あと、上記プログラムは上から3番目のforの停止条件がk<=nとなっていて、k=nとなるケースがあり配列pn[n]の領域外アクセスが発生してしまいます。k<nとすべきです。
ただ、上記プログラムを修正したとしても素数を求めているようには思えませんし結果も間違ってます。エラトステネスの篩(ふるい)的なアプローチのようにも見えますがどうなのでしょうか?(エラトステネスの篩になるよう改造してみましたがここに出してしまうとご本人の勉強にならないと思いますので伏せておきます。)
この回答への補足
エラトステネスの篩の応用バージョンのつもりです。
エラトステネスの篩で、予め2と3の倍数をぬいてしまい、篩にかけることで、メモリ使用率を減らそうという目的でつくりました。
まず、添え字iと、対応する自然数pの関係を考える。
普通に考えた時のプログラムでは、p=i、
2の倍数だけを除いたプログラムではp=2*i+3の関係があった。
2,3の倍数を除いたプログラムでは・・・
添え字i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
自然数p 5 7 11 13 17 19 23 25 29 31 35 37 41 43 47 49
添え字iが偶数(0も含む)の時は p=3*i+5
添え字iが奇数の時は p=3*i+4
の関係があることがわかる。さらに、「iが偶数の時0、奇数の時1になる式」(i%2)を使って、p=3*i+5-(i%2)でまとめられる。
次に、倍数を消していく作業について考える。
添え字iが偶数の時、奇数の時をそれぞれ別々の数列で考えると、どちらも公差6の等差数列になっていることがわかる。
iが偶数 5 11 17 23 29 35 41 47 53
iが奇数 7 13 19 25 31 37 43 49
そこで、例えば5の倍数を消していく作業について考えると、偶数数列においては5から始めて5番目ごとが5の倍数になっているのでそれらを消せばよいことがわかる。つまり、実際(奇数偶数を分けないで考える)は、2×5=10ごとに消せばいいことがわかる。奇数数列においては、最初に5の倍数がでてくる、25から始めれば良い。この25という数字はs=5*i+7-2*(i%2)で求められる(この式については最後に述べる)。
つまり、pから始めて2*pごと、s=5*i+7-2*(i%2)から始めて2*pごとに消していくことにより、倍数を消していく作業が完了する。
3、プログラムについて
1)pn[i]すべてに1をいれる
2)i=0から指定された数を超えるまで以下の作業を繰り返す
2-1)pn[i]の値が1ならば、iに対応する自然数pは素数であるので
A)pは残して2*p以降、2*pごとにpn[i]の値に0を入れる
B)Sから始めて、2*pごとにpn[i]の値に0を入れる
2-2)pn[i]の値が0ならば、iの値を1増やす
4、補足s=5*i+7-2*(i%2)について
2と同様「5」を例にとって考える。まず、5自身は偶数の数列にいるので、偶数の数列において5ずつ消していけば、5の倍数は消えていった。次に奇数数列の25については、このプログラム自身が2と3の倍数を抜いて考えているのだから、5×2=10(2の倍数)、5×3=15(3の倍数)、5×4=20(2の倍数)は現れないはずである。よって5×5=25が最小であることがわかる。ここで疑問に思うのが、pと5*pとは添え字の奇数・偶数が必ず異なるのか?ということである。
しかし、実際に5を例にとって考えると、5と25の添え字の奇数・偶数は異なっている。幸い、pと5*pとは添え字の奇数・偶数が必ず異なることが証明できるのである。
一般に、添え字iが偶数である自然数pはp=3*i+5=3*(2*h)+5=6*(h+1)-1という形で表せ、
また、添え字iが奇数である自然数pはp=3*i+4=3*(2*h+1)+4=6*(h+1)+1という形で表すことが出来る。
ここで、添え字iが偶数である自然数pについては、5*p=(6-1)(6*(h+1)-1)=6(5(h+!)-1)+1となり、5*pにあたる添え字iは奇数であることが証明できた。
また、添え字iが奇数である自然数pについては、5*p=(6-1)(6*(h+1)+1)=6(5(h+!)-1)-1
となり、5*pにあたる添え字は偶数であることが証明できた。
以上により、pと5*pとは添え字の奇数・偶数が必ず異なることが証明できた。
よって、同じ系列の出発点にはp自身、別の系列の出発点には、5*pが使える。
しかし、pや5*pは対応する自然数の値であって添え字の値ではない。
ここで5*pの添え字sについて考えるP=3*i+5-(i%2)より
5*p=5(3*i+5-(i%2))=3*(5*i+7-2*(i%2))+5-(1-(i%2))
=3*(5*i+7-2*(i%2))+5-(s%2) (s%2)=1-(i%2)より
よって、s=5*i+7-2*(i%2)とおけば、5*p=3*s+5-(s%2)が成り立つ。
・・・という考えのもとこのプログラムをつくったのですが、考え方が根本的にまちがっているのでしょうか?
No.2
- 回答日時:
★『stdbool.h』では『_Bool』が予約語です。
・『bool』は予約語ではないのでエラーになったと思います。
また C99 に対応しているコンパイラなら stdbool.h はありますが未対応なら
ヘッダはないのでこれまたエラーなります。
どんなコンパイラを使っているのか分かりませんがこのことを頭の片隅に
置いておいて下さい。
>よりメモリを効率よく使いたいため…
↑
それなら『ユークリッドの互除法』で計算させる方法もあります。
http://www2.cc.niigata-u.ac.jp/~takeuchi/tbasic/ …
ネットで検索するといろいろ見つかります。
また『_Bool』型を使ってもメモリ効率が良くなるかは分かりません。
言語仕様では 0、1 を記憶できれば良いだけで _Bool 型が 1 ビットの領域で
管理されるかは分かりません。0、1 で管理させたいならビット演算すれば良い。
・下にサンプルを載せます。
サンプル:
// ビット調査
int GetBool( unsigned char bool[], int no )
{
return (bool[ no >> 3 ] & (1 << (no & 0x7))) ? 1 : 0;
}
// ビット・セット
void SetBool( unsigned char bool[], int no )
{
bool[ no >> 3 ] |= (1 << (no & 0x7));
}
// ビット・リセット
void ResetBool( unsigned char bool[], int no )
{
bool[ no >> 3 ] &= ~(1 << (no & 0x7));
}
使い方:
int main( void )
{
unsigned char BoolTable[ 100 ]; // 800 ビット管理可能
SetBool( BoolTable, 123 ); // 123 番目のビットをON
ResetBool( BoolTable, 456 ); // 456 番目のビットをOFF
if ( GetBool(BoolTable,753) ){ // 753 番目のビットを調査
// 753 番目のビットは ON
}
else{
// 753 番目のビットは OFF
}
return 0;
}
以上。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# 並列プログラミングのπ計算について 1 2022/07/16 22:30
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# C言語 3 2022/11/09 13:27
- C言語・C++・C# 質問です 下記のコードを分かりやすく解説お願いします 初心者です #include ‹stdio.h 3 2022/05/26 22:03
- C言語・C++・C# LU分解法のピボット選択機能実装について(C言語・gcc-9) 1 2022/07/22 15:20
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- その他(プログラミング・Web制作) 十進BASICでの再帰についての質問です。 2 2022/11/18 09:17
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
おすすめ情報