Namey McNamo
Namey McNamo

Reputation: 45

How to Iterate through arithmetic operators across a static, excecutable formula in Python?

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

Answers (4)

Mikhail Churbanov
Mikhail Churbanov

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

  • After Step 1 ( first * evaluated ): 2 + 3 * 4
  • After Step 2 ( second * evaluated ): 2 + 12
  • After Step 3 ( + evaluated ): 14

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

gold_cy
gold_cy

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

chepner
chepner

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

Błotosmętek
Błotosmętek

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

Related Questions