당신은 정말 소수를 찾을 수 있습니까?
기사 디렉토리
소수의 정의는 매우 간단 보이는 숫자가 1 씩 나누어 그 자체,이 숫자가 소수 인 경우에만 경우.
Lianqi
정말 두려워하지 않는 많은 사람들이 할 수있는 주요 관련 쓰기 효율적인 알고리즘을 오전, 소수의 정의는 간단하다 생각하지 마십시오. 예를 들어, 다음과 같은 기능을 쓸 수 :
// 返回区间 [2, n) 中有几个素数
int countPrimes(int n)
// 比如 countPrimes(10) 返回 4
// 因为 2,3,5,7 是素数
당신은 어떻게이 기능을 쓸 것인가? 나는 우리가 작성해야 생각 :
int countPrimes(int n) {
int count = 0;
for (int i = 2; i < n; i++)
if (isPrim(i)) count++;
return count;
}
// 判断整数 n 是否是素数
boolean isPrime(int n) {
for (int i = 2; i < n; i++)
if (n % i == 0)
// 有其他整除因子
return false;
return true;
}
시간의 이러한 단어를 작성 복잡성 , 큰 문제. 첫째, 당신은 isPrime 아이디어가 충분히 효율적으로하지 않습니다 도움이되는 기능을 사용;
그리고 당신은 isPrime 기능을 사용하는 경우에도, 쓰기 알고리즘은 계산의 중복에 존재한다.
보호기
숫자 알고리즘을 작성하는 방법, 소수가 아닌 경우를 판별하려면 단순히 아래, 선착순. 그냥 약간의 조건을 순환에 대한 isPrim 코드 위를 수정 :
boolean isPrime(int n) {
for (int i = 2; i * i <= n; i++)
...
}
즉, 내가 n으로 통과 할 필요가 없습니다, 단지 수 있습니다 SQRT (n)을해야합니다. 우리는 예를 들어 줄 이유는, N = 12을 가정합니다.
12 = 2 × 6
12 = 3 × 4
12 = sqrt(12) × sqrt(12)
12 = 4 × 3
12 = 6 × 2
두 개의 제의 생성물이 차례로 SQRT (N)의 임계점을 반전하는 것을 알 수있다.
[2 SQRT (N)] 나눌 요인이 구간 내에서 발견되는 경우 즉, 우리가 직접 그 N 때문에 간격으로 소수 인 결론을 내릴 수있다 [SQRT (N), N]으로 발견되지 나눌 요인.
이제 시간은 isPrime의 복잡성의 함수는 O (SQRT (N))로 감소하지만, 우리는 countPrimes도 사용되는 등, 때문에, 더 많은 독자가 SQRT (N)의 의미를 이해하는 것이 단지 희망보다,이 기능을 필요가 없습니다 실제로 기능을 실현.
대승
효율적인 구현 countPrimes
이 문제에 대한 효과적인 솔루션의 핵심 개발과 포워드 안티 상기 종래의 사고 :
시작 2에서, 우리는이 다음 소수라는 것을 알고, 먼저 2 × 2 = 4, 3 × 2 = 6, 4 × 2 = 8 ... 하지 그것은 소수가 될 수 있습니다.
그리고 우리는 또한 3 소수 후 3 × 2 = 6, 3 × 발견 ... = 9 3, 3 × 4 = 12 도 소수가 될 수 없습니다.
당신이 그것을 배제의 논리의 약간을 이해한다면, 여기를 참조하십시오? 우리는 코드의 첫 번째 버전 보면 :
int countPrimes(int n) {
boolean[] isPrim = new boolean[n];
// 将数组都初始化为 true
Arrays.fill(isPrim, true);
for (int i = 2; i < n; i++)
if (isPrim[i])
// i 的倍数不可能是素数了
for (int j = 2 * i; j < n; j += i)
isPrim[j] = false;
int count = 0;
for (int i = 2; i < n; i++)
if (isPrim[i]) count++;
return count;
}
위의 코드는 당신이 이해 할 수있는 경우에, 당신은 전체 아이디어를 지배했지만, 최적화 할 수있는 두 개의 작은 영역이있다.
급증 기간
첫째, 그냥 숫자 때문에에 대한 루프 통과하기 만하면 대칭 요소로, 소수의 isPrime 기능 여부를 판단 해낸다 [2,sqrt(n)]
충분히. 여기에 루프에 대한 우리의 외부, 유사합니다 只需要遍历到 sqrt(n)
:
for (int i = 2; i * i < n; i++)
if (isPrim[i])
...
또한, 거의 최적화 될 수있는 루프 내측 통지하지 않는다. 우리의 이전 방법은 다음과 같습니다
for (int j = 2 * i; j < n; j += i)
isPrim[j] = false;
차례로 이것은 정수 여러 i 플래그가 false의 수, 중복 계산이 남아있다.
예를 들어, N = 25, I 알고리즘 마크에게 = 4 × 4 (2) = 8.4 × 3 = 12 번호 등이지만 두 수치는 I = 2 및 i가 2 × 4, 3 × 4 마크 = 3왔다 가.
우리는 2에서 내가 시작 * 대신, 조금을 최적화하고 내가 광장 탐색에서 시작 J하도록 할 수 있습니다 :
for (int j = i * i; j < n; j += i)
isPrim[j] = false;
따라서, 알고리즘의 효율적인 구현에 대한 주요 계수는, 사실,이 알고리즘은 에라토스테네스의 체라는 이름을 가지고 있습니다. 전체 최종 코드를보고 :
int countPrimes(int n) {
boolean[] isPrim = new boolean[n];
Arrays.fill(isPrim, true);
for (int i = 2; i * i < n; i++)
if (isPrim[i])
for (int j = i * i; j < n; j += i)
isPrim[j] = false;
int count = 0;
for (int i = 2; i < n; i++)
if (isPrim[i]) count++;
return count;
}
: 알고리즘의 시간 복잡도 분명 피연산자 있어야하는 대해 두 개의 중첩 루프 시간 더 어렵게 생각된다
... N- / N-2 + / + 3 N- / N- + 7분의 5 + ... = N- × ( 1/2 + 1/3 + 1/5 + 1 / 7 ...)
괄호는 상호 소수이다. 최종 결과는
, 관심있는 독자는 입증 알고리즘의 시간 복잡도를 조사 할 수있다.