1부터 n까지의 최소공배수를 빠르게 구하는 방법

1. 문제

 

11690번: LCM(1, 2, ..., n) (acmicpc.net)

 

11690번: LCM(1, 2, ..., n)

첫째 줄에 1보다 크거나 같고, n보다 작거나 같은 모든 자연수의 최소공배수를 출력한다. 정답이 매우 커질 수 있기 때문에, 232로 나눈 나머지를 출력한다.

www.acmicpc.net

 

2. 풀이

 

두 수 a,b의 최소공배수를 어떻게 구하는지 생각해본다면..

 

a랑 b를 소인수분해하고... 각각 소인수분해에서.. 소인수의 지수가 큰것끼리 곱하면 그것이 a,b의 최소공배수이다.

 

예를 들어 18과 24의 최소공배수를 구한다면..

 

18=232

 

24=233

 

여기서 소인수는 2와 3만 나타나는데.. 각각에서 지수가 큰 경우는 23이랑 32으로 두 수를 곱하는 것이 최소공배수이다.

 

그러니까 최소공배수는 72

 

그래서 1부터 n까지 소인수분해를 해서, 나타나는 모든 소인수들에 대해 소인수 지수가 가장 큰 경우만 찾아서 곱해가지고 구할려고 했는데

 

시간초과 나오더라

 

from sys import stdin

def get_prime(n):
    
    result = [1]*(n+1)
    result[1] = 0

    for i in range(2,int((n+1)**(1/2))+1):
        
        if result[i] == 1:
            
            for j in range(i+i,n+1,i):
                
                result[j] = 0
    
    return result

def factorization(n,prime_count):
    
    p = 2

    while p*p <= n:
        
        p_count = 0

        while n % p == 0:
            
            n = n//p
            p_count += 1
        
        x = prime_count.get(p,0)

        if x < p_count:
            
            prime_count[p] = p_count
        
        p += 1
    
    if n > 1:
        
        if prime_count.get(n,0) == 0:
            
            prime_count[n] = 1
    
    return prime_count

n = int(stdin.readline())

prime_list = get_prime(n)

prime_count = {}

for i in range(1,n+1):
    
    if prime_list[i] == 1:
        
        prime_count[i] = 1
    
    else:
        
        prime_count = factorization(i,prime_count)

answer = 1
mod = 2**32

for k,v in prime_count.items():

    for _ in range(v):

        answer *= k
        answer %= mod

print(answer)

 

소인수분해가 느려서 그런지 smallest prime factor를 이용해 빠르게 소인수분해해서 구해볼려고 했는데

 

그래도 안돼

 

from sys import stdin

def get_spf(n):
    
    spf = [i for i in range(n+1)]

    for i in range(2,n+1,2):
        
        spf[i] = 2

    for i in range(2,int((n+1)**(1/2))+1):
        
        if spf[i] == i:
            
            for j in range(i+i,n+1,i):
                
                if j % i == 0:

                    spf[j] = i

    return spf

def factorization(n,spf,prime_count):
    
    p_count = {}

    while n > 1:
        
        p_count[spf[n]] = p_count.get(spf[n],0) + 1

        n = n//spf[n]
    
    for k,v in p_count.items():
        
        prime_count[k] = max(prime_count.get(k,0),v)
    
    return prime_count

n = int(stdin.readline())

spf = get_spf(n)

prime_count = {}

for i in range(1,n+1):
    
    if spf[i] == i:
        
        prime_count[i] = 1
    
    else:
        
        prime_count = factorization(i,spf,prime_count)

answer = 1
mod = 2**32

for k,v in prime_count.items():

    for _ in range(v):

        answer *= k
        answer %= mod

print(answer)

 

 

여러가지 잔재주 부려서 어떻게든 풀어볼라했는데 안되더라고

 

그래서 다르게 접근해야한다는걸 알았지

 

"1부터 n까지 소인수분해를 한다면.. 나타나는 소인수는 n 이하의 소수이므로, n이하의 소수를 먼저 모두 찾는다

 

다음, 각각 소수에 대해 n 이하에서 지수의 최댓값이 얼마인지 계산하고..

 

이들을 전부 곱하면 그것이 최소공배수"

 

에라토스테네스의 체를 이용해서 n이하의 소수를 먼저 모두 찾고,

 

n이하에서 각 소수가 지수를 최대 얼마까지 가지는지 계산한다...

 

이건 어떻게 계산할 수 있지? 반복문으로 계속 곱해보는것도 있기는 하지만..

 

