소수를 찾을 때 에라토스테네스의 체를 이용하는 방법보다
더 빠르게 찾을 수 있는 방법이 있다.
1부터 소수를 찾아나가면서, 배수를 지워버리는 방법이다.
숫자의 배수들은 소수가 아니므로, 소수인지 검증하기 위해 다른 숫자들로 나눠볼 필요가 없다!!
전체 코드
prime = [] # 소수를 담을 배열
check = [False]+[False] + [True] * 9999 # index에 해당하는 숫자가 소수일 때만 True를 담을 배열
# 2부터 10000까지 소수 찾기 시작~!
for number in range(2, 10001):
if check[number]:
# True가 담겨있으면 소수임! 👉🏻 prime 배열에 추가한다.
prime.append(number)
# 찾은 소수의 배수들을 check 배열에서 False로 바꿔준다.
# 👉🏻 False로 바뀐 것들은 위 코드에서 prime 배열에 추가되지 않는다.
for i in range(number*2, 10000, number):
check[i] = False
prime = [] # 소수를 담을 배열
check = [False]+[False] + [True] * 9999 # index에 해당하는 숫자가 소수일 때만 True를 담을 배열
prime
: 이 배열에는 실제 소수를 담을 것이다.
check
: 이 배열의 Index가 소수라면 True를, 소수가 아니라면 False를 담을 것이다.
초기에는 [False, False, True, True ..., True]
를 담아둔다.
🤔 False를 두개 담아놓는 이유는? 👉🏻 0과 1은 소수가 아니니까~!
# check 배열에 담길 값들을 미리 보자! 👀
check[0] = False
check[1] = False
check[2] = True # 이렇게 소수일 경우에만 True로 그대로 두고, (2는 소수 ⭕️)
check[3] = True # (3은 소수 ⭕️)
check[4] = False # 소수가 아니면 아래 2단계에서 False로 바꿔줄 것이다. (4는 소수가 아님 ❌)
...
2단계가 잘 이해되지 않는다면, 3단계 먼저 읽어보긔 😊
# 2부터 10000까지 소수 찾기 시작~!
# number가 소수인지 검증한다.
for number in range(2, 10001):
if check[number]:
# True가 담겨있으면 소수임! 👉🏻 prime 배열에 추가한다.
prime.append(number)
... # 3단계에서 이어짐
range(2, 10001)
10000보다 작거나 같은 수number <= 10000
중에서 소수에 해당하는 값을 찾을 것이다.
( 🤔왜 10000? 👉🏻 문제에 제시된 가장 큰 수가 10000임ㅋㅎ )
0과 1은 어차피 소수가 아니니까 2부터 10000까지 반복문을 돌려돌려🌪
if check[number]
이 for문 안에서 조건문으로 number가 소수인지 검증한다.
check
배열에 담긴 값이 True
라면, 해당 Index에 해당하는 숫자는 소수이다.
(3단계에서 그렇게 만들 거임)
위에서 check[2]
에 True
를 넣어놨는데,
2는 소수이므로 일단 소수인지 검증할 필요가 없다. (즉, number가 2일 때는 당연히 실행되는 조건문이다.)
prime.append(number)
True
라면 소수를 담는 배열인 prime
에 number
를 추가한다.🔥 이제부터가 진짜 검증 시작임!!
아래 코드는 위의 2단계 코드에서 이어진다.
# 2부터 10000까지 소수 찾기 시작~!
# number가 소수인지 검증한다.
for number in range(2, 10001):
if check[number]:
# True가 담겨있으면 소수임! 👉🏻 prime 배열에 추가한다.
prime.append(number)
🚨🚨🚨 이 아래부터가 3단계 🚨🚨🚨
# 찾은 소수의 배수들을 check 배열에서 False로 바꿔준다.
# 👉🏻 False로 바뀐 것들은 위 코드에서 prime 배열에 추가되지 않는다.
for i in range(number*2, 10000, number):
check[i] = False
for i in range(number*2, 10000, number):
예를 들어,
number가 2라면
4, 6, 8 ... 등 2의 배수들은 소수가 아님
그러므로 check
배열에서 number
의 배수들에 해당하는 index에 False
값을 넣어줄 것이다.
반복문의 범위는
시작: number의 2배수
끝: 마지막 숫자
간격: number
이렇게 하면 number의 2배수부터 마지막 숫자 전까지 number의 배수들이 하나씩 i
에 들어온다.
이제 바깥 반복문에서는 여기서 False
를 넣은 number인 경우 if문의 조건에 맞지 않아
다음 반복으로 넘어가게 된다.
다른 방법들과 속도를 비교해보았다.
다른 방법들에는 에라토스테네스의 체를 모두 적용했다.
👉🏻 배수 제거 방식이 제일 빠름 😊
다른 방식들은 배수 제거 방식보다 무려 4.8배
나 더 반복한다.
속도도 4~5배
!
prime = [2, 3]
for number in range(5, 10001, 2):
for i in range(2, int(number**(1/2))+1):
if number % i == 0:
break
else:
prime.append(number)
prime = [2, 3]
for number in range(5, 10001, 2):
i = 2
while i * i <= number:
if number % i == 0:
break
i += 1
else:
prime += [number]
# 문제
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의 골드바흐 파티션을 출력한다. 출력하는 소수는 작은 것부터 먼저 출력하며, 공백으로 구분한다.
import sys
N = int(input())
numbers = [int(sys.stdin.readline()) for _ in range(N)]
prime = [] # 소수를 담을 배열
check = [False]+[False] + [True] * 9999 # index에 해당하는 숫자가 소수일 때만 True를 담을 배열
# 2부터 10000까지 소수 찾기 시작~!
for number in range(2, 10001):
if check[number]:
# True가 담겨있으면 소수임! 👉🏻 prime 배열에 추가한다.
prime.append(number)
# 찾은 소수의 배수들을 check 배열에서 False로 바꿔준다.
# 👉🏻 False로 바뀐 것들은 위 코드에서 prime 배열에 추가되지 않는다.
for i in range(number*2, 10000, number):
check[i] = False
소수를 찾아서 prime 배열에 넣어놓는다.
n은 최대 10,000까지 주어지므로, 10000까지만 구해주면 된다.
for number in numbers:
result = []
# 소수를 하나씩 변수 p로 가져온다.
for p in prime:
# 대소가 반전되고 나오는 건 똑같은 쌍이니까 👉🏻 또 따질 필요 없어서 break!
if p > number - p:
break
# (number - p)가 소수이면 => 소수의 합으로 number를 나타낼 수 있음
if check[number - p]:
result.append(p)
일단, prime
배열로 반복문을 돌려서 소수를 하나씩 가져온다.
p
변수에 소수가 하나씩 담긴다.
가져온 소수 p
와 다른 소수의 합이 number
가 되는 경우를 찾아야 한다.
number - p
가 소수라면 p
와 number - p
가 합이 number
가 되는 소수의 쌍이다.
if p > number - p:
break
(p
, number - p
) 쌍 중에서
(3, 5) 쌍과 (5, 3) 쌍은 동일한 쌍이므로 중복으로 따질 필요가 없다.
따라서, number - p
가 p
보다 작아지면 반복문을 종료한다.
if check[number - p]:
result.append(p)
p
는 prime 배열에서 하나씩 꺼내온 거니까 당연히 소수이고,
number - p
가 소수인지 확인해야 한다.
소수를 찾을 때 만든 배열 check에서 소수인지 검증할 숫자 인덱스가
True
이면 소수인 것을 이용한다.
소수라면, 합이 number가 되는 소수 쌍을 찾았으므로,
p
를 result 배열에 넣어둔다.
(p
의 짝은 number - p
로 구하면 되니까 p만 저장 ㄱ ㄱ)
# 가능한 p 중에서 제일 큰 p 찾기
print(max(result), number-max(result))
소수의 쌍은 위에서 구한 p
와 number - p
이다.
즉, 두 소수의 차이가 가장 작은 쌍은 p
가 가장 큰 경우를 찾으면 된다.
result 배열의 최대값을 찾고, 대응되는 짝을 구해 출력한다.
끝~
전체 코드
import sys
N = int(input())
numbers = [int(sys.stdin.readline()) for _ in range(N)]
prime = [] # 소수를 담을 배열
check = [False]+[False] + [True] * 9999 # index에 해당하는 숫자가 소수일 때만 True를 담을 배열
# 2부터 10000까지 소수 찾기 시작~!
for number in range(2, 10001):
if check[number]:
# True가 담겨있으면 소수임! 👉🏻 prime 배열에 추가한다.
prime.append(number)
# 찾은 소수의 배수들을 check 배열에서 False로 바꿔준다.
# 👉🏻 False로 바뀐 것들은 위 코드에서 prime 배열에 추가되지 않는다.
for i in range(number*2, 10000, number):
check[i] = False
# 소수의 합 쌍 찾기 시작~!
for number in numbers:
result = []
# 소수를 하나씩 가져온다.
for p in prime:
# 대소가 반전되고 나오는 건 똑같은 쌍이니까 👉🏻 또 따질 필요 없어서 break!
if p > number - p:
break
# (number - p)가 소수이면 => 소수의 합으로 number를 나타낼 수 있음
if check[number - p]:
result.append(p)
# 가능한 p 중에서 제일 큰 p 찾기
print(max(result), number-max(result))