소수 판정 알고리즘
1. 1개의 수만 판정할 때 : O(n0.5)O(n0.5)
bool isPrime(int n){ if(n <= 1) return false; for(int i = 2; i <= sqrt(n); i++){ if(n % i == 0) return false; } return true; }
어떤 한 수가 소수인지 아닌지 알고 싶을때는 위와 같이 돌리면 된다. √n√n까지 검사하는 이유는 "2...√n√n까지 나눴을 때 n의 약수가 없다면 n은 소수다"이기 때문이다. 이 증명법은 "약수, 소인수분해 알고리즘" 포스트에서 증명한
자연수 n에 대해 n의 어떤 약수 x와 n/x에 대해 min(x, n/x) <= √n√n이다.
에서
2...√n√n까지 나눴을 때 n의 약수가 없다면 n은 소수다
를 유도할 수 있다. 증명 과정은 간단하다. "2 ... √n√n 중 n의 약수가 없다면 n은 소수다."의 대우는 "n이 소수가 아니라면(합성수라면) 2 ... √n√n 중 n의 약수가 존재한다" 인데, 이는 위에서 이미 보인 사실이다.
2. 에라토스테네스의 체 - 여러개의 수를 판정할 때 : O(nloglogn)O(nloglogn)
typedef long long ll; // isPrime[i] : i가 소수이면 true, 아니면 false. void find_prime_basic(vector<bool>& isPrime, ll max_value){ vector<bool> isPrime(max_value, true); isPrime[0] = isPrime[1] = false; for(ll i = 2; i<=max_value; i++){ if(isPrime[i]){ for(ll j = i*2; j<=max_value; j+= i){ isPrime[j] = false; } } } }
지워지는 숫자의 개수는
i가 2일 때 : 2, 4, 6, 8, ... 제거 -> n2n2개 숫자 지워짐(그만큼 연산함)
i가 3일 때 : 3, 6, 9, 12, ... 제거 -> n3n3개의 숫자가 지워짐(그만큼 연산함)
...
이렇게 반복된다.
소수의 역수의 합(Harmonic progressiong of the sum of primes)은 log(log(n))이다.
따라서 - n(12+13+15+...12+13+15+...) = n(loglogn)n(loglogn)이다.
따라서 시간복잡도는 O(nloglogn)O(nloglogn)이다.
typedef long long ll; // isPrime[i] : i가 소수이면 true, 아니면 false. void find_prime_improved(vector<bool>& isPrime, ll max_value){ vector<bool> isPrime(max_value, true); isPrime[0] = isPrime[1] = false; for(ll i = 2; i<=sqrt(max_value); i++){ if(isPrime[i]){ for(ll j = i*i; j<=max_value; j+= i){ isPrime[j] = false; } } } }
향상된 버전으로는 바로 위 코드와 같다. 이 경우, max_value보다 작은 소수를 구하기 때문에 기존 소수 판정과 동일하게 outer for loop는 √n√n까지 반복한다. max_value보다 작은 모든 수 x 중 2, ... , √x√x까지 나눴을 때 x의 약수가 없다면 x는 소수인 것으로 판정할 수 있는데, 모든 x에 대해 √x√x < √n√n이기 때문이다. (자명한 이유다)
또, inner for loop의 j를 2i가 아니라 i*i부터 시작해도 된다. 왜냐하면, 2, ... , i-1의 모든 배수 k는 이미 isPrime[k] = false로 처리되었기 때문이다. 조금 풀어 쓰자면, inner for loop j가 2i부터 시작되는 경우 2i, 3i, 4i, ... , (i-1)i, i*i, (i+1)i, ... 로 나아가는데 2i의 경우 2의 배수, 3i의 경우 3의 배수, ... (i-1)i는 (i-1)의 배수로 이미 지워진 상태이기 때문이다.
신경써야 할 것은 inner, outer for loop의 i, j가 범위를 초과하지 않도록 범위계산을 미리 해 둬야 하는 것이다.
'PS > Algorithm' 카테고리의 다른 글
행렬곱 알고리즘 (0) | 2022.06.21 |
---|---|
약수, 소인수분해, 최대공약수/최소공배수 알고리즘 (0) | 2022.06.21 |
2020/07/08 공부 - dynamic programming (0) | 2022.06.21 |
2020/07/07 공부 - dynamic programming (0) | 2022.06.21 |
2020/07/06 공부 - dynamic programming (0) | 2022.06.21 |