log를 이용해서도 구할 수 있다.

 

예를 들어 n = 32에서 2의 지수는 최대 얼마까지? int(log2(32))

 

3의 지수는? int(log3(32)) ...

 

...

 

여기서 문제는 232로 나눈 나머지를 구해야하니까.. 모듈로 연산 성질을 이용해 곱하면서 나머지를 계속 구해가지고..

 

수를 압축시켜 빠르게 계산해야겠지

 

import math
from sys import stdin

def get_prime(n):
    
    result = [1]*(n+1)

    for i in range(2,int((n+1)**(1/2))+1):
        
        if result[i] == 1:
            
            for j in range(i+i,n+1,i):
                
                result[j] = 0
    
    return [i for i in range(2,n+1) if result[i] == 1]

n = int(stdin.readline())

prime_list = get_prime(n)

answer = 1

mod = 2**32

for p in prime_list:

    answer *= pow(p,int(math.log(n,p)),mod)
    answer %= mod

print(answer)

 

근데 이렇게 하면 시간초과가 아니라 메모리 초과가 나더라고

 

n의 최대가 108인데 result를 만들때 메모리 초과가 나나 했음

 

그래서 어떻게든 메모리 줄여볼려고 여러 잔재주를 부렸는데

 

get_prime에서 소수 리스트를 return하지 말고 result를 return해서 써먹도록 하면...

 

조금 더 채점하긴 하는데 어쨌든 메모리 초과나고

 

import math
from sys import stdin

def get_prime(n):
    
    result = [1]*(n+1)

    for i in range(2,int((n+1)**(1/2))+1):
        
        if result[i]:
            
            for j in range(i+i,n+1,i):
                
                result[j] = 0
    
    return result

n = int(stdin.readline())

prime_list = get_prime(n)

answer = 1

mod = 2**32

for i in range(2,n+1):
    
    if prime_list[i]:
        
        answer *= pow(i,int(math.log(n,i)),mod)
        answer %= (mod)

print(answer)

 

 

뭐 mod = 2**32로 저장하는 것 조차도 큰 수니까 메모리 잡아먹는다고 생각했고

 

여러 풀이 보니까 0,1  정수를 저장하지 말고 True, False bool을 저장하면 메모리 아낄수 있다고 하더라고

 

그렇게도 해봤는데 어쨌든 메모리 초과남

 

pypy3가 메모리 잡아먹는 괴물이라 더 이상 줄이기는 어려운듯

 

python3로 채점하자니 시간초과나고

 

import math
from sys import stdin

def get_prime(n):
    
    result = [True]*(n+1)

    for i in range(2,int((n+1)**(1/2))+1):
        
        if result[i]:
            
            for j in range(i+i,n+1,i):
                
                result[j] = False
    
    return result

n = int(stdin.readline())

prime_list = get_prime(n)

answer = 1

for i in range(2,n+1):
    
    if prime_list[i]:
        
        answer *= pow(i,int(math.log(n,i)),2**32)
        answer %= (2**32)

print(answer)

 

그래서 금단의 기술 비트마스크를 이용한 에라토스테네스의 체를 공부해서 해결했다

 

https://deepdata.tistory.com/822

 

비트마스크를 이용한 에라토스테네스의 체 배우기

1. 에라토스테네스의 체 기본형태 n이하의 소수를 구하는 알고리즘 n+1개의 1을 가지는 리스트로 초기화하고, 2부터 n의 제곱근까지 순회하여 해당 수의 배수를 모두 지우고 나면 소수만 남는다는

deepdata.tistory.com

 

import math
from sys import stdin

def get_prime_bitmask(n):
    
    result = [255]*((n+1)//8+1)

    result[0 >> 3] &= ~(1 << (0 & 7))
    result[1 >> 3] &= ~(1 << (1 & 7))

    for i in range(2,int((n+1)**(1/2))+1):
        
        if result[i >> 3] & (1 << (i & 7)):
            
            for j in range(i*i,n+1,i):
                
                result[j >> 3] &= ~(1 << (j & 7))
    
    return result

n = int(stdin.readline())

prime = get_prime_bitmask(n)

answer = 1

for i in range(2,n+1):
    
    if prime[i >> 3] & (1 << (i & 7)):
        
        answer *= pow(i,int(math.log(n,i)),2**32)
        answer %= (2**32)

print(answer)

 

c++은 그냥 해도 통과하는것 같던데

 

파이썬의 한계를 느끼기 시작..

728x90