Reputation: 45
I'm trying to use itertools to iterate through mathematical operators.
Typically with an array
of [1, 2, 3]
, using combinations
I can get the results:
1
1,2
1,3
2,3
1,2,3
etc.
I want to use this on an array
of [1, 2, 3]
in such a way that:
1+2+3
1+2-3
1+2/3
1+2*3
1-2+3
1-2-3
1-2/3
1-2*3
...
arises and gives results of the equation.
How might I go about doing this?
Upvotes: 3
Views: 1716
Reputation: 4500
Not an elegant one (made on knee) but works, just to point out my logic. The idea is to reduce the list one by one in correct order. E.g.:
Data: 1 * 2 + 3 * 4
The code:
import operator
import itertools
data = [1.0, 2.0, 3.0, 4.0]
operators_1 = [operator.mul, operator.div] # this operators have priority over that below
operators_2 = [operator.add, operator.sub]
def processOps(formula, data, operators):
res_formula = list(formula)
result = list(data)
for op in formula:
if op not in operators: continue
i = res_formula.index(op)
result = result[:i] + [op(result[i], result[i + 1])] + result[i + 2:]
res_formula.remove(op)
if len(result) == 1:
break
return (res_formula, result)
for f in itertools.product(operators_1 + operators_2, repeat=len(data)-1):
result = list(data)
formula = list(f)
formula, result = processOps(formula, result, operators_1)
formula, result = processOps(formula, result, operators_2)
print f, result
UDP This updated logic handles cases like (1 * 2) + (3 / 4) correctly.
Upvotes: 1
Reputation: 14236
Here is how I would approach it:
import itertools
import operator
First make the list of all possible combinations:
funcs = [operator.add, operator.sub, operator.mul, operator.div]
combos = list(itertools.product(funcs, repeat=2))
>>[(<function operator.add>, <function operator.add>),
(<function operator.add>, <function operator.sub>),
(<function operator.add>, <function operator.mul>),
(<function operator.add>, <function operator.div>),
(<function operator.sub>, <function operator.add>),
(<function operator.sub>, <function operator.sub>),
(<function operator.sub>, <function operator.mul>),
(<function operator.sub>, <function operator.div>),
(<function operator.mul>, <function operator.add>),
(<function operator.mul>, <function operator.sub>),
(<function operator.mul>, <function operator.mul>),
(<function operator.mul>, <function operator.div>),
(<function operator.div>, <function operator.add>),
(<function operator.div>, <function operator.sub>),
(<function operator.div>, <function operator.mul>),
(<function operator.div>, <function operator.div>)]
Then we will iterate through this list solving every possible outcome:
for fn in combos:
print 'This combo {} yielded this result {}'.format(fn, fn[1](fn[0](*seq[:2]), seq[-1]))
This combo (<built-in function add>, <built-in function add>) yielded this result 6
This combo (<built-in function add>, <built-in function sub>) yielded this result 0
This combo (<built-in function add>, <built-in function mul>) yielded this result 9
This combo (<built-in function add>, <built-in function div>) yielded this result 1
This combo (<built-in function sub>, <built-in function add>) yielded this result 2
This combo (<built-in function sub>, <built-in function sub>) yielded this result -4
This combo (<built-in function sub>, <built-in function mul>) yielded this result -3
This combo (<built-in function sub>, <built-in function div>) yielded this result -1
This combo (<built-in function mul>, <built-in function add>) yielded this result 5
This combo (<built-in function mul>, <built-in function sub>) yielded this result -1
This combo (<built-in function mul>, <built-in function mul>) yielded this result 6
This combo (<built-in function mul>, <built-in function div>) yielded this result 0
This combo (<built-in function div>, <built-in function add>) yielded this result 3
This combo (<built-in function div>, <built-in function sub>) yielded this result -3
This combo (<built-in function div>, <built-in function mul>) yielded this result 0
This combo (<built-in function div>, <built-in function div>) yielded this result 0
Edit: Here is a way that follows the rules of operations
ops = ['+','-','*','/']
combos = list(itertools.product(ops, repeat=2))
for tup in list(itertools.product(combos, [seq])):
print 'These operations {} evaluate to this ---> {}'.format(tup[0],eval(''.join(*zip(seq[0],tup[0][0],seq[1],tup[0][1],seq[-1]))))
These operations ('+', '+') evaluate to this ---> 6
These operations ('+', '-') evaluate to this ---> 0
These operations ('+', '*') evaluate to this ---> 7
These operations ('+', '/') evaluate to this ---> 1
These operations ('-', '+') evaluate to this ---> 2
These operations ('-', '-') evaluate to this ---> -4
These operations ('-', '*') evaluate to this ---> -5
These operations ('-', '/') evaluate to this ---> 1
These operations ('*', '+') evaluate to this ---> 5
These operations ('*', '-') evaluate to this ---> -1
These operations ('*', '*') evaluate to this ---> 6
These operations ('*', '/') evaluate to this ---> 0
These operations ('/', '+') evaluate to this ---> 3
These operations ('/', '-') evaluate to this ---> -3
These operations ('/', '*') evaluate to this ---> 0
These operations ('/', '/') evaluate to this ---> 0
Upvotes: 1
Reputation: 532122
Use the corresponding functions in the operator
module and iterate over pairs of them.
import itertools
import operator
ops = [operator.add, operator.sub, operator.mul, operator.div]
for f1, f2 in itertools.product(*ops, repeat=2):
print f1(array[0], f2(array[1], array[2]))
Now, if array
can have an arbitrary length, it gets a bit tricker.
for operations in itertools.product(*ops, repeat=len(array)-1):
result = operations[0](array[0], array[1])
for op, operand in zip(operations[1:], array[2:]):
result = op(result, operand)
print(result)
The structure above avoids having to know the appropriate identity element for each operation.
If you want to obey precedence (as seems likely), you'll want to create an expression and evaluate it with eval
(standard warnings apply).
for ops in itertool.product("+", "-", "*", "/", repeat=len(array)-1):
expr = "%s%s%s" % (array[0], ops[0], array[1])
for op, operand in zip(ops[1:], array[2:]):
expr = "%s%s%s" % (expr, op, operand)
result = eval(expr)
I leave it as an exercise to expand this to produce parenthesized expressions like (1+2)*3
in addition to 1+2*3
.
Upvotes: 0
Reputation: 12927
A solution generalized to any number of operands, and preserving normal precedence of operators:
from itertools import product
operands = [1, 2, 3, 4]
operators = [ '+', '*', '-', '//' ] # change '//' to '/' for floating point division
for opers in product(operators, repeat=len(operands)-1):
formula = [ str(operands[0]) ]
for op, operand in zip(opers, operands[1:]):
formula.extend([op, str(operand)])
formula = ' '.join(formula)
print('{} = {}'.format(formula, eval(formula)))
Upvotes: 0