私は、整数を取り、ブール値を返すメソッドを考え出そうとしています。これは、数値が素数であるかどうかを示しており、Cをあまり知りません。誰かが私にいくつかのポインタを与えてくれますか?
基本的に、私はこれをC#で次のように行います:
static bool IsPrime(int number)
{
for (int i = 2; i < number; i++)
{
if (number % i == 0 && i != number)
return false;
}
return true;
}
さて、Cのことは忘れてください。数字を与えて、それが素数であるかどうかを判断するように頼んだとします。どうやってやるの?ステップを明確に書き留めてください。それからそれらをコードに変換することを心配してください。
アルゴリズムを決定したら、プログラムの作成方法を理解し、他の人がそれを支援するのがはるかに簡単になります。
編集:投稿したC#コードは次のとおりです。
static bool IsPrime(int number) {
for (int i = 2; i < number; i++) {
if (number % i == 0 && i != number) return false;
}
return true;
}
これは、非常に近いそのままのCです。 Cにはbool
型もtrue
もfalse
もありませんので、少し変更する必要があります(編集:Kristopher JohnsonはC99がstdbool.hヘッダーを追加したことを正しく指摘しています)。一部の人々はC99環境にアクセスできないため(ただし、使用する必要があります!)、非常に小さな変更を加えましょう。
int IsPrime(int number) {
int i;
for (i=2; i<number; i++) {
if (number % i == 0 && i != number) return 0;
}
return 1;
}
これは、必要なことを行う完全に有効なCプログラムです。あまり手間をかけずに少し改善できます。まず、i
は常にnumber
よりも小さいため、i != number
が常に成功することを確認してください。私たちはそれを取り除くことができます。
また、実際に除数をnumber - 1
まで試してみる必要はありません。 sqrt(number)に達したらチェックを停止できます。 sqrt
は浮動小数点演算であり、微妙な山をもたらすため、実際にはsqrt(number)
を計算しません。代わりに、i*i <= number
を確認するだけです。
int IsPrime(int number) {
int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
ただし、最後の1つです。元のアルゴリズムに小さなバグがありました! number
が負、ゼロ、または1の場合、この関数は数が素数であると主張します。あなたはおそらくそれを適切に処理したいでしょうし、あなたは正の値だけを気にする可能性が高いので、number
を無署名にしたいかもしれません:
int IsPrime(unsigned int number) {
if (number <= 1) return 0; // zero and one are not prime
unsigned int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
これは間違いなく、数値が素数であるかどうかを確認する最速の方法ではありませんが、機能し、非常に簡単です。コードを変更する必要はほとんどありませんでした!
誰もこれに言及していないことに驚いています。
エラトステネスのふるい を使用します
詳細:
エラトステネスのふるいは素数を見つけて保存します。新しい数の素数がチェックされると、以前のすべての素数が素数リストと照合されます。
理由:
スティーブン・キャノンは非常によく答えました!
しかし
これは、最大√nまでのすべてのテストの3倍の速度です。
int IsPrime(unsigned int number) {
if (number <= 3 && number > 1)
return 1; // as 2 and 3 are prime
else if (number%2==0 || number%3==0)
return 0; // check if number is divisible by 2 or 3
else {
unsigned int i;
for (i=5; i*i<=number; i+=6) {
if (number % i == 0 || number%(i + 2) == 0)
return 0;
}
return 1;
}
}
このプログラムは、素数チェックのために単一の数をチェックするのに非常に効率的です。
bool check(int n){
if (n <= 3) {
return n > 1;
}
if (n % 2 == 0 || n % 3 == 0) {
return false;
}
int sq=sqrt(n); //include math.h or use i*i<n in for loop
for (int i = 5; i<=sq; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
2からチェックする数値のルートまでの各整数のモジュラスを確認します。
モジュラスがゼロに等しい場合、素数ではありません。
擬似コード:
bool IsPrime(int target)
{
for (i = 2; i <= root(target); i++)
{
if ((target mod i) == 0)
{
return false;
}
}
return true;
}
この質問を読んだ後、2 * 3 = 6の倍数でループを実行することでいくつかの答えが最適化を提供したという事実に興味をそそられました。
そのため、同じ考え方で2 * 3 * 5 = 30の倍数で新しい関数を作成します。
int check235(unsigned long n)
{
unsigned long sq, i;
if(n<=3||n==5)
return n>1;
if(n%2==0 || n%3==0 || n%5==0)
return 0;
if(n<=30)
return checkprime(n); /* use another simplified function */
sq=ceil(sqrt(n));
for(i=7; i<=sq; i+=30)
if (n%i==0 || n%(i+4)==0 || n%(i+6)==0 || n%(i+10)==0 || n%(i+12)==0
|| n%(i+16)==0 || n%(i+22)==0 || n%(i+24)==0)
return 0;
return 1;
}
両方の機能を実行し、時間をチェックすることにより、この機能が本当に高速であると言えます。 2つの異なる素数を持つ2つのテストを見てみましょう。
$ time ./testprimebool.x 18446744069414584321 0
f(2,3)
Yes, its prime.
real 0m14.090s
user 0m14.096s
sys 0m0.000s
$ time ./testprimebool.x 18446744069414584321 1
f(2,3,5)
Yes, its prime.
real 0m9.961s
user 0m9.964s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 0
f(2,3)
Yes, its prime.
real 0m13.990s
user 0m13.996s
sys 0m0.004s
$ time ./testprimebool.x 18446744065119617029 1
f(2,3,5)
Yes, its prime.
real 0m10.077s
user 0m10.068s
sys 0m0.004s
だから私は、一般化すると誰かがあまりに多く得るのだろうかと思った。私は最初に包囲を行い、指定された原始素数のリストをきれいにする関数を考え出し、次にこのリストを使ってより大きな素数を計算します。
int checkn(unsigned long n, unsigned long *p, unsigned long t)
{
unsigned long sq, i, j, qt=1, rt=0;
unsigned long *q, *r;
if(n<2)
return 0;
for(i=0; i<t; i++)
{
if(n%p[i]==0)
return 0;
qt*=p[i];
}
qt--;
if(n<=qt)
return checkprime(n); /* use another simplified function */
if((q=calloc(qt, sizeof(unsigned long)))==NULL)
{
perror("q=calloc()");
exit(1);
}
for(i=0; i<t; i++)
for(j=p[i]-2; j<qt; j+=p[i])
q[j]=1;
for(j=0; j<qt; j++)
if(q[j])
rt++;
rt=qt-rt;
if((r=malloc(sizeof(unsigned long)*rt))==NULL)
{
perror("r=malloc()");
exit(1);
}
i=0;
for(j=0; j<qt; j++)
if(!q[j])
r[i++]=j+1;
free(q);
sq=ceil(sqrt(n));
for(i=1; i<=sq; i+=qt+1)
{
if(i!=1 && n%i==0)
return 0;
for(j=0; j<rt; j++)
if(n%(i+r[j])==0)
return 0;
}
return 1;
}
私はコードを最適化しなかったと思いますが、それは公平です。さて、テスト。動的メモリが非常に多いため、リスト2 3 5はハードコーディングされた2 3 5よりも少し遅いと予想しました。しかし、あなたが怒鳴るのを見ることができるので、それは大丈夫でした。その後、時間はどんどん小さくなり、最高のリストは次のようになりました。
2 3 5 7 11 13 17 19
8.6秒で。そのため、誰かがそのような手法を使用するハードコードされたプログラムを作成する場合、ゲインはそれほど大きくないため、リスト2 3および5を使用することをお勧めします。ただし、コードを作成する場合は、このリストでも構いません。問題は、ループなしですべてのケースを述べることができない、またはコードが非常に大きくなることです(1658879 ORs
、つまりそれぞれの内部if
に||
があります)。次のリスト:
2 3 5 7 11 13 17 19 23
時間が大きくなり始め、13秒でした。ここでテスト全体:
$ time ./testprimebool.x 18446744065119617029 2 3 5
f(2,3,5)
Yes, its prime.
real 0m12.668s
user 0m12.680s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7
f(2,3,5,7)
Yes, its prime.
real 0m10.889s
user 0m10.900s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11
f(2,3,5,7,11)
Yes, its prime.
real 0m10.021s
user 0m10.028s
sys 0m0.000s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13
f(2,3,5,7,11,13)
Yes, its prime.
real 0m9.351s
user 0m9.356s
sys 0m0.004s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17
f(2,3,5,7,11,13,17)
Yes, its prime.
real 0m8.802s
user 0m8.800s
sys 0m0.008s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19
f(2,3,5,7,11,13,17,19)
Yes, its prime.
real 0m8.614s
user 0m8.564s
sys 0m0.052s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23
f(2,3,5,7,11,13,17,19,23)
Yes, its prime.
real 0m13.013s
user 0m12.520s
sys 0m0.504s
$ time ./testprimebool.x 18446744065119617029 2 3 5 7 11 13 17 19 23 29
f(2,3,5,7,11,13,17,19,23,29)
q=calloc(): Cannot allocate memory
PS。プログラムを終了するとすぐにメモリが解放されるため、OSにこのタスクを与えて、意図的にfree(r)を解放しませんでした。ただし、計算後にコードを実行し続ける場合は、解放するのが賢明です。
ボーナス
int check2357(unsigned long n)
{
unsigned long sq, i;
if(n<=3||n==5||n==7)
return n>1;
if(n%2==0 || n%3==0 || n%5==0 || n%7==0)
return 0;
if(n<=210)
return checkprime(n); /* use another simplified function */
sq=ceil(sqrt(n));
for(i=11; i<=sq; i+=210)
{
if(n%i==0 || n%(i+2)==0 || n%(i+6)==0 || n%(i+8)==0 || n%(i+12)==0 ||
n%(i+18)==0 || n%(i+20)==0 || n%(i+26)==0 || n%(i+30)==0 || n%(i+32)==0 ||
n%(i+36)==0 || n%(i+42)==0 || n%(i+48)==0 || n%(i+50)==0 || n%(i+56)==0 ||
n%(i+60)==0 || n%(i+62)==0 || n%(i+68)==0 || n%(i+72)==0 || n%(i+78)==0 ||
n%(i+86)==0 || n%(i+90)==0 || n%(i+92)==0 || n%(i+96)==0 || n%(i+98)==0 ||
n%(i+102)==0 || n%(i+110)==0 || n%(i+116)==0 || n%(i+120)==0 || n%(i+126)==0 ||
n%(i+128)==0 || n%(i+132)==0 || n%(i+138)==0 || n%(i+140)==0 || n%(i+146)==0 ||
n%(i+152)==0 || n%(i+156)==0 || n%(i+158)==0 || n%(i+162)==0 || n%(i+168)==0 ||
n%(i+170)==0 || n%(i+176)==0 || n%(i+180)==0 || n%(i+182)==0 || n%(i+186)==0 ||
n%(i+188)==0 || n%(i+198)==0)
return 0;
}
return 1;
}
時間:
$ time ./testprimebool.x 18446744065119617029 7
h(2,3,5,7)
Yes, its prime.
real 0m9.123s
user 0m9.132s
sys 0m0.000s
偶数(バー2)が素数にならないことを付け加えます。これにより、forループの前に別の条件が発生します。したがって、終了コードは次のようになります。
int IsPrime(unsigned int number) {
if (number <= 1) return 0; // zero and one are not prime
if ((number > 2) && ((number % 2) == 0)) return 0; //no even number is prime number (bar 2)
unsigned int i;
for (i=2; i*i<=number; i++) {
if (number % i == 0) return 0;
}
return 1;
}
int is_prime(int val)
{
int div,square;
if (val==2) return TRUE; /* 2 is prime */
if ((val&1)==0) return FALSE; /* any other even number is not */
div=3;
square=9; /* 3*3 */
while (square<val)
{
if (val % div == 0) return FALSE; /* evenly divisible */
div+=2;
square=div*div;
}
if (square==val) return FALSE;
return TRUE;
}
2と偶数の処理は、奇数を奇数で割ったもののみを処理するメインループから除外されます。これは、偶数を法とする奇数が常にゼロ以外の答えを与えるため、これらのテストが冗長になるためです。または、別の言い方をすれば、奇数は別の奇数で均等に割り切れますが、never偶数(E * E => E、E * O => E、O * E => EおよびO * O => O)。
除算/モジュラスは、x86アーキテクチャでは実際にコストがかかりますが、コストはどの程度異なります( http://gmplib.org/~tege/x86-timing.pdf を参照)。一方、乗算は非常に安価です。
Sieve of Eratosthenesを使用すると、「既知の」素数アルゴリズムと比較して計算が非常に高速になります。
Wikiの擬似コード( https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes )を使用することで、C#で解決策を得ることができます。
public bool IsPrimeNumber(int val) {
// Using Sieve of Eratosthenes.
if (val < 2)
{
return false;
}
// Reserve place for val + 1 and set with true.
var mark = new bool[val + 1];
for(var i = 2; i <= val; i++)
{
mark[i] = true;
}
// Iterate from 2 ... sqrt(val).
for (var i = 2; i <= Math.Sqrt(val); i++)
{
if (mark[i])
{
// Cross out every i-th number in the places after i (all the multiples of i).
for (var j = (i * i); j <= val; j += i)
{
mark[j] = false;
}
}
}
return mark[val];
}
IsPrimeNumber(1000000000)は21秒758ミリ秒かかります。
NOTE:値はハードウェアの仕様によって異なる場合があります。
for (i = 2; i <= number/i; i++)
を使用して、反復を制限します。
number%i
、number/i
は、商と剰余の計算が一緒に行われるため、多くのコンパイラーで効率的です。したがって、両方を実行するのに追加コストは発生しません。
非常に簡単な解決策:
bool IsPrime(unsigned number) {
for(unsigned i = 2; i <= number/i; i++){
if(number % i == 0){
return false;
}
}
return number >= 2;
}