카테고리 없음

(정수론/파이썬) 소인수분해 구하기

미친토끼 2021. 9. 2. 00:00

'친절한 수론 길라잡이' 83페이지 연습문제 11.4

 

11.4 오일러 함수의 함수값인 phi(n)을 계산하는 프로그램을 만들어라. 1과 n사이의 모든 수들에 대해 n과 서로소인 수 a를 찾는 방법 말고, n의 소인수분해를 이용하는 방법을 사용해 phi(n)을 계산하라.

 

# 소수 판정 함수. 소수면 True, 아니면 False 반환
def isPrime(num):
  root_val = round(num ** 0.5) # 제곱근 값을 구한다
  for i in range(2, root_val+1): # 제곱근값+1 까지만 약수를 조사해도 충분하다
    if num % i == 0:  # 중간에 나눠지면 소수가 아니다
      return False
  return True

# 약수 구하는 함수
def getDivisor(num):
  divisors = []
  root_num = round(num ** 0.5)
  
  for i in range(1, root_num+1):
    if num % i == 0:
      divisors.append(i)
      divisors.append(num // i)
  return sorted(list(set(divisors)))


# 인수분해를 해서 결과를 딕셔너리로 반환
# 가령 인수분해 결과가 2^3 * 3^2 * 7 이라면
# {2:3, 3:2, 7:1} 반환
def factorize(num):
  divisor_primes = [] # 분해된 인수를 차곡차곡 저장하는 리스트
  factorize_dict = {} # 리스트의 원소 갯수를 세어 넣는 용도
  
  # num이 소수라면 (num, 1)을 딕셔너리에 넣어 반환한다.
  # 소수 판정은 숫자의 제곱근까지만 조사하면 되므로 간단하다.
  if isPrime(num):
      factorize_dict[num] = 1
      return factorize_dict
  
  # 인수분해 루틴은 소수 판정 루틴과는 조금 다르게 접근할 필요가 있다.
  i = 2
  while True:
      # i가 소수인지 검사해서 소수라면 num을 i로 나눈다
      # i로 나눠서 나머지가 0이라면, 인수분해가 한번 된 것이다.
      if isPrime(i) and num % i == 0:
          divisor_primes.append(i) # 나누는 수를 저장 (가령, num이 12라면 i = 2)
          num = num // i   # 몫, 가령 24//2 = 12
          #print(f"{i}, {num}")
          # 12는 소수가 아니므로 반복문 계속. 소수가 나오면 그 소수를 저장하고 반복문 종료.
          if isPrime(num):
              divisor_primes.append(num)
              break  # return divisor_primes
          i = 2 # 인수분해를 한번 했으므로 숫자를 2로 다시 나눠보기로 설정
      else:
          i += 1 # 인수분해가 되지 않으면 젯수(나누는 수)를 1증가
  print(divisor_primes)
  
  for n in divisor_primes:
      if n in factorize_dict:
          factorize_dict[n] += 1   
      else:  
          factorize_dict[n] = 1      

  return factorize_dict


print(factorize(24))
print(factorize(1512)) # 2^3* 3^3 * 7
print(factorize(3600)) # 2^4* 3^2 * 5^2
print(factorize(2147483647)) # 소수

num = 3600
result = factorize(num) # 2^4* 3^2 * 5^2
length = len(result) 
print (f"{num} = ", end="")
for i, (k, v) in enumerate(result.items()):
    print(f"{k}^{v}", end="")
    if i == length - 1: break # 마지막 항목에는 * 표시를 덧붙이지 않음
    print(" * ", end="")

출력:

[2, 2, 2, 3]
{2: 3, 3: 1}
[2, 2, 2, 3, 3, 3, 7]
{2: 3, 3: 3, 7: 1}
[2, 2, 2, 2, 3, 3, 5, 5]
{2: 4, 3: 2, 5: 2}
{}
[2, 2, 2, 2, 3, 3, 5, 5]
3600 = 2^4 * 3^2 * 5^2