-1

Is there any function that can take input(arrays of numbers) and find all possible calculations on them that would result in the desired result(number).

For example if input is 2, 1, 4, 6, 7 and the desired result is 3. It would return something like.

2+1 = 3
7-4 = 3
6/2 = 3
Cedrick
  • 7
  • 2
  • 3
    This would be a good application for recursive programming. Which operators and functions do you want to allow? – Sebastian Dec 30 '21 at 19:59
  • 1
    How long is input array? What are the possible operations? What is the range of input numbers? What is the exact output in this scenario, is it the 3 line output you have given? And most importantly, what have you tried? – vish4071 Dec 30 '21 at 20:04
  • Should the calculations be algebraically exact or can they be done with floating point? – Sebastian Dec 30 '21 at 20:07
  • Too many loose ends here. If you want the "complex-string-expressions" as output as well, that makes things even more complicated. Also, what is the use case? – vish4071 Dec 30 '21 at 20:09
  • Can the numbers and operations be used more than once in an expression? The numbers are numbers and not digits? – Sebastian Dec 30 '21 at 20:11
  • The numbers are exact without floating point ranging from 0-9. I had written the program with conditional but that made it harder to implement the new condition – Cedrick Dec 30 '21 at 20:37
  • The input is ideally 6 integers. And any numbers out of the six integers can be used. – Cedrick Dec 30 '21 at 20:39
  • If you use the numbers once and have +-*/ and parantheses, there are about 7 million combinations. That should be no problem for a computer to work through. – Sebastian Dec 30 '21 at 20:50
  • I guess I'll try that – Cedrick Dec 30 '21 at 21:00
  • Example solution: loop over 6 numbers as first number A. Solution found? Otherwise (for each first number) loop to pick 1 of 5 remaining second numbers B, loop 1 to 6 for operation A+B, A-B, B-A, A*B, A/B, B/A. Solution found? Otherwise store as C. Loop 1 to 4 to pick third number D. And so on ... – Sebastian Dec 30 '21 at 21:02
  • @Sebastian Your example solution fails to account for parentheses. – btilly Dec 30 '21 at 21:06
  • The parantheses are handled implicitly. If the numbers are 4=A, 6=B, 2=D in that order and the operations are A * B=C and D-C in that order, the resulting expression is 2-(4*6), whereas if the numbers are 2=A, 4=B, 6=D in that order and the operations are A-B=C and C * D in that order, the resulting expression is (2-4)*6. – Sebastian Dec 30 '21 at 21:12
  • Okay, you are right. Two independent parantheses won't work. – Sebastian Dec 30 '21 at 21:17
  • By my estimate, there are 30,965,760 ways to combine a given list of 6 numbers with 4 operators and parentheses. Of course we're counting A+B as separate from B+A. And I don't promise that I didn't make a calculation error that is off by an order of magnitude either way. – btilly Dec 30 '21 at 23:17
  • Does this answer your question? [How to design an algorithm to calculate countdown style maths number puzzle](https://stackoverflow.com/questions/15293232/how-to-design-an-algorithm-to-calculate-countdown-style-maths-number-puzzle) – Sneftel Jan 01 '22 at 22:42
  • I created an example program at https://godbolt.org/z/3eWPzdcrh If the question had been open, I could paste the answer here – Sebastian Jan 02 '22 at 00:21
  • For your simplified question, you can go to the godbolt link, set N to 4, complex to 2, numbers to your five numbers and result to 3. The output is shown in the window below together with 4-1. – Sebastian Jan 04 '22 at 21:47

3 Answers3

1

What you want to do is to evaluate all possible calculations & find options leading to the correct result.

One easy way to structure it is to implement a RPN calculation (reverse polish notation). https://en.wikipedia.org/wiki/Reverse_Polish_notation

Basically what you have to do is to enumerate stacks of numbers & operators so that you have always one more numebr than operators.

Jean Valj
  • 119
  • 4
0

I thought that this was a fun problem, so I decided to demonstrate how to do it efficiently. On my system it takes about half a second to find all 4193 solutions.

The trick is to use dynamic programming to build a data structure from which the expressions can all be found efficiently.

from fractions import Fraction

def bit_subset(index, things):
    i = 0
    bit = 1
    answer = []
    while bit <= index:
        if bit & index:
            answer.append(things[i])
        i += 1
        bit += bit
    return answer

def indexes_to_bit_subset(indexes):
    answer = 0
    for index in indexes:
        answer += 2**index
    return answer

def calculation_tree (digits):
    # Here is the tree structure:
    #
    #   By bit_integer of subset used
    #     By value reached
    #       [
    #          count_of_ways,
    #          [
    #              [
    #                  count,
    #                  first subset bit_integer,
    #                  first value,
    #                  op ('+', '-', '*', '/', None),
    #                  second subset bit_integer (may be 0),
    #                  second value (may be None)
    #               ]
    #          ]
    #       ]
    #
    tree = {}

    # Populate with all of the raw numnbers.
    for i in range(len(digits)):
        # By bit_integer for 1 thing.
        tree[2**i] = {
            # By value reached (use fractions to avoid roundoff
            Fraction(digits[i], 1): [
                # Just 1 way to do it.
                1,
                # What are the ways?  Let me tell you!
                [[1, 2**i, digits[i], None, 0, None]]
                ]
            }

  # This loops over all subsets with something in the first.
    for subset_index in range(1, 2**len(digits)):
        # The indexes into the chosen subset.
        index_subset = bit_subset(subset_index, list(range(len(digits))))
        if subset_index not in tree:
            subtree = {}
            tree[subset_index] = subtree

            # Look at ways to split it with something in both sides
            for sub_subset_index in range(1, 2**len(index_subset)-1):
                co_subset_index = 2**(len(index_subset)) - 1 - sub_subset_index

                # We have a split by indexes into index_subset.
                # We need to turn that into a split by indexes into digits
                # Which we represent by integers representing bits.
                index_sub_subset = bit_subset(sub_subset_index, index_subset)
                sub_subset_bit_index = indexes_to_bit_subset(index_sub_subset)

                index_co_subset = bit_subset(co_subset_index, index_subset)
                co_subset_bit_index = indexes_to_bit_subset(index_co_subset)

                # Let's pull out the already known calculation results.
                subtree1 = tree[sub_subset_bit_index]
                subtree2 = tree[co_subset_bit_index]
                for value1 in subtree1.keys():
                    count1 = subtree1[value1][0]
                    for value2 in subtree2.keys():
                        count2 = subtree2[value2][0]
                        # We now have to add each possible operation result to subtree
                        options = {
                            '+': value1 + value2,
                            '-': value1 - value2,
                            '*': value1 * value2,
                        }
                        if value2 != 0:
                            options['/'] = value1 / value2

                        for op, value in options.items():
                            if value in subtree:
                                subtree[value][0] += count1 * count2
                                subtree[value][1].append([
                                    count1 * count2,
                                    sub_subset_bit_index,
                                    value1,
                                    op,
                                    co_subset_bit_index,
                                    value2
                                ])
                            else:
                                subtree[value] = [
                                    count1 * count2,
                                    [[
                                        count1 * count2,
                                        sub_subset_bit_index,
                                        value1,
                                        op,
                                        co_subset_bit_index,
                                        value2
                                    ]]
                                ]

    return tree

# Yields the expressions that result in value
def expression_iter (tree, bit_integer, value):
    if bit_integer in tree:
        subtree = tree[bit_integer]
        if value in subtree:
            ways = subtree[value][1]
            for (
                    count,
                    bit_integer1, value1,
                    op,
                    bit_integer2, value2
            ) in ways:
                if op is None:
                    yield str(value1)
                else:
                    for expr1 in expression_iter(tree, bit_integer1, value1):
                        for expr2 in expression_iter(tree, bit_integer2, value2):
                            yield f'({expr1} {op} {expr2})'

def all_expressions(digits, target):
    tree = calculation_tree(digits)
    frac_target = Fraction(target, 1)
    for bit_integer, subtree in tree.items():
        if frac_target in subtree:
            for expr in expression_iter(tree, bit_integer, frac_target):
                yield expr


for expr in all_expressions([2, 1, 4, 6, 7], 3):
    print(expr)
btilly
  • 43,296
  • 3
  • 59
  • 88
-1

What I think you can do is you can make it generate random numbers using a rng
module. Make them do random numbers in a rational range and do random operations with them. Print the equations IF they equal a certain number. YOu could also make a simple AI like thing that sets a rational range of the random numbers to make it faster. (Notice/edit, the random range selects a number and the number is converted to one of the input numbers.)

EXAMPLE

# simple 2 digit example
import random
array = [2, 5, 6, 1]
c = 1
wantedresult = 7
while c == 1:
  i = random.randint(0, 4)
  a = random.randint(0, 4)
  numa = array[i]
  numb = array[a]
  answer = numb - numa 
  if answer == wantedresult:
    print(numb + " - " + numa)
  answer = numb + numa 
  if answer == wantedresult:
    print(numb + " + " + numa)
  answer = numa + numb
  if answer == wantedresult:
    print(numa + " + " + numb
  answer = numa - numb
  if answer == wantedresult:
    print(numa + " - " + numb
ArdenyUser
  • 53
  • 8
  • How does anything like this gives the specific output to his input? Can you explain your approach with OP's example? – vish4071 Dec 30 '21 at 20:06
  • It would take an array and randomly combine them and if they get a certain answer/result it is printed on the screen. – ArdenyUser Dec 30 '21 at 20:08
  • 1
    And how does randomly combining the array-elements help? Why not just loop through for all possible pairs and do all possible operations? – vish4071 Dec 30 '21 at 20:10
  • Yes, it uses random operations with them and if the random equation equals a certain number, it prints it out. So if I did not misunderstand the question it works. What you said, "loop through for all possible pairs and do all possible operations" is quite simulair to what it does. It repeats mixing equations from an array together. It prints feasible answers. – ArdenyUser Dec 30 '21 at 20:11
  • In that case, I think you have misunderstood here. He wants `all possible calculations that would result in the desired result`, which can never be achieved by introducing randomness. If array size is small, say <100, with only integers and using basic `+-*/%^` operations, this can trivially (and sbould be) solved through brute force – vish4071 Dec 30 '21 at 20:15
  • Yeah, that is what it does. We both may be misunderstanding each other. It randomly mixes and sees what gives a certain answer. :) – ArdenyUser Dec 30 '21 at 20:16
  • 100 is rather large, if you do combinations, where all 100 numbers appear ... – Sebastian Dec 30 '21 at 20:17
  • If you really think your solution works, I would encourage you to write a working code. For even ~50 elements with 6 operations, instead of publishing all possible calculations, your code would most prb get into an infinite loop. – vish4071 Dec 30 '21 at 20:17
  • @Sebastian, yes, combinations for his "complex-expression" output would still not be possible. However, what he posted in "code-format" would be possible – vish4071 Dec 30 '21 at 20:18
  • Okay then. I will modify my post. – ArdenyUser Dec 30 '21 at 20:19
  • Finding all combinations with >15 numbers is unrealistic (as long as you do not introduce mathematical and logical reasoning to skip most of the wrong solutions) – Sebastian Dec 30 '21 at 20:23
  • Why is the output not possible? If you find a solution, you know, how you got it? – Sebastian Dec 30 '21 at 20:25
  • I made a small example of my concept As you said, large sale examples will need some fine tuning. – ArdenyUser Dec 30 '21 at 20:27