问题
I came across a fun math problem yesterday and have it solved, but with the code I wrote, I had to do a keyboard interrupt or it would run forever, lol. So I changed it to have an end condition, but now it only prints 1 solution and stops.
The problem goes like this: "You have the numbers 123456789, in that order. Between each number, you must insert either nothing, a plus sign, or a multiplication sign, so that the resulting expression equals 2002. Write a program that prints all solutions. (There are two.)"
import random
def try1(param):
global solved
opers = ['+', '*', '']
hotpotato = ('%s'.join(param) % (random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
)
)
if eval(hotpotato) == 2002:
solved += 1
print "Solution:", hotpotato, "= 2002 :-)"
else:
pass
solved = 0
while solved == 0:
try1('123456789')
This code prints the first solution it encounters and stops. Can anyone tell me how to get it to print both solutions before it stops?
回答1:
Store your solutions in a set:
solutions = set([])
Each time a solution is found, update the set:
solutions.append(solution)
Sets are nice because there don't store duplicates:
>>> len(set([1, 1, 1, 1, 1, 1, 1]))
1
So just loop until the set's size is greater than one:
while len(solved) < 2:
try1('123456789')
Also, you can shorten this code:
hotpotato = ('%s'.join(param) % (random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
)
)
To this:
hotpotato = ('%s'.join(param) % (random.choice(opers) for i in range(8))))
回答2:
Don't use random, enumerate all possible operator combinations (well, you can cut the search space a bit, if the first couple of numbers the result is larger than 2002, there is no way the result is going to be smaller). itertools
is your friend.
If you do that, your program will finish in no time.
If you know that there are exactly two solutions you can return the solution from try1
and do the loop till you collected two different solutions, but that's not really elegant, is it?
回答3:
The solution to your problem is, breaking when solved == 2.
But your code's real problem is using random. Using random in an algorithm is generally a bad idea. There is a possibility that your code lasts over a century.
There is much simplier and faster way using itertools:
import itertools
for s in itertools.product(("+", "*", ""), repeat=8):
z = itertools.izip_longest("123456789", s, fillvalue="")
e = "".join(itertools.chain.from_iterable(z))
if eval(e) == 2002:
print(e)
There is no need to break when two solutions found, because the code already completes in 0.2 seconds :).
回答4:
To receive both (all) solutions you need completely different approach to solve this problem. Check all permutations of inserted operations. How to calculate permutations are shown here: http://www.bearcave.com/random_hacks/permute.html
EDIT:
Example:
ops = ['+', '*']
def gen(ver, i):
if i == len(ver):
return
for op in ops:
ver = ver[:i] + op + ver[i:]
if eval(ver) == 2002:
yield ver
for j in range(i + 2, len(ver)):
for sol in gen(ver, j):
yield sol
ver = ver[:i] + ver[i+1:]
for sol in gen("123456789", 1):
print "solution:", sol
Output:
solution: 1*2+34*56+7+89
solution: 1*23+45*6*7+89
回答5:
For the record, I would encourage a different approach to search for the solutions, such as suggested in Andy T's answer or yi_H's answer. That said, this answer addresses the issue presented in the question.
The code you present will run until one answer is found and stop, since finding the first answer will make solved
not equal 0. Since you know there are 2 solutions, you can change your while loop condition:
while solved < 2:
try1('123456789')
In response to Mark's comment that this can lead to duplicated answers, here is code which will make sure you get different solutions:
import random
def try1(param):
global solved
try1.prev_soln = []
opers = ['+', '*', '']
hotpotato = ('%s'.join(param) % (random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
random.choice(opers),
)
)
if eval(hotpotato) == 2002:
if hotpotato not in try1.prev_soln:
solved += 1
try1.prev_soln.append(hotpotato)
print "Solution:", hotpotato, "= 2002 :-)"
else:
pass
solved = 0
while solved < 2:
try1('123456789')
Of course, this approach assumes 2 solutions. If you had an unknown number of solutions you would not know when to stop, thus my recommendation for another approach to finding the solutions.
来源:https://stackoverflow.com/questions/7506264/checking-for-unique-output-in-python