2017-12-30 54 views
-2

C에서 거대한 숫자의 가장 큰 소수 요소를 찾으려고합니다. 100 또는 10000과 같은 작은 숫자는 제대로 작동하지만 실패합니다. 실패로 인해 계속 유지됩니다. 실행 및 실행 내 core2duo 및 i5에서 수십 분 동안 매우 큰 target 숫자입니다 (대상 번호에 대한 코드 참조). 내 알고리즘이 맞습니까?C 코드가 영원히 계속 실행됩니다. *

저는 C가 생소하고 큰 숫자로 인해 어려움을 겪습니다. 내가 뭘 원하는 건 정정이나 지침이 아닙니다 bignum 바인딩 및 물건 (아직 나는 시도했지만 꽤 확실한)와 파이썬을 사용하여 할 수있는 솔루션이 아니지만 아니면 내가 몇 가지 작은 실수를했을 수도 있습니다 실현 너무 피곤, 어쨌든 여기에 내가 쓴 코드 :

#include <stdio.h> 
// To find largest prime factor of target 
int is_prime(unsigned long long int num); 

long int main(void) { 
    unsigned long long int target = 600851475143; 
    unsigned long long int current_factor = 1; 
    register unsigned long long int i = 2; 
    while (i < target) { 
     if ((target % i) == 0 && is_prime(i) && (i > current_factor)) { //verify i as a prime factor and greater than last factor 
      current_factor = i; 
     } 
     i++; 
    } 
    printf("The greates is: %llu \n",current_factor); 
return(0); 
} 


int is_prime (unsigned long long int num) { //if num is prime 1 else 0 
    unsigned long long int z = 2; 
    while (num > z && z !=num) { 
     if ((num % z) == 0) {return 0;} 
     z++; 
    } 

return 1; 
} 
+1

이제까지는 현재 위치를 확인하기 위해 디버거에서 들렸다? 나는 컨텍스트없이 코드를 읽는 것으로 가정 할 것이다. is_prime 오버 플로우로 인해 루프가 멈출 수도있다. – X39

+0

아니,하지만 printf를 추가하여 코드가 제대로 작동하는지 알 수있다. 그러나 얼마나 오래? –

+1

'is_prime'을 반으로 최적화 : 짝수를 테스트하고,'z = 3'을 설정하고 루프의 반복마다 2 씩 증가시킵니다. –

답변

4

6 천억 개의 반복은 아무런 사소한 시간도 걸릴 것입니다. 이 점을 상당히 줄여야합니다.

는 여기에 힌트 : 임의의 정수 값 x을 감안할 때, 우리는 y가 요소임을 발견 할 경우, 우리는 암시 x/y도 요소임을 발견했습니다. 즉, 요소는 항상 쌍으로됩니다. 따라서 중복 작업을 수행하기 전에 얼마나 반복해야하는지에 한계가 있습니다.

그 한도는 무엇입니까? 자, yx/y보다 커질 교차점은 무엇입니까?

외부 루프에이 최적화를 적용하면 코드 실행 시간이 is_prime 함수에 의해 제한된다는 것을 알 수 있습니다. 하지만 물론 비슷한 기술을 적용 할 수도 있습니다.

+0

당신이 나에게주는 힌트를주는 해결책이 맞다면 어쩌면 나는 SE를 떠나 자신이 이해할 수있을 것입니다. 그래서 당신은 확신합니까? –

+1

@UbdusSamad - 예. "독자를위한 운동으로 남긴"비트는 그 한계가 무엇인지 파악하는 것입니다. –

+0

그래, 고마워. 내가 작업하고 2 시간 동안 나의 첫 번째 접근법도 실행 해 보도록하겠습니다. 다시 한 번 감사드립니다! –

4

수의 제곱근 때까지 반복함으로써, 우리는 모든 요인이다 얻을 수 있습니다 (factorN/factorfactor<=sqrt(N)을).. 이 작은 아이디어 하에서 해결책이 존재합니다. 확인하는 요소가 sqrt(N)보다 작 으면 sqrt(N)보다 큰 해당 요소가 있습니다. 따라서 sqrt(N)까지만 확인하면 남은 요소를 얻을 수 있습니다.

여기에서는 프라임 찾기 알고리즘을 명시 적으로 사용할 필요가 없습니다. 인수 분해 논리 자체는 목표가 소수인지 여부를 추론합니다. 그래서 남은 것은 pairwise 요인을 확인하는 것입니다.

unsigned long long ans ; 
for(unsigned long long i = 2; i<=target/i; i++) 
    while(target % i == 0){ 
     ans = i; 
     target/=i; 
    } 

if(target > 1) ans = target; // that means target is a prime. 
//print ans 

편집 : 점은 (chux) 추가 할 - 이전 코드에서 i*i을 우리가 i<=target/i를 사용하면 피할 수 있습니다 오버 플로우가 발생할 수 있습니다. 반올림 오류가 발생하기 쉬운 정수 연산 더블 수학의 혼합 -

또 다른 선택은 현명한 선택이 있기 때문에하지

다음
unsigned long long sqaure_root = isqrt(target); 
for(unsigned long long i = 2; i<=square_root; i++){ 
... 
} 

sqrt의 사용에보다주의해야하는 것 . 대답은 6857 될 것 지정한 대상

.

+0

