Loading [MathJax]/jax/output/CommonHTML/jax.js

프로그래밍

[C/C++] 백준 4948: 베르트랑 공준(소수)

작삼심일 2021. 3. 7. 11:58

베르트랑 공준

https://www.acmicpc.net/problem/4948

4948번: 베르트랑 공준

베르트랑 공준은 임의의 자연수 n에 대하여, n보다 크고, 2n보다 작거나 같은 소수는 적어도 하나 존재한다는 내용을 담고 있다. 이 명제는 조제프 베르트랑이 1845년에 추측했고, 파프누티 체비쇼

www.acmicpc.net

베르트랑 공준은 임의의 자연수 n에 대해서 n보다 크고 2n보다 작거나 같은 소수는 적어도 하나 존재한다는 내용이다. 예를 들어 n=10일 때, 10보다 크고 20보다 작거나 같은 소수는 11,13,17,19로 총 4개 존재한다. 백준의 4948번: 베르트랑 공준 문제는 임의의 자연수 n을 입력받았을 때, n 보다 크고 2n보다 작거나 같은 소수의 개수를 찾는 문제이다.

이 문제에서의 핵심 사항은 다음과 같다.

  • 여러 개의 n을 입력 받아 반복적으로 소수의 개수를 출력해야 한다.
  • n의 값이 큰 경우에도 효율적으로 소수를 찾아낼 수 있어야 한다.

그렇다면 소수를 효율적으로 구할 수 있는 방법은 무엇일까?

소수 확인 방법

가장 기본적으로 소수를 확인하는 함수의 기본형은 다음과 같다.

bool IsPrime(int n)
{
    if (n==1)
    {
    	return false;
    }
    for (int i = 2; i < n; i++)
    {
        if(n % i == 0)
            return false;
    }
    return true;
}

입력받은 n1인 경우, 1은 소수의 정의에 의해 소수가 아니므로 false를 출력한다. 1이 아닌 경우에는 2부터 크고 자기 자신보다 하나 작은 자연수 n1까지의 모든 값에 대해서 나누어 떨어지는지 확인하고, 나누어 떨어지는 경우에는 false이고, 아닌 경우에는 true이다.

이 방법의 큰 단점은 바로 n이 커질 수록 반복 회수가 커진다는 의미이다. 백준 4948번: 베르트랑 공준 문제는 자연수 n부터 2n까지의 모든 자연수를 검사해야 하므로 수행 시간은 대략 O(N2)만큼 커진다는 의미이다. 이를 해결하기 위해서 첫 번째 방법은 에라토스테네스의 체를 사용하는 것이 있는데, 이것의 아이디어는 확인된 소수의 정수배인 자연수는 모두 소수가 될 수 없다는 것부터 시작이다. 이 문제에 에라토스테네스의 체를 사용 하기에는 검사해야 할 자연수의 개수가 너무 많다는 점이다. 이로 인해 문제에서 정의한 n이 가질 수 있는 모든 범위의 자연수에 대한 배열을 만들기 위한 메모리의 낭비와 소수의 모든 정수배인 자연수가 소수가 아님을 체크하는 과정에서 수행 시간 낭비가 발생할 수 있다.

두 번째 방법은 검사 횟수 자체를 줄이는 것이다. 이는 자연수가 가질 수 있는 공약수들을 토대로 만든 아이디어이다. 자연수 n이 서로 다른 두 개의 자연수 a,b로 이루어져 있고, 각각은 다음과 같은 관계를 가진다.

n=ab

0<ab<n

이 경우 a2n보다 작거나 같고, b2n보다 큰거나 같은 값을 같는다. 즉, 다음과 같은 관계식을 만족한다.

$a^2 {\leq} n {\leq} b^2$

anb

이다. 따라서 자연수 n이 소수인지 판별하기 위해서 n1까지의 값을 검사할 필요 없이 2부터 [n]보다 작은 모든 값을 검사하면 된다. 여기서 [x]x보다 작거나 같은 정수를 의미한다. 설명은 복잡했지만 결과만 활용해 소수 검사 함수를 만들면 다음과 같다.

bool IsPrime(int n)
{
	if (n == 1)
		return false;

	for (int i = 2; (i*i) <= n; i++)
	{
		if (n % i == 0)
		{
			return false;
		}
	}
	return true;
}

여기서 sqrt(n)이 아닌 ii를 쓴 이유는 제곱근을 계산 하는 것보다 두 수를 곱하는 것이 수행 시간적으로 더 빠르기 때문이다.

따라서 4948번: 베르트랑 공준의 제출 코드는 다음과 같다.

#include <iostream>
#include <cstring>

using namespace std;

bool IsPrime(int n)
{
	if (n == 1)
		return false;

	for (int i = 2; (i*i) <= n; i++)
	{
		if (n % i == 0)
		{
			return false;
		}
	}
	return true;
}

int main(void)
{
	int n = 1, count = 0;

	while (n != 0)
	{
		cin >> n;
		if (n == 0)
			break;
		count = 0;

		for (int i = (n + 1); i <= (2 * n); i++)
		{
			if (IsPrime(i))
			{
				count++;
			}
		}

		cout << count << "\n";
	}

	return 0;
}

 

728x90
반응형