수학
에라토스테네스의 체
소수를 체로 거르기
1부터 60까지 수가 있습니다. 몇 개가 남을지 먼저 고르고 지워지지 않은 가장 작은 수를 눌러 그 수의 배수를 지워 보세요.
1부터 60까지 수가 있어요. 1 은 처음부터 지워져 있습니다. 2·3·5·7 의 배수를 지우고 나면 남는 수는 몇 개일까요?
지운 수 0개 · 남은 수 59개
내 예상 개 · 실제 17개 → 달랐어요
누른 수입니다. 마지막 수는 눌러도 지워지는 수가 없어서 거기서 끝났습니다.
- 남은 수 — 17개: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59
- 이 수들의 공통점 — 1 과 자기 자신 말고는 나누어떨어지는 수가 없다
약수가 1 과 자기 자신뿐인 수 = 소수(prime number)
작은 수부터 그 배수를 지워 나가는 방법 = 에라토스테네스의 체
7 까지만 눌러도 끝이었습니다. 11 부터는 11 × 11 = 121 이 60을 넘어서 눌러도 지울 수가 없습니다. 60 이하의 합성수는 7 이하의 소수를 약수로 꼭 하나는 가집니다.
수마다 나눠 보기 · 1000만 이하54.8초
체 · 1000만 이하0.55초
남은 수는 더 작은 수의 배수가 아니다
- 지워지지 않은 수 — 더 작은 어떤 수의 배수도 아니다. 그래서 1 과 자기 자신 말고는 약수가 없다
- 작은 수부터 누른다 — 앞에서 누른 수가 합성수를 대신 지워 준다. 3 을 누르기 전의 9 는 아직 지워지지 않았을 뿐 약수가 3 인 수다
- 처음부터 지워 둔 1— 약수가 1 하나뿐이라 “1 과 자기 자신” 규칙에서 따로 뺐다
코드는 소수를 만날 때마다 배수를 지운다
위에서 지운 것과 같은 1~60 입니다. is_prime[k] 가 True 인 수가 지워지지 않고 남은 수입니다.
다음을 눌러 한 줄씩 넘기며 지워지는 칸을 보세요. 안쪽 반복은 칸마다 되풀이되지만 소수 하나당 두 스텝으로 묶어 보여 줍니다.
남은 수 59개
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
다음을 눌러 한 줄씩 넘겨 보세요
지운 수 = 0
sieve.py
- 1def sieve(n):
- 2 is_prime = [True] * (n + 1)
- 3 is_prime[0] = is_prime[1] = False
- 4 for i in range(2, int(n ** 0.5) + 1):
- 5 if is_prime[i]:
- 6 for j in range(i * i, n + 1, i):
- 7 is_prime[j] = False
- 8 return [k for k in range(n + 1) if is_prime[k]]
0 / 17
if is_prime[i]— i 가 아직 지워지지 않았을 때만 안쪽 반복으로 들어간다range(i * i, n + 1, i)— i × i 부터 i 칸씩 지운다. i × 2 부터 i × (i − 1) 까지는 더 작은 수가 이미 지웠다int(n ** 0.5)— i 는 7 까지만 돈다. 8 부터는 i × i 가 60 을 넘어 새로 지울 수가 없다
같은 방법으로 1~100 을 거르면 눌러서 배수가 지워지는 가장 큰 수는 얼마일까요?
체에서 틀리는 자리
is_prime[0] = is_prime[1] = False를 빼면 — 에러가 안 난다. 소수 목록이 17개가 아니라 19개이고[0, 1, 2, …]로 시작한다i * i대신i * 2부터 지우면 — 결과는 같다. 지우는 횟수만 늘어서 100만 기준 2,122,048번 대 2,197,839번이다. 시간은 0.035초와 0.036초로 거의 같았다- 바깥 반복을 n 까지 돌리면 — √n 보다 큰 수까지 전부 훑는다. 100만에서 999번이 아니라 999,999번 돈다. 0.035초가 0.049초가 됐다
- 수 하나만 소수인지 알고 싶으면 — 체를 만들 필요가 없다. √n 까지
n % d로 나눠 보면 된다. 1,000,000,007 은 0.0013초 만에 소수라고 나왔다 - 칸이 많으면 메모리가 문제다 — 리스트는 한 칸이 8바이트라 100만 칸이 8,000,056바이트(
sys.getsizeof)다. 1억 칸이면 약 800MB다.bytearray는 한 칸 1바이트라 약 100MB다 - 슬라이스로 한 번에 지우면 —
is_prime[i*i::i] = [False] * len(range(i*i, n+1, i)). 1000만에서 0.55초가 0.22초로 줄었다
문제에서 이 말이 보이면
N 이하 소수를 전부 구한다소수 판별을 여러 번 한다약수가 없는 수를 거른다
이 탭의 알고리즘 목록은 여기까지입니다. 왼쪽 사이드바에서 다른 장을 골라 다시 풀어 보세요.