programing

C - 숫자가 소수인지 확인합니다.

projobs 2022. 8. 13. 12:02
반응형

C - 숫자가 소수인지 확인합니다.

정수를 취해서 부울을 반환하고 소수인지 아닌지를 나타내는 방법을 생각하려고 합니다만, 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는 잊어버려숫자를 주고 프라임인지 아닌지 확인해보라고 하자.어떻게 하는 거야?순서를 명확하게 적어, 코드로 변환하는 것에 대해 고민합니다.

알고리즘이 정해지면 프로그램 작성 방법을 알아내는 것이 훨씬 쉬워지고 다른 사람이 도움을 줄 수 있습니다.

edit: 게시한 C# 코드는 다음과 같습니다.

static bool IsPrime(int number) {
    for (int i = 2; i < number; i++) {
        if (number % i == 0 && i != number) return false;
    }
    return true;
}

이것은 현재로선 거의 유효한 C입니다.bool하고 C를 입력하지 않고 C를 입력하지 않고 C를 입력하지 않습니다.true ★★★★★★★★★★★★★★★★★」falseKristopher Johnson의 C99 stdbool이다.header).h 」 ) 。C99 환경에 액세스 할 수 없는 사람도 있기 때문에(단, 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(어느 정도)로 하다.★★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;
}

마지막으로, 원래의 알고리즘에 작은 버그가 있었습니다! ifnumber이 함수는 숫자가 음수이거나 0 또는 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;
}

이 방법이 소수인지 확인하는 가장 빠른 방법은 아니지만 작동하며 매우 간단합니다.우리는 당신의 코드를 거의 수정하지 않았습니다!

숫자가 소수인지 확인하기 위해 밀러/라빈 알고리즘을 사용하고 있습니다.

#include <stdlib.h>

typedef size_t positive_number; // also try __uint128_t

static inline positive_number multiplication_modulo(positive_number lhs, positive_number rhs, positive_number mod) {
    positive_number res = 0; // we avoid overflow in modular multiplication
    for (lhs %= mod, rhs %= mod; rhs; (rhs & 1) ? (res = (res + lhs) % mod) : 0, lhs = (lhs << 1) % mod, rhs >>= 1);
    return res; // <= (lhs * rhs) % mod
}

static int is_prime(positive_number n, int k) {
    positive_number a = 0, b, c, d, e, f, g; int h, i;
    if ((n == 1) == (n & 1)) return n == 2;
    if (n < 51529) // fast constexpr check for small primes (removable)
        return (n & 1) & ((n < 6) * 42 + 0x208A2882) >> n % 30 && (n < 49 || (n % 7 && n % 11 && n % 13 && n % 17 && n % 19 && n % 23 && n % 29 && n % 31 && n % 37 && (n < 1369 || (n % 41 && n % 43 && n % 47 && n % 53 && n % 59 && n % 61 && n % 67 && n % 71 && n % 73 && ( n < 6241 || (n % 79 && n % 83 && n % 89 && n % 97 && n % 101 && n % 103 && n % 107 && n % 109 && n % 113 && ( n < 16129 || (n % 127 && n % 131 && n % 137 && n % 139 && n % 149 && n % 151 && n % 157 && n % 163 && n % 167 && ( n < 29929 || (n % 173 && n % 179 && n % 181 && n % 191 && n % 193 && n % 197 && n % 199 && n % 211 && n % 223))))))))));
    for (b = c = n - 1, h = 0; !(b & 1); b >>= 1, ++h);
    for (; k--;) {
        for (g = 0; g < sizeof(positive_number); ((char*)&a)[g++] = rand()); // random number
        do for (d = e = 1 + a % c, f = n; (d %= f) && (f %= d););
        while (d > 1 && f > 1);
        for (d = f = 1; f <= b; f <<= 1);
        for (; f >>= 1; d = multiplication_modulo(d, d, n), f & b && (d = multiplication_modulo(e, d, n)));
        if (d == 1) continue;
        for (i = h; i-- && d != c; d = multiplication_modulo(d, d, n));
        if (d != c) return 0;
    }
    return 1;
}

이 테스트에서는 첫 번째 소수점을 인쇄합니다.

#include <stdio.h>
int main() {
    // C Fast Iterative Algorithm
    // The First 10,000 Primes
    for (int i = 0 ; i < 104730 ; ++i)
        if (is_prime(i, 20))
            printf("%d %c", i, (i+1) % 10 ? ' ' : '\n');

    if (is_prime(9223372036854775783UL, 12))
        if (is_prime(9223372036854775643UL, 12))
            if (!is_prime(3037000493ULL * 3037000453ULL, 12))
                printf("Done.\n");
}

해서 ㅇㅇㅇㅇㄹㄹㄹㄹㄹㄹㄹㄹㄹㄹㄹㄹㄹㄹㄹ에 넣을 수 요.primes.c+ execute : 파일 + 실행 : 파일 +

gcc -O3 -std=c99 -Wall -pedantic primes.c ; ./a.out ;

의 변형은 있습니다.log(n).
__uint128_t 타입은 128비트 정수 GCC 확장으로 사용할 수 있습니다.

Stephen Canon은 매우 잘 대답했습니다!

그렇지만

  • 알고리즘은 2와 3을 제외한 모든 소수가 6k ± 1이라는 것을 관찰함으로써 더욱 개선될 수 있다.
  • 이는 모든 정수가 일부 정수 k 및 i = -1, 0, 1, 2, 3, 또는 4에 대해 (6k + i)로 표현될 수 있기 때문입니다. 2 나누기(6k + 0), (6k + 2), (6k + 4), 3 나누기(6k + 3)입니다.
  • 따라서 보다 효율적인 방법은 n이 2 또는 3으로 나누어질 수 있는지 테스트한 다음 6k ± 1 ≤ n 형식의 모든 숫자를 확인하는 것입니다.
  • 이것은 모든 m에서 µ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; 
        }
    }
    

