본문 바로가기
Algorithm/백준

[Python] 백준 9020번 - 골드바흐의 추측

by 애기 개발자 2023. 7. 10.
반응형

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

 

9020번: 골드바흐의 추측

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

www.acmicpc.net

 

혼자 힘으로 풀었는가? O

알고리즘 분류
 - 수학
 - 정수론
 - 소수 판정
 - 에라토스테네스의 체

 

문제

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의 골드바흐 파티션을 출력한다. 출력하는 소수는 작은 것부터 먼저 출력하며, 공백으로 구분한다.

제한

  • 4 ≤ n ≤ 10,000

 


앞서 풀었던 에라토스테네스의 체 문제와 비슷한 유형이지만 조금 더 어려웠다.

먼저 소수집단을 구하고

 

그 중에서 가장 격차가 적은  두 소수의 합이 주어진 n과 일치하는 값을 찾는 방식이다.

 

처음엔 소수를 구한 후 2중 for문을 돌려서 주어진 n을 찾을 때까지 계속 더하는 식으로 했으나

 

시간초과가 발생했다.

 

(시간초과 코드)

import sys
from collections import deque
input = sys.stdin.readline

t = int(input())

limit = 10000
data = [True] * (limit+1)
sosu = set()
sosu2 = list()
partition = int(limit ** 0.5)
for i in range(2, 10000):
    if data[i] == True:
        sosu2.append(i)
        for j in range(i+i, limit+1, i):
            data[j] = False

for _ in range(t):
    n = int(input())
    flag = False
    a, b = 0, 0
    for i in range(len(sosu2)):
        if sosu2[i] >= n:
            break
        for j in range(i, len(sosu2)):
            if sosu2[i] + sosu2[j] > n:
                break
            if sosu2[i] + sosu2[j] == n:
                if a == 0:
                    a, b = sosu2[i], sosu2[j]
                else:
                    if abs(a-b) > abs(sosu2[i]-sosu2[j]):
                        a, b = sosu2[i], sosu2[j]
    print(a, b)

 


 

이후 방법을 다시 생각하여

 

중간값부터 찾는 방법을 생각했다.

 

예를 들어  n = 1000일 경우

 

500, 500부터 찾는 것이다.

 

위처럼 소수만 모아놓은 배열에서 찾는 것이 아닌 이미 찾아놓은 10000개의 data 배열을 이용하여 찾는 것이다.

 

값이 크면 앞의 숫자를 줄이고

 

값이 작으면 뒤의 숫자를 늘리는 방식으로

 

(정답 코드)

import sys
input = sys.stdin.readline

t = int(input())

limit = 10000
data = [True] * (limit+1)
sosu = list()
for i in range(2, int((limit+1) ** 0.5)):
    if data[i] == True:
        for j in range(i+i, limit+1, i):
            data[j] = False
            
for _ in range(t):
    n = int(input())
    a, b = n//2, n//2
    while True:
        if data[a] == False:
            a -= 1
            continue
        if data[b] == False:
            b += 1
            continue
        if data[a] == data[b]:
            if a + b == n:
                print(a, b)
                break
            elif a + b > n:
                a -= 1
            else:
                b += 1

 

 

 

반응형

댓글