问题
I came across this puzzle from centerofmath.org via Twitter:
There is a ten-digit number where the leftmost digit is also the number of zeros in the number, the second leftmost digit is the number of ones and so forth until the last digit (or rightmost digit) is the number of nines in that number. What is this number and is it unique?
I wrote a program in Python to solve the puzzle:
def yielder(exponent):
i = 10**exponent
while i < (10**(exponent+1))-1:
yield i
i += 1
def is_solution(num):
l = list(num)
#l.reverse()
is_sol = True
for i, e in enumerate(l):
if int(e) != l.count(str(i)):
is_sol = False
break
return is_sol
if __name__=="__main__":
import sys
ofile = open('solution.txt', 'w')
j = int(sys.argv[1])
print("checking between {0} and {1}".format(10**j, (10**(j+1))-1))
ofile.write("Solutions between {0} and {1}:\n".format(10**j, (10**(j+1))-1))
for x in yielder(j):
if is_solution(str(x)):
ofile.write('\t{0}\n'.format(x))
print("solution is {0}".format(x))
ofile.close()
And the solution I got is 6210001000
But the problem is it took several hours to solve. I want to know if there is some technique I could have used to make it faster
BTW these are the numbers between 10 9,999,999,999 that are solutions
Solutions between 10 and 99:
Solutions between 100 and 999:
Solutions between 1000 and 9999:
1210
2020
Solutions between 10000 and 99999:
21200
Solutions between 100000 and 999999:
Solutions between 1000000 and 9999999:
3211000
Solutions between 10000000 and 99999999:
42101000
Solutions between 100000000 and 999999999:
521001000
Solutions between 1000000000 and 9999999999:
6210001000
回答1:
It's much faster if you construct the number other than search for it.
def to_the_number(n):
digits=list(map(int,n))
assert(len(digits))==10
done = False
while not done:
done = True
for i in range(10):
if digits[i]!=digits.count(i):
digits[i]=digits.count(i)
print(digits)
done = False
return ''.join(map(str, digits))
And start from any number:
>>> to_the_number('1234567890')
[1, 1, 3, 4, 5, 6, 7, 8, 9, 0]
[1, 1, 0, 4, 5, 6, 7, 8, 9, 0]
[1, 1, 0, 0, 5, 6, 7, 8, 9, 0]
[1, 1, 0, 0, 0, 6, 7, 8, 9, 0]
[1, 1, 0, 0, 0, 0, 7, 8, 9, 0]
[1, 1, 0, 0, 0, 0, 0, 8, 9, 0]
[1, 1, 0, 0, 0, 0, 0, 0, 9, 0]
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[8, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[8, 1, 0, 0, 0, 0, 0, 0, 1, 0]
[7, 1, 0, 0, 0, 0, 0, 0, 1, 0]
[7, 2, 0, 0, 0, 0, 0, 0, 1, 0]
[7, 2, 1, 0, 0, 0, 0, 0, 1, 0]
[7, 2, 1, 0, 0, 0, 0, 1, 1, 0]
[7, 2, 1, 0, 0, 0, 0, 1, 0, 0]
[6, 2, 1, 0, 0, 0, 0, 1, 0, 0]
[6, 2, 1, 0, 0, 0, 1, 1, 0, 0]
[6, 2, 1, 0, 0, 0, 1, 0, 0, 0]
'6210001000'
回答2:
You can cut out massive amounts of number checks by using a bit more logic. For example, you know that the sum of the individual digits must be 10, otherwise the number has too many digits, so try taking advantage of that.
You could, for example, change your yielder to only generate numbers for which the condition sum(int(n) for n in num) == len(num)
is satisfied, then you don't need to check with an expensive loop on numbers which you already know can't make sense.
来源:https://stackoverflow.com/questions/12704061/how-can-i-speed-up-my-solution-to-this-self-describing-number-puzzle