입력으로 들어온 테스트 케이스에 대해서 두 소수의 합으로 나타낼 수 있는지 구하여라, 이 때 두 소수의 차이가 가장 큰 경우를 결과로 출력한다. 만약 소수의 합으로 결과를 나타낼 수 없다면 "Goldbach's conjecture is wrong."을 출력한다.
전형적인 소수 찾기 문제이다. 소수 찾기는 에라토스테네스의 체를 쓴다고 누누히 언급했다. 에라토스테네스의 체에 대한 설명은 이 문서를 참고하자. 그렇다면 동일하게 에라토스테네스의 체를 쓰는 문제를 왜 또 글을 작성하냐면 기존의 방식보다 훨씬 간단하게 체를 구현해서 소수를 구하는 방식을 찾았기 때문이다.
def eratosthenes_sieve(N):
IsPrime[0] = IsPrime[1] = False
for i in range(2, int(N**0.5) + 1):
for j in range(i * 2, N + 1, i):
IsPrime[j] = False
prime = []
for i in range(N + 1):
if IsPrime[i]:
prime.append(i)
return prime
isPrime = set(i for i in range(2, M))
for i in range(2, int(M**0.5 + 1)):
if i in isPrime:
isPrime.difference_update(range(i * i, M + 1, i))
코드의 길이, 실행 시간 모두 수정한 버전이 더 좋은 성능을 보인다. 실행 방식을 설명하자면 아래와 같다.
range의 반환이 list형이라고 생각할 수 있는데 실제로는 마땅히 range 객체라고 불러야한다. 그 성질은 'iterable'한 정수 원소를 가진 list 유사의 객체라고 할 수 있다.
- 파이썬에서 list, dict, set, str, bytes, tuple, range 등은 iterable한 객체입니다.
difference_update 함수 : 두 Set에서 공통 요소, 교집합를 제거한다
- 반환값은 따로 없음
위와 같이 개선된 에라토스테네스의 체를 소개하기 위해 해당 글을 작성했다.
체를 사용해 소수를 모두 구했다면 이후 두 정수를 구하는 건 간단하게 구현가능하다. prime을 앞에서 꺼내면서 n - prime의 값이 prime안에 존재하다면 바로 출력하고 break해주면 된다.
prime, n-prime을 구하면 그 차이는 n - ( 2 * prime )이므로 가장 먼저 찾은 prime이 가장 작은 prime이고 해당 값이 차이가 가장 커지는 값이다.
import sys
input = sys.stdin.readline
M = 10**6
isPrime = set(i for i in range(2, M))
for i in range(2, int(M**0.5 + 1)):
if i in isPrime:
isPrime.difference_update(range(i * i, M + 1, i))
while True:
n = int(input())
if n == 0:
break
for prime in isPrime:
if n - prime in isPrime:
print(n, "=", prime, "+", n - prime)
break
else:
print("Goldbach's conjectrue is wrong.")