forked from jsundram/euler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
092 - Square Chain.py
98 lines (77 loc) · 2.49 KB
/
092 - Square Chain.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python
# encoding: utf-8
"""
092 - Square Chain.py
Created by Jason Sundram on 2010-08-06.
Copyright (c) 2010. All rights reserved.
Problem 92
01 April 2005
A number chain is created by continuously adding the square of the digits in a number to form a new number until it has been seen before.
For example,
44 -> 32 -> 13 -> 10 -> 1 -> 1
85 -> 89 -> 145 -> 42 -> 20 -> 4 -> 16 -> 37 -> 58 -> 89
Therefore any chain that arrives at 1 or 89 will become stuck in an endless loop. What is most amazing is that EVERY starting number will eventually arrive at 1 or 89.
How many starting numbers below ten million will arrive at 89?
"""
from timed import timed
from utils import combinations_with_replacement, factorial
import operator
from collections import defaultdict
import psyco; psyco.full()
def get_next(n):
return sum([int(i)*int(i) for i in str(n)])
def num_permutations(l):
"""returns the number of permutations possible from iterable l. Copied from 74, with fixes"""
counts = defaultdict(int)
for i in l:
counts[i] += 1
# Can't have leading digit 0
invalid = 0
if 0 in counts:
n = list(l)
n.remove(0) # just get rid of one
invalid = num_permutations(['A' if i == 0 else i for i in n])
return factorial(len(l)) / reduce(operator.mul, map(factorial, counts.values()), 1) - invalid
@timed
def faster():
"""765ms"""
table = {0:0, 1:1, 89:89}
for i in xrange(2, 600):
n = i
while i != 1 and i != 89:
i = get_next(i)
table[n] = i
T = {}
count = 0
for i in xrange(1, 8):
for c in combinations_with_replacement(range(10), i):
s = ''.join(map(str, sorted(c)))
n = get_next(int(s))
if table[n] == 89:
count += num_permutations(c)
return count
@timed
def original_solution():
""" original_solution took 7846.661 ms (yikes, but first try took 60s)
The answer (original) is: 8581146
"""
table = {1:1, 89:89}
for i in xrange(2, 600):
n = i
while i != 1 and i != 89:
i = get_next(i)
table[n] = i
N = 10**7
count = 0
pows = dict([(str(i), i*i) for i in xrange(10)])
for n in xrange(2, N):
next = 0
for i in str(n):
next += pows[i]
if table[next] == 89:
count += 1
return count
def main():
print 'The answer (original) is: %d' % faster()
if __name__ == '__main__':
main()