0

I was wondering if there is a way to permutate math operations since you can't place them in a list (to my understanding). I want to solve this problem

NUMBERMANIA: Calculate the number 1042 using numbers [1, 2, 9, 2, 59, 974] and basic arithmetic operations (+, -, *, /). Each of the numbers can be used only once.

Using code, my idea is to iterate through every possible permutation of the numbers, which is possible through itertools, then slot them into a function which does this.

[number1] [math operation] [number2] [math operation] ... [math operation] [number6] e.g 1 * 2 / 9 + 2 / 59 * 974

But the numbers don't have to be in this order, and the math operations don't have to be in that order or in that particular frequency in which it appears.

Is there a way to do something like this with math operations?

ThePyGuy
  • 17,779
  • 5
  • 18
  • 45
user268493
  • 65
  • 1
  • 4

2 Answers2

4

operator has such arithmetic functions available for use:

import operator as op

op.add(1, 2)  # returns 3

Which means you can create a list of them:

ops = [op.add, op.mul]
[f(3, 4) for f in ops]  # returns [7, 12]

Then, permutations can be done via: How to generate all permutations of a list?

Then iterate over the permutations, applying them in turn.

Kache
  • 15,647
  • 12
  • 51
  • 79
3

A clean way could be to have a mapping of operation characters to binary lambda functions:

operations = {
    '+': lambda a, b: a + b,
    '*': lambda a, b: a * b,
    ...
}

Usage:

print(operations['+'](2, 59))
print(operations['*'](2, 2))

Output:

61
2

For a more end-to-end solution, you could consider a function which takes in an arbitrary list of operands and corresponding binary operators, and places the operations between them, then evaluates the result.

def substituteAndEval(operands, operations):
    return eval(" {} ".join(str(x) for x in operands).format(*operations))

Usage:

numbers = [1, 2, 9, 2, 59, 974]
operations = ('+', '-', '*', '/', '+')
print(substituteAndEval(numbers[:5], operations))

Output: 2.694915254237288

The remaining work would then be to generate and pass in the random operands and operations.

Note that Python 3+ eval() returns floating point numbers from integer division. You can replace the divide symbol/ with // to force floor division.

zr0gravity7
  • 2,917
  • 1
  • 12
  • 33
  • Thank you so much. I didn't know that the .join function could be used in that manner (outside of a single list), and that you can evaluate a number with a string and not integers. Could I clarify as to why the * is needed in the "*operations"? – user268493 Jul 13 '21 at 10:48
  • 1
    The `join()` method (as with most built-ins that take lists) also works for generators (what we are using here) and other sequence types. The [splat `*`](https://docs.python.org/3/reference/expressions.html#expression-lists) (aka "iterable unpacking") operator is needed to spread the `operations` iterable into the function's positional arguments. The implementation of `format()` also probably has a signature like `def format(*args):`, so that it can accept any number of positional arguments and pack them into a list `args`. See https://stackoverflow.com/questions/3394835/use-of-args-and-kwargs – zr0gravity7 Jul 13 '21 at 15:18