참고 : 'i * i '은 오버 플로우의 대상이됩니다. 'for (unsigned long long i = 2; i <= target/i; i ++)'는 그렇지 않습니다. 'target/i'의 비용은 종종'target % i'을보고'target % i'과'target/i'를 함께 계산하는 좋은 컴파일로 최소입니다. – chux

+0

@chux :'i <= sqrt (target)'또는'i <= target/i'가 더 나은 선택일까요? – coderredoc

+0

'sqrt (target)'은 좋지 않습니다. 정수 연산과 '이중'연산의 혼합은 반올림 오류가 발생하기 쉽습니다. 'sqrt (target)'을 다시 계산하면 각 루프도 약합니다. 코드는 루프 이전에 이전의 'unsigned long long sqtarget = isqrt (target);'을 수행 할 수 있습니다. – chux

0

is_prime은 다른 소수에 대해서만 num을 테스트해야한다는 점에서 닭고기 및 달걀 문제가 있습니다. 따라서 3의 배수이기 때문에 9를 검사 할 필요가 없습니다.

은 소수 배열을 유지 관리 할 수 ​​있으며 새 num이 테스트 될 때마다 배열에 추가 할 수 있습니다. num isr 배열의 각 소수에 대해 테스트되고 배열의 소수에서 나눌 수없는 경우 자체가 소수이며 배열에 추가됩니다. Aray는 malloc'd와 relloc'd가 될 필요가 있습니다. 목표가있는 소수의 수를 계산할 수있는 형식이 없다면 (나는 그러한 수식이 존재하지 않는다고 생각합니다).

EDIT : 대상을 테스트 할 소수의 수 600,851,475,143은 약 7,500,000,000이고 테이블의 메모리가 부족할 수 있습니다.

다음과 같은 접근 방식은 적응 될 수

  • 는 상기 무력을 사용하는

  • 상기 소수위한 unsigned long long int를 사용 UINT_max

  • 의 소수까지 unsiged int를 사용 특정 메모리 소비.

UINT_MAX는 4294967295로 정의하고 주위 천억까지 소수를 포함 할 7.5 * 4 = 30GB

참조에게 또한 The Prime Pages 비용 것입니다.

+0

... 이유를 밝히지 않은 downvote는 금지되어야합니다 ... 접근 방식에 문제가없는 것을 볼 수 없습니다. 설명 해주십시오. –

+0

그 배열은 메모리, 너무 많은 메모리를 먹을 것입니다! –

+1

'is_prime'을 최적화하는 것은 불필요합니다. 소수의 시간에만 호출되기 때문입니다. 아니면 외부 루프가 최적화 될 때까지 최적화 할 필요가 없습니다. –

0

아무 잘못 없다, 그냥 예를 들어, 최적화를 필요로 : 당신은 내가 할 솔루션을하지 않기 때문에

int is_prime(unsigned long long int num) { 
    if (num == 2) { 
     return (1);    /* Special case */ 
    } 
    if (num % 2 == 0 || num <= 1) { 
     return (0); 
    } 
    unsigned long long int z = 3; /* We skipped the all even numbers */ 
    while (z < num) {    /* Do a single test instead of your redundant ones */ 
     if ((num % z) == 0) { 
      return 0; 
     } 
     z += 2;      /* Here we go twice as fast */ 
    } 
return 1; 
} 

이 또한 큰 다른 문제는 당신이 방법을 찾을 수있다 (Z < NUM) 동안 만 그것을 최적화하고, 유사하게 첫 번째 기능을 스스로 알아보십시오.

EDIT : 다른 누군가가 나보다 50 초 앞선 primes 솔루션의 배열 목록을 게시했지만 당신이 초보자이고 조작이 쉬운 배열을 처음부터 쉽게 사용할 수 있기 때문에 쉬운 솔루션을 제공하기로했습니다. 포인터와 물건을 배우십시오).

+0

downvotes 주셔서 감사 ... 나는 그것이 아닌 최적의 솔루션 때문이라고 생각하지만 계속 OP에서 그는 그가 _learning_ C라고 이것은 쉽게 이해할 수있다. –

+0

배열과 포인터를 올바르게 처리 할 수 ​​있습니다! –

+0

오, 좋아, 그럼, 당신은 당신이 동적으로 할당 된 긴 int ints의 배열을 찾고있는 동안 찾을 소수를 추가해야합니다, 당신은 얼마나 많은 RAM을 가지고 있는지 모르지만 그것에 대한 저가형 노트북과 함께 몇 가지 문제가 발생할 수 있습니다 예...(실제로는 시스템의 sizeof (long long int)에 따라 다르다. 작은 PC에서는 16이 아니기 때문에 실제 문제는 없다.) –

2

코드 2 큰 문제

while (i < target) 루프가 매우 비효율적이다
  1. 있습니다. 요인을 발견하면 targettarget = target/i;으로 줄일 수 있습니다. 또한, 인자 i은 여러 번 발생할 수 있습니다. 수정되지 않음.

  2. is_prime(n)은 매우 비효율적이다. 그것의 while (num > z && z !=num)n 시간 루프 수 있습니다. 여기에서도 몫을 사용하여 반복을 sqrt(n) 번으로 제한합니다.

    int is_prime (unsigned long long int num) { 
        unsigned long long int z = 2; 
        while (z <= num/z) { 
        if ((num % z) == 0) return 0; 
        z++; 
        } 
        return num > 1; 
    }