오일러 프로젝트 34

숫자 145에는 신기한 성질이 있습니다. 각 자릿수의 팩토리얼(계승)을 더하면 1! + 4! + 5! = 1 + 24 + 120 = 145 처럼 자기 자신이 됩니다. 이렇게 각 자릿수의 팩토리얼을 더하면 자기 자신이 되는 모든 수의 합을 구하세요. 단, 1! = 1 과 2! = 2 의 경우는 덧셈이 아니므로 제외합니다.

http://euler.synap.co.kr/prob_detail.php?id=34

접근

문제의 조건, 각 자리의 팩토리얼을 합한 값이 자기 자신이 되는 조건을 통해서 검사할 수 있는 범위를 어디까지 설정해야 할지를 먼저 결정해야 한다. 한자리 수의 팩토리얼 중에 가장 큰 값은 9!로 이 값은 362,880이다. 9! x 6 = 2,177,280 이고 9! x 7 = 2,540,160이다. 즉, 약 254만이상 부터는 각 자리 수의 팩토리얼을 더해도 자기 자신이 되기에는 턱없이 부족한 숫자만 나올 것이다.

실제로는 250만 이하의 수 중에서 각 자리 숫자의 계승의 합이 가장 크려면 2,499,999 인데, 이 수의 각 자리 숫자의 계승의 합은 1,814,426 에 그친다. 다시 1,814,426 이하에서 계승의 합이 가장 큰 수를 만들어보면 1,799,999 인데, 이 수의 각 자리 계승의 합은 1,819,441로 원래 값보다 크다. 따라서 검사할 수 있는 가장 큰 수의 범위는 1,799,999로 잡을 수 있다.

이 값은 2백만에 가까운 수이기 때문에, 파이썬이라면 루프를 돌기에 상당히 부담스러운 범위이기는 하다. 따라서 각 자리 숫자의 팩토리얼의 합을 구하는 함수가 얼마나 빠르게 작동하느냐가 관건이 될 수 있다. 우선, 다음과 같이 팩토리얼을 계산하는 함수와, 각 자리 숫자의 계승의 합을 구하는 함수를 작성할 수 있을 것이다.

from functools import reduce

# 팩토리얼을 계산하는 함수 준비
_prod = lambda xs: reudce(lambda x, y: x * y, xs, 1)
_fac = lambda n: 1 if n < 2 else _prod(range(1, n + 1))

def process(n: int) -> int:
  xs = map(int, str(n))
  return sum(_fac(x) for x in xs)

이 함수를 180만번 모두 다른 값에 대해서 호출해야 하는데, 이 함수에는 정수를 문자열로 바꾸고 다시 문자를 정수로 바꾸며, 누적곱을 계산하는 과정이 들어가기 때문에 그 자체로는 간단하지만 상대적으로 비싼 연산을 많이 사용하고 있다.

우선 주목할 것은 팩토리얼 연산인데, 실제로 각 자리 숫자의 팩토리얼 값만 구하면 되기 때문에 팩토리얼은 0~9 사이의 숫자에 대해서만 계산할 것이다. 그렇다면 매번 계산하기 보다는 미리 계산한 값을 사용하는 것이 훨씬 빠를 것이다. (참고로 팩토리얼은 누적곱이므로, 0! = 1 로 간주할 수 있다.)

fs = (1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880)
fs[n]

그리고 각 자리 숫자를 구하는 것은 10으로 반복해서 나눈 나머지들을 사용하면 된다. 따라서 각 자리 계승의 합을 구하는 함수는 아래와 같이 작성된다.

def process(n: int) -> int:
  fs = (1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880)
  s = 0
  while n > 0:
    n, r = divmod(n, 10)
    s += fs[r]
  return s

%%time
s = 0
for a in range(10, 180_0000):
  if process(a) ==  a:
    s += a
print(s)

기타

사실 문제에서 제외한 1과 2를 포함하여 이 조건을 만족하는 수는 딱 4개 밖에 없다. 이러한 수들을 ‘팩토리올(factoriols)’이라고 부른다고 한다.

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop