Friday, May 15, 2015

Project Euler #34 - digit factorials

프로젝트 오일러 34번
145 = 1! + 4! + 5! 이다. 이런 성질을 만족하는 숫자를 모두 찾아서 더하면? 단 1!=1, 2!=2는 뻔한 경우로, 제외한다.

9! 이 약 36만 정도니까, 최대 7자리 숫자까지 가능하다. 그런데 9999999 != 9! * 7 이고.. 종이에 조금 끄적거려 보면 7자리가 되는 경우는 없다.

1자리~6자리의 모든 수에 대해서 위의 조건을 만족하는지 보려면 대략 천만번 loop를 돌아야 한다. 반대로 [1,1,2,6,24,...] 숫자들을 6개까지 더해서 위의 조건을 만족하는지 보는 게 더 효율적이다. 4000개 정도의 숫자만 체크하면 된다.

f = [0,1]
for i in range(1,10):
    f.append(f[-1]*i)

a = {x1+x2+x3+x4+x5+x6 for x1 in f for x2 in f for x3 in f for x4 in f for x5 in f for x6 in f if x1<=x2<=x3<=x4<=x5<=x6}

sm = 0
for n in [n for n in a if n >2]:
    if n == sum([f[int(x)+1] for x in str(n)]): sm += n

print sm

예쁘지는 않지만.. f[x+1]에는 x! 에 해당하는 값이 들어가 있도록 했다.
a에는 최대 6자리 모든 조합이 들어가도록 했다. if 뒤에 "and x1+x2+...+x6 > 2"를 추가하고 싶지만 한 줄이 너무 길어져서... --;
이렇게 하면 0.2초 걸린다. 위에서 "종이에 조금 끄적거려 보면"을 생략하고 7자리까지 조건을 체크하면 더 오래 걸린다.

시간을 더 줄이는 방법! 아래의 코드를 실행하면 7자리까지 모두 체크하면서 시간은 0.03초로 줄어든다. 위의 a = ... 에 해당하는 코드를 길게 풀어 쓴 건데, 보기에는 지저분해도 효과는 좋다. 31번 동전 조합 문제도 이렇게 풀면 실행 시간이 훨씬 줄어든다.

f = [1]
for i in range(1,10):
    f.append(f[-1]*i)

rs = set()
for n9 in range(7):
    s9 = f[9] * n9
    ub8 = 7 - n9
    for n8 in range(ub8):
        s8 = s9 + f[8] * n8
        ub7 = ub8 - n8
        for n7 in range(ub7):
            s7 = s8 + f[7] * n7
            ub6 = ub7 - n7
            for n6 in range(ub6):
                s6 = s7 + f[6] * n6
                ub5 = ub6 - n6
                for n5 in range(ub5):
                    s5 = s6 + f[5] * n5
                    ub4 = ub5 - n5
                    for n4 in range(ub4):
                        s4 = s5 + f[4] * n4
                        ub3 = ub4 - n4
                        for n3 in range(ub3):
                            s3 = s4 + f[3] * n3
                            ub2 = ub3 - n3
                            for n2 in range(ub2):
                                s2 = s3 + f[2] * n2
                                ub1 = ub2 - n2
                                for n1 in range(ub1):
                                    s1 = s2 + f[1] * n1
                                    if s1 > 2 and s1 == sum([f[int(x)] for x in str(s1)]):
                                        rs.add(s1)

print sum(rs)



참고로.. 9!*7까지의 모든 자연수에 대해 위의 조건을 만족하는지 체크해 보면 15초 정도 걸린다. 좋지 않은 방법

f = [1]
for i in range(1,10):
    f.append(f[-1]*i)

sm = 0

for n in range(10,7*f[9]):
    if n == sum([f[int(x)] for x in str(n)]): sm += n

print sm

No comments: