[백준] 9020 골드바흐의 추측(실버1) - Python

jiiiii·2022년 1월 12일
2

알고리즘

목록 보기
9/19
post-custom-banner

문제

1보다 큰 자연수 중에서 1과 자기 자신을 제외한 약수가 없는 자연수를 소수라고 한다. 예를 들어, 5는 1과 5를 제외한 약수가 없기 때문에 소수이다. 하지만, 6은 6 = 2 × 3 이기 때문에 소수가 아니다.

골드바흐의 추측은 유명한 정수론의 미해결 문제로, 2보다 큰 모든 짝수는 두 소수의 합으로 나타낼 수 있다는 것이다. 이러한 수를 골드바흐 수라고 한다. 또, 짝수를 두 소수의 합으로 나타내는 표현을 그 수의 골드바흐 파티션이라고 한다. 예를 들면, 4 = 2 + 2, 6 = 3 + 3, 8 = 3 + 5, 10 = 5 + 5, 12 = 5 + 7, 14 = 3 + 11, 14 = 7 + 7이다. 10000보다 작거나 같은 모든 짝수 n에 대한 골드바흐 파티션은 존재한다.

2보다 큰 짝수 n이 주어졌을 때, n의 골드바흐 파티션을 출력하는 프로그램을 작성하시오. 만약 가능한 n의 골드바흐 파티션이 여러 가지인 경우에는 두 소수의 차이가 가장 작은 것을 출력한다.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고 짝수 n이 주어진다.

출력

각 테스트 케이스에 대해서 주어진 n의 골드바흐 파티션을 출력한다. 출력하는 소수는 작은 것부터 먼저 출력하며, 공백으로 구분한다.


풀이

우선 n이 소수인지 확인하는 is_prime() 함수를 만든다.

def is_prime(n):
    if n == 1:
        return False
    for j in range(2, int(n**0.5) + 1):
        if n % j == 0:
            return False
    return True

n이 소수인지 확인하기 위한 방법으로는 n 미만의 수로 n이 나누어 떨어지는지 확인하는 방법과 에라스토테네스의 체 방법이 있는데, 나는 for문 돌리는 것에 익숙해져 있어서 이 방법을 잘 쓴다.

하지만 무작정 for문을 돌리게 되면 어떤 문제든 거의 무조건 시간 초과가 난다.
그래서 n이 range(2, n**0.5) 사이에 있는 숫자로 나누어 떨어지는 지 확인을 한다.

왜 sqrt(n)까지만 확인을 하면 되는 걸까?

  1. 우선 n이 sqrt(n) 이하의 수로 나누어 떨어지지 않는다고 가정을 한다.
  2. n = x * y라고 가정을 한다.
    ⇒ 그러면 1에 의해 n은 sqrt(n) 이하의 수로는 나누어 떨어지지 않으므로
    x > sqrt(n)
    y > sqrt(n)
    ⇒ xy > n

따라서 n = xy라고 가정한 2번은 모순이다.
따라서 n이 sqrt(n) 이하의 수로 나누어 떨어지지 않는다면 그 이상의 수로도 나누어 떨어질 수 없다.

가장 차이가 적은 두 소수를 합해서 해당 짝수가 되도록 하려면?

처음에는 너무 복잡하게 생각해서 num 이하의 소수를 따로 리스트에 넣어서 그 수들의 조합을 구해서 그 조합들의 숫자들이 더해서 num이 되는지를 확인하고 그 중에서 또 차이가 더 적은 한 쌍을 구하는 코드를 짰는데 역시나 시간 초과ㅋ

구글링을 해봤더니 너무나도 간단한 답이 있었다.
num을 반으로 쪼개고 한 개는 +1씩, 한 개는 -1씩 해보면서 두 수가 모두 소수인지 확인하는 것.

for _ in range(int(input())):
    num = int(input())

    a, b = num//2, num//2
    while a > 0:
        if is_prime(a) and is_prime(b):
            print(a, b)
        else:
            a -= 1
            b += 1

전체 코드

def is_prime(n):
    if n == 1:
        return False
    for j in range(2, int(n**0.5) + 1):
        if n % j == 0:
            return False
    return True


for _ in range(int(input())):
    num = int(input())

    a, b = num//2, num//2
    while a > 0:
        if is_prime(a) and is_prime(b):
            print(a, b)
        else:
            a -= 1
            b += 1
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 9월 26일

코드 뿐만 아니라 이 문제를 위해 필요한 개념들도 같이 설명해주셔서 감사합니다!
덕분에 많은 도움 받고 갑니다~ 😁

답글 달기
comment-user-thumbnail
2022년 11월 23일

자세한 설명 잘 봤습니다.
전체 코드에서 while문 안에 break가 필요할 것 같아요 :) 종료조건을 설정해야 답에 맞게 나올 것 같습니다 !

답글 달기