아무도 이것을 언급하지 않았다니 놀랍다.

에라토스테네 체 사용

세부사항:

  1. 기본적으로 비소수수는 1 이외의 다른 숫자와 그 자체로 나눌 수 있습니다.
  2. 따라서, 비소수는 소수의 곱이 될 것이다.

에라토스테네스의 체는 소수를 찾아 저장합니다.새 숫자의 prime가 체크되면 이전 모든 prime가 know prime 목록과 대조됩니다.

이유:

  1. 이 알고리즘/문제는 "민망하게 병렬"로 알려져 있습니다.
  2. 그것은 소수들의 모음을 만든다.
  3. 동적 프로그래밍 문제의 한 예입니다.
  4. 빠르다!

오버플로 버그 방지

unsigned i, number;
...
for (i=2; i*i<=number; i++) {  // Buggy
for (i=2; i*i<=number; i += 2) {  // Buggy
// or
for (i=5; i*i<=number; i+=6) { // Buggy

.number프라임과i*i이 타입의 최대치에 가깝습니다.

타입에 .signed, unsigned더 넓어집니다.

예:

let let 렛츠고UINT_MAX_SQRT최최 [ 65535unsigned32번입니다.

★★★★★★★★★★★★★★★★ for (i=2; i*i<=number; i++)는 '10년'이 발생했을 때 합니다.UINT_MAX_SQRT*UINT_MAX_SQRT <= number ★★★★★★★★★★★★★★★★★」numberprime 이며, 다음 반복에서는 곱셈 오버플로가 발생합니다.유형이 서명된 유형일 경우 오버플로는 UB가 됩니다.부호 없는 타입의 경우는, 그 자체는 UB가 아니지만, 논리는 무너져 버렸습니다.잘린 제품이 다음을 초과할 때까지 중단이 계속됩니다.number수 . 을 사용법 32비트 포함unsigned4,294,967,291명

ifsome_integer_type_MAX메르센 수상이었어요i*i<=number절대 사실이 아니야


버그를 하려면 , 「 」, 「 」를 고려해 주세요.number%i,number/i는 많은 1.1만 계산하면 지수와 나머지의 계산이 함께 이루어지기 때문에 1과 비교하여 두 가지 작업을 모두 수행하는 데 추가 비용이 들지 않습니다.

심플한 풀 레인지 솔루션:

bool IsPrime(unsigned number) {
    for(unsigned i = 2; i <= number/i; i++){
        if(number % i == 0){
            return false;
        }
    }
    return number >= 2;
}

이 질문을 읽은 후, 일부 답변은 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;
}

두 기능을 모두 실행하고 시간을 확인함으로써 이 기능이 매우 빠르다는 것을 알 수 있었습니다.두 개의 서로 다른 소수점을 갖는 두 가지 테스트를 살펴보겠습니다.

$ 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초입니다. 누군가가 않기 25를 하는 것이 .따라서 누군가가 이러한 기술을 사용하는 하드코드 프로그램을 만든다면 이득이 크지 않기 때문에 목록 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에 부여한 것은 의도적으로 해방시킨 것이 아닙니다.프로그램이 종료되면 메모리는 해방되기 때문에 시간을 벌기 위해서입니다.그러나 계산 후에도 코드를 계속 실행하려면 이 기능을 해제하는 것이 좋습니다.


보너스

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
  1. 작은 소수점 표를 만들고 입력된 숫자를 나누는지 확인합니다.
  2. 숫자가 1까지 생존한 경우 증가 기준으로 의사 원시성 검정을 시도합니다.예를 들어 Miller-Rabin 원시성 검정을 참조하십시오.
  3. 숫자가 2까지 생존한 경우 잘 알려진 한계보다 작으면 소수라고 결론을 내릴 수 있습니다.그렇지 않으면 답변은 "아마도 프라임"일 것입니다.Wiki 페이지에서 이러한 경계에 대한 몇 가지 값을 찾을 수 있습니다.

이 프로그램은 프라임리티 체크를 위해 단일 번호를 확인하는 데 매우 효율적입니다.

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부터 루트까지의 각 정수의 계수를 확인합니다.

계수가 0이면 소수가 아닙니다.

유사 코드:

bool IsPrime(int target)
{
  for (i = 2; i <= root(target); i++)
  {
    if ((target mod i) == 0)
    {
      return false;
    }
  }

  return true;
}

짝수(막대 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와 짝수의 처리는 포함되지 않습니다.이는 홀수 모듈로 짝수는 항상 0이 아닌 응답을 주기 때문에 이러한 테스트는 용장화되기 때문입니다.또는 홀수는 다른 홀수로는 균등하게 나누어지지만 짝수로는 나누어지지 않는다(E*E=>E, E*O=>E, O*E=>E 및 O*O=>O).

x86 아키텍처에서 분할/모듈러스는 비용이 많이 들지만 비용은 다릅니다(http://gmplib.org/~tege/x86-modulus.pdf 참조).반면에 곱셈은 꽤 싸다.

Eratostenes의 Sieve를 사용하면 "알려진" 소수 알고리즘에 비해 계산 속도가 상당히 빨라집니다.

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];
}

IsPrime Number(10000000)는 21s 758ms가 소요됩니다.

메모: 값은 하드웨어 사양에 따라 다를 수 있습니다.

언급URL : https://stackoverflow.com/questions/1538644/c-determine-if-a-number-is-prime

반응형