이번에도 시간 차이를 보고 싶어 2가지 방법으로 풀어보았다.
처음에는 N보다 작은 소수를 리스트에 집어넣고 소수 안에서 계속 나누면 된다고 생각했는데 시간 초과가 떴다.. 오히려 소수들로만 나누기 때문에 시간 초과를 막을 거라고 생각했는데..
N = int(input())
lst = []
if N != 1:
for n in range(2, N+1):
is_prime = True
for i in range(2, n//2 + 1):
if n % i == 0:
is_prime = False
break
if is_prime:
lst.append(n)
while N != 1:
for i in lst:
while N % i == 0:
print(i)
N //= i
역시나 소수의 리스트를 만드는 데 시간이 오래 걸리기 때문이다.
1. 2 ~ N까지 모든 수를 돌면서 소수 판별 → 소수만 리스트에 저장
2. 소수 판별은 n//2까지 나눠봄 → 최악의 경우 O(N²)에 가까운 연산
3. 그 다음에 소인수분해 시작 → 소수 리스트 길이가 커서 루프도 많이 돔
최악의 경우 O(N²)의 시간 복잡도가 발생한다.
하기 때문이다.
그래서 사실 n//2까지가 아니라 √N까지만 도는 게 더 최적화된 방식이다.
N = int(input())
if N != 1:
for i in range(2, N+1):
while N % i == 0:
print(i)
N /= i
if N == 1:
break
N = int(input())
i = 2
while N != 1:
if N % i == 0:
print(i)
N /= i
else:
i += 1

나는 1번 방법이 for문을 한 번 더 쓰기 때문에 시간이 더 오래 걸릴 거라고 생각했는데 웬걸.. 2가 훨씬 오래 걸렸다.
아무리 봐도 똑같은 횟수만큼 루프를 도는 것처럼 보였는데.. gpt를 계속 괴롭힌 결과.. 횟수에서의 차이점이 아니라 for과 while에서의 i+=1 계산의 차이점이라고 한다. i+=1이라는 연산을 위해 새 객체를 생성하고 바인딩함으로써 루프 오버헤드가 커진다고 한다.

gpt가 알려준 대로 제곱근을 사용해 봤는데.. 엥? 완전오나전 시간이 단축되었다. 기억하자 소수는 제곱근..
import math
N = int(input())
if N != 1:
i = 2
while i <= math.isqrt(N):
while N % i == 0:
print(i)
N //= i
i += 1
if N > 1:
print(N)