소수, 에라토스테네스의 체, 표준분해, 오일러 피 함수

소수와 합성수 (primes and composites)
소수:
합성수:
※
※ 소수(prime number)의 발음은 '소쑤'이고, 소수(decimal notation)의 발음은 '소:수'이다.
따라서
소수 판정법 1,
어떤 수
def is_prime(n):
if n <= 1:
return False
for i in range(2, n):
if n % i == 0:
return False
return True
소수 판정법 2,
어떤수
그러므로
그러므로 주어진 수의 제곱근인
이 경우의 시간복잡도는
참고로, int(n ** 0.5) + 1와 같이 쓸 수 있지만, ** 0.5 연산은 애초에 부동소수점 연산이다.
def is_prime(n):
if n <= 1:
return False
if n == 2:
return True
if n % 2 == 0:
return False
i = 3
while i * i <= n:
if n % i == 0:
return False
i += 2
return True
따라서 while 까지 내려온 수는 짝수가 아니므로
(그렇다고해서 전체 시간복잡도가 줄어들지는 않는다.)
알고리즘대회가 아닌 기업 코딩테스트 수준에서
※ 매우 큰 수의 경우 밀러-라빈(소수판정) 또는 폴라드 로(소인수분해) 등을 이용한다. 거의 대회에서나 사용되는 고급 알고리즘이다.
에라토스테네스의 체,
1은 소수가 아니므로 제외한다.
2부터 소수의 배수를 제외하는 방법이다.
2는 소수이므로 2보다 큰 2의 배수를 제외한다.
3은 소수이므로 3보다 큰 3의 배수를 제외한다.
4는 이미 지워졌다.
5는 소수이므로 5보다 큰 5의 배수를 제외한다.
이렇게 반복하면 상당히 빠른 시간내에 합성수를 제외할 수 있다.
is_prime = [True] * (N + 1)
is_prime[0] = is_prime[1] = False
i = 2
while i * i <= N:
if is_prime[i]:
for j in range(i * i, N + 1, i):
is_prime[j] = False
i += 1
# if is_prime[x] == True, then x is prime (1 <= x <= N)
특정 구간의 수가 소수인지 판정하는 방법에 효율적이다.
주로 소수를 판정해야 하는 수가 쿼리로 계속 주어지는 경우,
쿼리 개수
BOJ 1929, 소수 구하기, 실버 3, https://www.acmicpc.net/problem/1929
(물론 이 문제는 위의
BOJ 4134, 다음 소수, 실버 4, https://www.acmicpc.net/problem/4134
주어진
게다가
따라서 처음에 소개한
참고로 소수의 개수는 무한함이 증명되어있기 때문에 무한루프에 걸리지 않고 반드시 멈춘다.
import sys
#sys.stdin = open('input.txt', 'r')
input = sys.stdin.readline
def is_prime(n):
if n <= 1:
return False
if n == 2:
return True
if n % 2 == 0:
return False
i = 3
while i * i <= n:
if n % i == 0:
return False
i += 2
return True
T = int(input())
for _ in range(T):
n = int(input())
i = n
while not is_prime(i):
i += 1
print(i)
산술의 기본정리 (The Fundamental Theorem of Arithmetic; FTA)
(1)보다 큰 소수는 소수이거나, 소수들의 곱으로 표현할 수 있다.
(2) 이 표현법은 (곱하는 순서를 정해주면) 유일하다.
Corollary보다 큰 정수 은 아래와 같이 표준형(canonicㅁa form)으로 나타낼 수 있고 이는 유일(unique)하다.
이때은 양의 정수이며 각 의 대소관계는 이다.
증명은 생략한다.
유클리드 호제법과 유사하게
의 표준분해가 다음과 같다고 하자.
[양의 약수의 개수]의 모든 양의 약수들의 개수를 이라 하면
이다.
[양의 약수의 합]의 모든 양의 약수들의 합은 라 하면
이다.
소인수분해 알고리즘
관련 문제
BOJ 11653, 소인수분해, 브론즈 1, https://www.acmicpc.net/problem/11653
소수판정법과 유사하게 인수
가장 큰 소수는
시간복잡도는
N = int(input())
i = 2
while i * i <= N:
while N % i == 0:
print(i)
N //= i
i += 1
if N > 1:
print(N)
BOJ 2312, 수 복원하기, 실버 3, https://www.acmicpc.net/problem/2312
import sys
#sys.stdin = open('input.txt', 'r')
#sys.setrecursionlimit(10 ** 7)
input = sys.stdin.readline
T = int(input())
for _ in range(T):
N = int(input())
p = 2
while p * p <= N:
p_count = 0
while N % p == 0:
p_count += 1
N //= p
if p_count:
print(p, p_count)
p += 1
# N이 1보다 큰 경우, 남은 N은 소수임
if N > 1:
print(N, 1)
BOJ 16563, 어려운 소인수분해, 골드 4, https://www.acmicpc.net/problem/16563
이번엔 소인수분해를 할 수가 쿼리로 주어진다.
쿼리 개수가
에라토스테네스의 체를 응용하여 1차원 배열 F[x]를 x의 가장 작은 인수라 하자.
예를 들어 F[10]=2, F[9]=3, F[49]=7, F[p]=p이다, (p는 소수)
참고로 F[1] = 0이다.
예를 들어, 30을 인수분해한다고 하자.
그러면 F[30]=2이므로 2를 출력하고 30을 2로 나눈 15를 찾는다.
F[15]=3이므로 3을 출력하고 15를 3으로 나눈 5를 찾아간다.
F[5]=5이므로 5를 출력하고 5를 5로 나눈 1을 찾아간다.
마지막으로 남은 수 1은 while 조건에 위배되므로 while loop가 끝나게 된다.
import sys
#sys.stdin = open('input.txt', 'r')
#sys.setrecursionlimit(10 ** 7)
input = sys.stdin.readline
MAX_N = 5000000
F = [i for i in range(MAX_N + 1)]
F[1] = 0
i = 2
while i * i <= MAX_N:
if F[i] == i:
for j in range(i * i, MAX_N + 1, i):
if F[j] == j:
F[j] = i
i += 1
def factorize(F, x):
while x > 1:
print(F[x], end=' ')
x //= F[x]
print()
N = int(input())
k_list = list(map(int, input().split()))
for k in k_list:
factorize(F, k)
오일러 피 함수 (Euler's phi function)
정수론에는 자주 등장하는 함수들이 있는데, 오일러 피 함수는 그중 하나이다.
Euler phi-function일 때, 은 중에서 를 만족하는 의 수로 정의한다.
즉,이하의 양의 정수 중에서 과 서로소인 정수의 개수를 이라 한다.
Properties
(1)is prime.
(2)에 대하여 은 짝수이다.
(3) 소수에 대하여
(4)이다. 즉 는 곱셈함수(multiplicative function)이다.
(5)이라 하면
(1)
(4)
(5)
BOJ 11689, GCD(n, k)=1, 골드 1, https://www.acmicpc.net/problem/11689
오일러 피 함수를 직접 물어보는 문제이다.
에라토스테네스의 체를 응용한 반복문이므로 시간복잡도는
그리고 오일러 함수의 정의에 따라
그런데 분수를 이용한 곱셈은 부동소수점을 이용하므로 적절하지 않다.
if n % p == 0:
while n % p == 0:
n //= p
result -= result // p
전체 코드는 다음과 같다.
def euler_phi(n):
result = n
p = 2
while p * p <= n:
if n % p == 0:
while n % p == 0:
n //= p
result -= result // p
p += 1
if n > 1:
result -= result // n
return result
N = int(input())
print(euler_phi(N))
수학적으로는
그러나 프로그래밍 문제 상황에 따라
BOJ 23832, 서로소 그래프, 골드 1, https://www.acmicpc.net/problem/23832
서로 다른 두 정점이 서로소인 경우에만 간선을 연결한다.
그러니까
Brute Force로 모든 정점 쌍을 찾으면
(예시 코드 생략)
BOJ 4355, 서로소, 골드 1, https://www.acmicpc.net/problem/4355
단순히 서로소를 판단하고 싶으면
그러나
따라서 이 문제에서
(예시 코드 생략)
'스터디 > 정수론' 카테고리의 다른 글
[정수론] 디오판토스 방정식 (Diophantine equation) (0) | 2024.08.19 |
---|---|
[정수론] 약수, 배수, 최대공약수, 최소공배수와 관련된 알고리즘 (0) | 2024.08.10 |
[정수론] 오일러의 정리, 페르마의 소정리, 중국인의 나머지 정리, 윌슨의 정리 (0) | 2024.08.06 |