오늘 배운 것
- 에라토스테네스의 체
소수 구하기
알고리즘 문제 중 특정 범위의 소수를 구하라는 문제들이 많이 있다.
이 문제를 푸는 가장 간단한 방법은 범위의 모든 수가 소수인지 아닌지 판별하면 된다.
특정 수 n이 소수인지 아닌지 판별하는 방법으로는 n이 2부터 n-1까지에서 나눠 떨어지는 것이 있는지 보는 것이다.
for(int j=2;j<n;j++){
if(n%j==0){
check = true; // 소수임
break;
}
}
이렇게 한다면 시간 복잡도는 O(N)이다.
이 방법보다 조금 더 빠르게 할 수 있는 방법은 n이 2부터 sqrt(N) = 루트 N까지에서 나눠 떨어지는 것이 있는지 보는 것이다.
for(int j=2;j<=sqrt(n);j++){
if(n%j==0){
check = true; // 소수임
break;
}
}
이렇게 한다면 시간 복잡도는 위의 방법보다 조금 빠르겠지만 여전히 O(N)이다.
하지만 n개의 수에 대해서 O(N)의 시간 복잡도라면 특정 범위에서 소수를 구하는 시간 복잡도는 O(N^2)가 된다.
따라서 시간 초과가 되는 문제들이 많이 있다.
이럴 때 사용하는 것이 에라토스테네스의 체 방법이다.
원리
- 구하고자 하는 소수의 범위만큼 1차원 배열 생성한다.
- 1은 소수가 아니니 제거한다.
- 2부터 시작해 현재 선택된 숫자의 배수에 해당하는 수를 배열에서 다 지운다.
- 3번을 진행할 때 처음 선택된 숫자 (2, ..)는 지우지 않는다.
- 다음 선택하는 수는 3~4를 진행한 후 지워지지 않은 수이다.
- 배열 끝까지 3~5을 진행한 후 배열에 남은 모든 수를 출력한다.
Ex
에라토스테네스의 체 방법을 사용해 1~10까지에서 소수를 구해보자.
제거한 숫자는 취소선 으로 표시하고 선택한 수는 글자색 으로 표시할 것이다.
1단계
1 2 3 4 5 6 7 8 9 10
2단계
1 2 3 4 5 6 7 8 9 10
3~4단계
1 2 3 4 5 6 7 8 9 10
5단계
1 2 3 4 5 6 7 8 9 10
6단계 (위의 것 반복)
1 2 3 4 5 6 7 8 9 10
시간 복잡도
에라토스테네스의 체 구현하려면 이중 for문 사용해서 시간 복잡도 O(N^2) 이라고 생각할 수 있다.
실제 시간 복잡도는 최적화 정도에 따라 다르겠지만 일반적으로 O(Nlog(logN)) 이다.
그 이유는 배수를 삭제하는 연산 때문이다.
실제 구현에서 바깥쪽 for문을 생략하는 경우 빈번히 발생하기 때문이다.
구현 코드 예시
#include <stdio.h>
int number = 100000;
int a[1000001];
void primeNumberSieve() {
// 1. 배열을 생성하여 초기화한다.
for(int i=2;i<=number;i++) {
a[i] = i;
}
// 2. 2부터 시작해서 특정 수의 배수에 해당하는 수를 모두 지운다.
// (지울 때 자기자신은 지우지 않고, 이미 지워진 수는 건너뛴다.)
for(int i=2;i<=number;i++) {
if(a[i]==0) continue; // 이미 지워진 수라면 건너뛰기
// 이미 지워진 숫자가 아니라면, 그 배수부터 출발하여, 가능한 모든 숫자 지우기
for(int j=2*i;j<=number; j+=i) {
a[j] = 0;
}
}
// 3. 2부터 시작하여 남아있는 수를 모두 출력한다.
for(int i=2;i<=number;i++) {
if(a[i]!=0) printf("%d ", a[i]);
}
}
int main(void) {
primeNumberSieve();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main() {
// 1. 13까지 수 중 소수 찾기
int num = 13;
vector<bool> v(num + 1, true);
// 2. 소수 판별
for (int i = 2; i * i <= num; i++) {
if (v[i]) {
for (int k = i * i; k <= num; k += i) {
v[k] = false;
}
}
}
// 3. 소수 확인
cout << "소수 : ";
for (int i = 2; i < v.size(); i++) {
if(v[i]) cout << i << " ";
}
cout << "\\n";
return 0;
}
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n = 100;
vector<bool> v(n + 1, 1);
for (int i = 2; i * i <= n; i++) {
if (v[i]) {
for (int k = i * i; k <= n; k += i) {
v[k] = 0;
}
}
}
for (int i = 2; i < v.size(); i++) {
if(v[i]) cout << i << " ";
}
}
'TIL' 카테고리의 다른 글
24.08.05 TIL - Java 람다 기초 (0) | 2024.08.06 |
---|---|
24.08.04 TIL - 윈도우 스프링 프로젝트 AWS 초 간단 배포 (0) | 2024.08.05 |
24.08.03 TIL - Effective Java Item 55 (0) | 2024.08.04 |
24.08.02 TIL - application.properties 분리 (0) | 2024.08.03 |
24.07.31 TIL - @Scheduled 관련 (1) | 2024.07.31 |