Reputation: 676
I know that there are other questions like mine but the only problem is that they get all the combinations for all of the varibales in list but I want it so that the user enters the number that the want and the numbers they need to make the desired number. This is the code that I have:
numbers = []
operators = ['+', '*', '-', '/']
desire = int(input("Enter the number you want: "))
num1 = int(input("Enter First number: "))
num2 = int(input("Enter Second number: "))
num3 = int(input("Enter Third number: "))
num4 = int(input("Enter Fourth number: "))
numbers.append(num1)
numbers.append(num2)
numbers.append(num3)
numbers.append(num4)
But I have no idea how to expand on this
This is an example on what the code should do:
Say that the number they want made is 24
and
Say the numbers that they enter are 1, 9, 8, 2
output should be this:
9 - 1 + 8 * 2 = 24
etc...
All possible solutions need to be listed
All suggestions will be greatly appriciated
Upvotes: 4
Views: 5276
Reputation: 42133
You could use permutations from the itertools module to arrange numbers and operators in all possible ways into a string formula. Then use eval() to compute the result.
For example:
from itertools import permutations
numbers = ["1","9","8","2"]
target = 24
operators = ["+","-","*","/"]
for values in permutations(numbers,len(numbers)):
for oper in permutations(operators,len(numbers)-1):
formula = "".join(o+v for o,v in zip([""]+list(oper),values))
if eval(formula) == target: print(formula,"=",target)
[UPDATE1] If you are allowed to use the same operator more than once (as suggested by your comment on 1+1+1*8=24), you will need to use combinations_with_replacement to generate more operator patterns:
from itertools import permutations,combinations_with_replacement
numbers = ["1","1","1","8"]
target = 10
operators = ["+","-","*","/"]
seen = set()
for values in permutations(numbers,len(numbers)):
for operCombo in combinations_with_replacement(operators,len(numbers)-1):
for oper in permutations(operCombo,len(numbers)-1):
formula = "".join(o+v for o,v in zip([""]+list(oper),values))
if formula not in seen and eval(formula) == target:
print(formula,"=",target)
seen.add(formula)
Essentially, this only differs from the previous example by the insertion of the for operCombo in ...
loop.
Note: The combinations will generate formulas that look exactly the same so you will want to avoid printing solutions that have already been seen (as I did here). Duplications would also occur in the previous example if any numbers were repeated in the input.
Also note that in order for 9-1+8*2 to result in 24, the multiplication must be performed before additions and subtractions (i.e. under precedence rules) otherwise 9-1+8*2=32. You would need to support parentheses to cover different orders of operation.
[UPDATE2] Supporting parentheses is a bit more involved depending on how many numbers you want to allow. For 4 numbers, there are 11 patterns:
If you have more than 4 numbers there will be more patterns of parentheses grouping.
Here's an example (for 4 numbers):
from itertools import permutations,combinations_with_replacement
numbers = ["9","8","1","2"]
target = 24
operators = ["+","-","*","/"]
groups = ['X+X+X+X', 'X+X+(X+X)', 'X+(X+X)+X', '(X+X+X)+X', '(X+X)+X+X', 'X+(X+X+X)', '((X+X)+X)+X', 'X+(X+(X+X))', 'X+((X+X)+X)', '(X+X)+(X+X)', '(X+(X+X))+X']
seen = set()
for values in permutations(numbers,len(numbers)):
for operCombo in combinations_with_replacement(operators,len(numbers)-1):
for oper in permutations(operCombo,len(numbers)-1):
formulaKey = "".join(oper+values)
if formulaKey in seen: continue # ignore variations on parentheses alone
for pattern in groups:
formula = "".join(o+p for o,p in zip([""]+list(oper), pattern.split("+")))
formula = "".join(v+p for v,p in zip([""]+list(values),formula.split("X")))
try:
if eval(formula) == target:
print(formula,"=",target)
seen.add(formulaKey)
break
except: pass
Groupings could result in divisions by zero, so a try:except block had to be added.
This produces the following result:
9*8/(1+2) = 24
9+8*2-1 = 24
9*8/(2+1) = 24
9-1+8*2 = 24
9-(1-8*2) = 24
9-1+2*8 = 24
(9-1)*2+8 = 24
9/(1+2)*8 = 24
9/((1+2)/8) = 24
9-(1-2*8) = 24
9+2*8-1 = 24
9/(2+1)*8 = 24
9/((2+1)/8) = 24
8+(9-1)*2 = 24
8*9/(1+2) = 24
8*9/(2+1) = 24
8-(1-9)*2 = 24
8/(1+2)*9 = 24
8/((1+2)/9) = 24
8+2*(9-1) = 24
8*2+9-1 = 24
8*2-1+9 = 24
8/(2+1)*9 = 24
8/((2+1)/9) = 24
8-2*(1-9) = 24
8*2-(1-9) = 24
2*(9-1)+8 = 24
2*8+9-1 = 24
2*8-1+9 = 24
2*8-(1-9) = 24
To generate the parentheses grouping patterns for more numbers, you can use this function:
from itertools import product
import re
def groupPatterns(count,pattern=None):
arr = pattern or "X"*count
if len(arr) < 2 : return [arr]
result = []
for mid in range(1,len(arr)):
leftPattern = groupPatterns(count,arr[:mid])
rightPattern = groupPatterns(count,arr[mid:])
for left,right in product(leftPattern,rightPattern):
result += [left + right]
if len(left) > 1 : result += ["(" + left + ")" + right]
if len(right) > 1 : result += [left + "(" + right + ")"]
if len(left) > 1 and len(right) > 1:
result += ["(" + left + ")(" + right + ")"]
if pattern: return result # recursion
patterns = [] # final, add "+" between X value placeholders or groups
for pat in sorted(set(result),key=lambda x:len(x)):
pat = re.sub("X(?=X)", r"X+", pat) # XX --> X+X
pat = re.sub("X\(", r"X+(", pat) # X( --> X+(
pat = re.sub("\)X", r")+X", pat) # )X --> )+X
pat = re.sub("\)\(", r")+(", pat) # )( --> )+(
patterns.append(pat)
return patterns
And then replace groups = ["X+X+X+X",...
with groups = groupPatterns(len(numbers))
in the previous example.
OR, create a completely generic function for any number of values, with or without grouping and operator reuse:
from itertools import permutations,combinations_with_replacement
def numbersToTarget(numbers,target,reuseOper=True,allowGroups=True,operators=["+","-","*","/"]):
groups = groupPatterns(len(numbers)) if allowGroups else [ "+".join("X"*len(numbers)) ]
seen = set()
for values in permutations(numbers,len(numbers)):
for operCombo in combinations_with_replacement(operators,len(numbers)-1) if reuseOper else [operators]:
for opers in permutations(operCombo,len(numbers)-1):
formulaKey = str(opers)+str(values)
if formulaKey in seen: continue # ignore variations on parentheses alone
for pattern in groups:
formula = "".join(o+p for o,p in zip([""]+list(opers), pattern.split("+")))
formula = "".join(str(v)+p for v,p in zip([""]+list(values),formula.split("X")))
try:
if eval(formula) == target:
seen.add(formulaKey)
yield formula
break
except: pass
for formula in numbersToTarget([9,8,1,2],24):
print("24 =",formula)
for formula in numbersToTarget([9,8,1,2,5],0,allowGroups=False):
print("0 =",formula)
Upvotes: 5
Reputation: 5774
Here is what I came up with using eval()
to mathematically evaluate the string of math operators (note this is not a very secure method and malicious users could potentially hack your program through it. If you are deploying then maybe look at Evaluating a mathematical expression in a string)
numbers = []
operators = ['+', '*', '-', '/']
desire = int(input("Enter the number you want: "))
num1 = input("Enter First number: ")
num2 = input("Enter Second number: ")
num3 = input("Enter Third number: ")
num4 = input("Enter Fourth number: ")
numbers.append(num1)
numbers.append(num2)
numbers.append(num3)
numbers.append(num4)
for operator1 in operators:
for operator2 in operators:
for operator3 in operators:
problem = numbers[0] + operator1 + numbers[1] + operator2 + numbers[2] + operator3 + numbers[3]
result = int(eval(problem))
if result == desire:
print("{} = {}".format(problem, result))
My first simple test
Enter the number you want: 40
Enter First number: 10
Enter Second number: 10
Enter Third number: 10
Enter Fourth number: 10
Yields
10+10+10+10 = 40
A more complex test
Enter the number you want: 18
Enter First number: 6
Enter Second number: 3
Enter Third number: 4
Enter Fourth number: 4
Yeilds:
6*3+4-4 = 18
6*3*4/4 = 18
6*3-4+4 = 18
6*3/4*4 = 18
6/3+4*4 = 18
It should be noted though that this solution does not consider the various orders for your numbers. I'll see if I can craft something more devious
UPDATE
I've crafted a way to consider all permutations of numbers as well
def make_order_combinations():
number_orders = []
for i in range(4):
for j in range(4):
for k in range(4):
for z in range(4):
if i != j and i != k and i != z and j != k and j != z and k != z:
number_orders.append((i, j, k, z))
return number_orders
def solve_given_number_order(number_order):
for operator1 in operators:
for operator2 in operators:
for operator3 in operators:
problem = numbers[number_order[0]] + operator1 + numbers[number_order[1]] + operator2 + numbers[number_order[2]] + operator3 + numbers[number_order[3]]
# print(problem)
result = eval(problem)
# print(result)
if result == desire:
print("{} = {}".format(problem, result))
numbers = []
operators = ['+', '*', '-', '/']
desire = int(input("Enter the number you want: "))
num1 = input("Enter First number: ")
num2 = input("Enter Second number: ")
num3 = input("Enter Third number: ")
num4 = input("Enter Fourth number: ")
numbers.append(num1)
numbers.append(num2)
numbers.append(num3)
numbers.append(num4)
list_of_orders = make_order_combinations()
for order in list_of_orders:
solve_given_number_order(order)
Now the test
Enter the number you want: 67
Enter First number: 15
Enter Second number: 4
Enter Third number: 7
Enter Fourth number: 1
Yields
15*4+7*1 = 67
15*4+7/1 = 67.0
15*4+1*7 = 67
15*4*1+7 = 67
15*4/1+7 = 67.0
15*1*4+7 = 67
15/1*4+7 = 67.0
4*15+7*1 = 67
4*15+7/1 = 67.0
4*15+1*7 = 67
4*15*1+7 = 67
4*15/1+7 = 67.0
4*1*15+7 = 67
4/1*15+7 = 67.0
7+15*4*1 = 67
7+15*4/1 = 67.0
7+15*1*4 = 67
7+15/1*4 = 67.0
7+4*15*1 = 67
7+4*15/1 = 67.0
7+4*1*15 = 67
7+4/1*15 = 67.0
7+1*15*4 = 67
7*1+15*4 = 67
7/1+15*4 = 67.0
7+1*4*15 = 67
7*1+4*15 = 67
7/1+4*15 = 67.0
1*15*4+7 = 67
1*4*15+7 = 67
1*7+15*4 = 67
1*7+4*15 = 67
Where you can see it does consider all possible rearrangement of the numbers as well. Order of operations does still apply though and therefore the output:
1*7+4*15 = 67
Should be read as (1*7)+(4*15) = 67
Upvotes: 2
Reputation: 171
You can try with the permutations module inside itertools
from itertools import permutations, combinations
numbers = ""
solutions = []
operators = "+*-/"
desire = int(input("Enter the number you want: "))
num1 = input("Enter First number: ")
num2 = input("Enter Second number: ")
num3 = input("Enter Third number: ")
num4 = input("Enter Fourth number: ")
#concatenate the input
numbers = num1 + num2 + num3 + num4
#generate all possible permutations of this characters
num_permutations = [p for p in permutations(numbers)]
op_combinations = [p for p in combinations(operators,3)]
for n_perm in num_permutations:
for op_perm in op_combinations:
cur_expression = ""
for i in range(3):
cur_expression += n_perm[i] + op_perm[i]
cur_expression += n_perm[3]
tmp_solution = eval(cur_expression)
if desire == tmp_solution:
solutions.append(tmp_solution)
Upvotes: 1
Reputation: 46901
this is really not well tested (and probably does too much work) but may get you started:
from operator import mul, add, sub, truediv
from itertools import permutations, combinations_with_replacement
operators = (mul, add, sub, truediv)
desire = 24
numbers = [1, 9, 8, 2]
OP2SYM = {mul: '*', add: '+', sub: '-', truediv: '/'}
for op0, op1, op2 in combinations_with_replacement((mul, add, sub, truediv), 3):
for n0, n1, n2, n3 in permutations(numbers, 4):
# print(op0, op1, op2)
# print(n0, n1, n2, n3)
if op0(n0, op1(n1, op2(n2, n3))) == desire:
print('{} {} ({} {} ({} {} {}))'.format(
n0, OP2SYM[op0], n1, OP2SYM[op1], n2, OP2SYM[op2], n3))
if op0(op1(n0, n1), op2(n2, n3)) == desire:
print('({} {} {}) {} ({} {} {})'.format(
n0, OP2SYM[op0], n1, OP2SYM[op1], n2, OP2SYM[op2], n3))
if op2(op1(op0(n0, n1), n2), n3) == desire:
print('(({} {} {}) {} {}) {} {}'.format(
n0, OP2SYM[op0], n1, OP2SYM[op1], n2, OP2SYM[op2], n3))
it outputs
((8 * 2) + 9) - 1
((2 * 8) + 9) - 1
a simpler idea would be to constuct the strings of the form '6*3-4+4'
and use ast.literal_eval
to evaluate them
Upvotes: 1