약수의 합 아주 빠르게 찾기 - 더블 카운팅(배수를 이용해 약수를 찾기), smallest prime factor
1. 문제
17427번: 약수의 합 2
두 자연수 A와 B가 있을 때, A = BC를 만족하는 자연수 C를 A의 약수라고 한다. 예를 들어, 2의 약수는 1, 2가 있고, 24의 약수는 1, 2, 3, 4, 6, 8, 12, 24가 있다. 자연수 A의 약수의 합은 A의 모든 약수를 더
www.acmicpc.net
2. 풀이
문제를 요약하면 1부터 n까지 각각 약수의 합을 전부 더한 값을 출력하는 문제
시간이 0.5초라서 당연히 1부터 n까지 소인수분해하고 약수의 합을 구한다면 시간초과겠지
약수의 합을 구하는 공식은...
https://deepdata.tistory.com/588
약수의 합과 약수의 개수 공식 익히기
1. 약수의 개수 자연수 n의 소인수분해가 $$n = p_{1}^{x_{1}}p_{2}^{x_{2}}...p_{k}^{x_{k}}$$라고 한다면, n의 양의 약수의 개수는 $$d(n) = (x_{1}+1)(x_{2}+1)...(x_{k}+1)$$ n을 소인수분해하여, 소인수들의 지수 + 1의
deepdata.tistory.com
0.5초라면 상당히 빠르게 구해야할 것 같은 느낌이 드는데
일단 smallest prime factor를 이용한다면 소인수분해를 조금 더 빠르게 할 수 있다
기억이 안나니 살짝 봐주고
https://deepdata.tistory.com/604
소인수분해 심화응용 - linear sieve와 smallest prime factor 알고리즘 익히기
1. 문제 16563번: 어려운 소인수분해 (acmicpc.net) 16563번: 어려운 소인수분해 첫째 줄에는 자연수의 개수 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에는 자연수 ki (2 ≤ ki ≤ 5,000,000, 1 ≤ i ≤ N)가 N개
deepdata.tistory.com
def get_spf(n):
#각 수의 spf는 i가 되도록 초기화
spf = [i for i in range(n+1)]
#모든 짝수는 2를 소인수로 가진다.
for i in range(4,n+1,2):
spf[i] = 2
#3부터 n**(1/2)까지 spf를 계산
for i in range(3,int((n+1)**(1/2))+1):
#i의 가장 작은 소인수가 i라는 것은
#i가 소수이고..
if spf[i] == i:
#i를 제외한 i의 배수는 i로 나누어 떨어지고
for j in range(i*i, n+1, i):
#아직 j의 spf가 계산되지 않았다면..
if spf[j] == j:
#i의 배수인 j는 spf가 i이다.
spf[j] = i
return spf
이를 이용해서 소인수분해를 수행한다.
그런데 약수의 합을 구해야하므로...
소인수랑 해당 소인수의 지수까지 구해서.. 등비수열 공식으로 한번에 구해줘야한다
def factorization(n,spf):
summation = {}
answer = 1
while n != 1:
summation[spf[n]] = summation.get(spf[n],0) + 1
n = n//spf[n]
for a,b in summation.items():
answer *= (a**(b+1)-1)//(a-1)
return answer
소인수분해 할때마다, dictionary에 소인수랑 지수도 저장해두고
소인수분해가 끝나면, key,value를 순회해서 약수의 합을 한번에 구해준다
그러면 모든 준비가 끝났다
spf를 구해주고, 1부터 n까지 순회해서...
i가 1인 경우 spf[1] = 1이라서.. 1 더해주고 (1은 소수가 아니라 예외사항)
spf[i] == i인 경우 i는 소수이므로 i+1을 더해준다.. 소수 i의 약수는 1,i니까
그 외에는 소인수분해를 통해 약수의 합을 구해서 더해준다
n = int(stdin.readline())
spf = get_spf(n)
answer = 0
for i in range(1,n+1):
if i == 1:
answer += 1
elif spf[i] == i:
answer += (i+1)
else:
answer += factorization(i,spf)
print(answer)
3. 더블 카운팅(double counting)
어떠한 등식을 증명할 때, 양변의 의미가 같음을 밝혀서 식의 값도 서로 같다고 증명하는 방법
등식이 도저히 직접 증명하기 어려울때, 간접적으로 증명하는 방식으로 자주 사용
더블 카운팅 - 나무위키
더블카운팅(Double Counting)은 어떠한 등식을 증명할때 양 변의 의미가 같음을 밝힘으로써 식의 값도 같다고 증명하는 것이다. 예를 들면 다음 등식은 더블카운팅을 이용해 증명할 수 있다. 2+4+6+⋅
namu.wiki
4. 배수를 이용해 약수를 찾는 방법
예를 들어 1부터 10까지 각각 약수를 적어본다면 다음과 같다
그러면 놀라운 특징이 보이는데
1은 10개가 있다.
2는 5개, 3은 3개, 4는 2개, 5는 2개, 6,7,8,9,10은 1개
이는 10 이내에서 1의 배수가 10개있고
2의 배수는 5개 2,4,6,8,10가 있고
3의 배수는 3개 3,6,9가 있고..
4의 배수는 2개 4,8이 있고
5의 배수는 2개 5,10이 있고,...
6,7,8,9,10은 10 이내에서 배수가 1개 있고
따라서 1부터 n 이내에서 모든 약수들의 합은 각각의 약수를 찾는것이 아니라,
조금 다르게 생각해서 1부터 n 각각이 n이내에서 배수의 개수가 몇개인지 세면 아주 빠르게 찾을 수 있다.
배수의 개수는 어떻게 셀 수 있을까?
구체적으로 n 이내에서 x의 배수의 개수는 몇개일까? n//x개이다.
10이내에서 2의 배수가 10//2 = 5로 5개
from sys import stdin
n = int(stdin.readline())
answer = 0
for i in range(1,n+1):
answer += (i*(n//i))
print(answer)
'정수론' 카테고리의 다른 글
1부터 n까지의 최소공배수를 빠르게 구하는 방법 (0) | 2023.05.04 |
---|---|
세 수의 최소 공배수를 최대로 만드는 방법 (0) | 2023.04.27 |
정수론 문제 - 나머지 연산을 꼭 기억하고 있어라 (0) | 2023.04.24 |
라그랑주의 네 제곱수 정리를 이용한 알고리즘(다이나믹 프로그래밍, 브루트포스 연습) (0) | 2023.04.23 |
팩토리얼 끝에서 가장 먼저 나오는 0이 아닌 수를 찾는 방법 (0) | 2023.04.12 |