Как выполнить все возможные комбинации арифметических c операций над 3 целыми числами? - PullRequest
0 голосов
/ 02 мая 2020

Предположим, у меня есть три целых числа. Я хочу получить список всех возможных значений, полученных путем выполнения всех 16 (4x4 из *, /, +, -) операций между ними.

Как если бы у меня было 3 4 1, мы должны получить значения 1, 2, 6, 7, 8, 9, 11, 12, 13, 15 и 16. То есть

res = num1 (op1) num2 (op2) num3 где операторы:

["**", "*/", "*+", "*-", "/*", "//", "/+", "/-", "+*", "+/", "++", "+-", "-*", "-/", "-+", "--"]

Однако, выгода заключается в том, что деление возможно только в том случае, если x%y==0, т.е. делитель является фактором дивиденды.

Итак, до сих пор мне удавалось перебивать каждую операцию, но я пропускаю некоторые ответы. Я также определил пользовательскую операцию деления для учета улова .

Я должен вернуть список, содержащий уникальные значения, которые являются положительными.


Мой текущий код беспорядок, но здесь это ради этого Здесь также пропущены некоторые значения.

def div(x, y):
    if x!=0 and y!=0:
        if x%y==0:return x/y
        else:return None


ops_lis = ["**", "*/", "*+", "*-", "/*", "//", "/+", "/-", "+*", "+/", "++", "+-", "-*", "-/", "-+", "--"]

d1, d2, d3 = map(int, input().split())
cal_lis, res_lis = [], []
for op in ops_lis:
    if op[0] == "*" and op[1] == "*":cal_lis.append(d1*d2*d3)
    if op[0] == "*" and op[1] == "/":
        if div(d1*d2, d3) != None:cal_lis.append(div(d1*d2, d3))
        cal_lis.append(div(d1*d2, d3))
    if op[0] == "*" and op[1] == "+":
        cal_lis.append(d1*(d2+d3))
        cal_lis.append((d1*d2)+d3)
    if op[0] == "*" and op[1] == "-":
        cal_lis.append(d1*d2-d3)
        cal_lis.append(d1*(d2-d3))
        cal_lis.append((d1*d3)-d2)
    if op[0] == "/" and op[1] == "*":cal_lis.append(div(d1, d2*d3))
    if op[0] == "/" and op[1] == "/":
        if div(d1, d2) == None or div(d2, d3) == None:
            cal_lis.append(None)
        else:
            cal_lis.append(div(div(d1, d2), d3))
    if op[0] == "/" and op[1] == "+":
        if div(d1, d2) == None:
            cal_lis.append(None)
        else:
            cal_lis.append(div(d1, d2)+d3)
    if op[0] == "/" and op[1] == "-":
        if div(d1, d2) == None:
            cal_lis.append(None)
        else:
            cal_lis.append(div(d1, d2)-d3)

    if op[0] == "+" and op[1] == "*":
        cal_lis.append(d1+d2*d3)
        cal_lis.append((d1+d2)*d3)
        cal_lis.append((d1+d3)*d2)
    if op[0] == "+" and op[1] == "/":
        if div(d2, d3) == None:
            cal_lis.append(None)
        else:
            cal_lis.append(d1+div(d2, d3))
    if op[0] == "+" and op[1] == "+":cal_lis.append(d1+d2+d3)
    if op[0] == "+" and op[1] == "-":cal_lis.append(d1+d2-d3)

    if op[0] == "-" and op[1] == "*":
        cal_lis.append(d1-d2*d3)
        cal_lis.append((d1-d2)*d3)
        cal_lis.append((d1-d3)*d2)
    if op[0] == "-" and op[1] == "/":
        if div(d2, d3) == None:cal_lis.append(None)
        else: cal_lis.append(d1-div(d2, d3))
        if div(d1-d2, d3) == None:cal_lis.append(None)    
        else: cal_lis.append(div(d1-d2, d3))
    if op[0] == "-" and op[1] == "+":cal_lis.append(d1-d2+d3)
    if op[0] == "-" and op[1] == "-":cal_lis.append(d1-d2-d3)

# print(cal_lis)
cal_lis = [int(cal) for cal in cal_lis if cal!=None]
for res in cal_lis:
    if (res > 0 and res not in res_lis):
        res_lis.append(int(res))
for a in sorted(res_lis):
    print(a, end=" ")
print()

Существует ли эффективный способ выполнить эту задачу (возможно, с использованием деревьев?), Учитывая, что условие деления также верно?

Любая помощь будет быть оцененным ...


РЕДАКТИРОВАТЬ: добавлены ограничения

Расчет должен соответствовать следующему формату: число = кости1 op1 кости2 op2 кости3

где:

  • op1 и op2 могут быть +, -, * или /. Например, op1 = + и op2 = /
  • dice1, dice2 и dice3 могут быть любой комбинацией чисел, брошенных в текущем раунде. Значение ad ie можно использовать один раз в расчете.
  • Для деления делитель должен быть фактором деления
  • Можно добавить скобки, чтобы переопределить приоритет операторов op1 и op2. т.е. (dice1 op1 dice2) op2 dice3 или dice1 op1 (dice2 op2 dice3)

Ответы [ 4 ]

2 голосов
/ 02 мая 2020

Чтобы получить все возможные результаты, вам нужно включить группировку операций в ваши комбинации. Для этого я бы предложил рекурсивную функцию:

def calcAll(*values,seen=None):
    seen = seen or set()
    if len(values) == 2:
        a,b  = values
        a,sa = (a[0],f"({a[1]})") if isinstance(a,tuple) else (a,str(a))
        b,sb = (b[0],f"({b[1]})") if isinstance(b,tuple) else (b,str(b))
        if a>b : a,sa, b,sb = b,sb, a,sa
        if (a,b) in seen or seen.add((a,b)) :return                
        yield a+b, f"{sa}+{sb}"
        yield a*b, f"{sa}*{sb}"
        yield a-b, f"{sa}-{sb}"
        yield b-a, f"{sb}-{sa}"
        if b != 0 and a%b==0: yield a//b, f"{sa}/{sb}"
        if a != 0 and b%a==0: yield b//a, f"{sb}/{sa}"
        return
    pairs = ((i,j) for i in range(len(values)-1) for j in range(i+1,len(values)))
    for i,j in pairs:
        rest = [*values]
        a,b  = rest.pop(j),rest.pop(i)
        for paired in calcAll(a,b,seen=seen):            
            for result in calcAll(paired,*rest):
               if result in seen or seen.add(result): continue
               yield result

вывод:

# distinct positive solutions sorted by result
for r,sr in sorted(calcAll(3,4,1)):
    if r>0: print(sr,"=",r)

(1*4)-3 = 1
4-(3/1) = 1
(4/1)-3 = 1
(4-3)*1 = 1
4/(1+3) = 1
4-(1*3) = 1
(4-3)/1 = 1
1/(4-3) = 1
3/(4-1) = 1
(1+3)/4 = 1
(4-1)/3 = 1
1-(3-4) = 2
4-(3-1) = 2
(1+4)-3 = 2
(1-3)+4 = 2
(4-3)+1 = 2
4/(3-1) = 2
(4-1)+3 = 6
(3-1)+4 = 6
3-(1-4) = 6
4-(1-3) = 6
(4+3)-1 = 6
(1*4)+3 = 7
(3/1)+4 = 7
(4/1)+3 = 7
(1*3)+4 = 7
(4+3)/1 = 7
(4+3)*1 = 7
(1+4)+3 = 8
(1+3)+4 = 8
(3-1)*4 = 8
(4+3)+1 = 8
(4-1)*3 = 9
(4*3)-1 = 11
(1*4)*3 = 12
(4*3)*1 = 12
(4*3)/1 = 12
(1*3)*4 = 12
(3/1)*4 = 12
(4/1)*3 = 12
(4*3)+1 = 13
(1+4)*3 = 15
(1+3)*4 = 16

Если вы хотите получить только положительные результаты:

print( set(r for r,_ in calcAll(3,4,1) if r>0) )
{1, 2, 6, 7, 8, 9, 11, 12, 13, 15, 16}

Функция также работает для больших списков номеров:

# one solution for each positive result of operations between 4 numbers
for r,sr in sorted(dict(calcAll(1,2,3,4)).items()):
    if r>0: print(sr,"=",r)
(2/1)+(3-4) = 1
(2-1)-(3-4) = 2
(2/1)-(3-4) = 3
(4*3)/(2+1) = 4
((4*3)/2)-1 = 5
(4*3)/(2/1) = 6
((4*3)/2)+1 = 7
(4+3)-(1-2) = 8
(4*3)-(2+1) = 9
(4*3)-(2/1) = 10
(1-2)+(4*3) = 11
(4*3)/(2-1) = 12
(4*3)-(1-2) = 13
(2/1)+(4*3) = 14
(2+1)+(4*3) = 15
(1+(4+3))*2 = 16
(3*(4+2))-1 = 17
(3/1)*(4+2) = 18
(3*(4+2))+1 = 19
((3*2)-1)*4 = 20
(2+1)*(4+3) = 21
((4*3)-1)*2 = 22
(2*(4*3))-1 = 23
(2/1)*(4*3) = 24
(2*(4*3))+1 = 25
(1+(4*3))*2 = 26
(1+(4*2))*3 = 27
(1+(3*2))*4 = 28
(4+1)*(3*2) = 30
(3+1)*(4*2) = 32
(2+1)*(4*3) = 36        

, а также для дублирующих номеров:

for r,sr in sorted(dict(calcAll(3,3,3)).items()):
    if r>0: print(sr,"=",r)

3-(3/3) = 2
3/(3/3) = 3
(3/3)+3 = 4
(3*3)-3 = 6
(3+3)+3 = 9
(3*3)+3 = 12
(3+3)*3 = 18
(3*3)*3 = 27
2 голосов
/ 02 мая 2020

Вот решение, которое использует itertools для генерации каждой комбинации двух операторов и Python eval для вычисления строки.

from itertools import product
ops = ['+', '-', '*', '/']
nums = [3, 4, 0]

combos = product(ops, repeat=2)
for expr in combos:
        eval_me = f'{nums[0]} {expr[0]} {nums[1]} {expr[1]} {nums[2]}'
        try:
            result = eval(eval_me)
            print (f'{eval_me} = {result}')
        except ZeroDivisionError as e:
            print (f'expression "{eval_me}" caused an exception: {e}')

С 4 операторами у нас есть 4 * 4 = 16 результатов.

Вот результаты:

3 + 4 + 0 = 7
3 + 4 - 0 = 7
3 + 4 * 0 = 3
expression "3 + 4 / 0" caused an exception: division by zero
3 - 4 + 0 = -1
3 - 4 - 0 = -1
3 - 4 * 0 = 3
expression "3 - 4 / 0" caused an exception: division by zero
3 * 4 + 0 = 12
3 * 4 - 0 = 12
3 * 4 * 0 = 0
expression "3 * 4 / 0" caused an exception: division by zero
3 / 4 + 0 = 0.75
3 / 4 - 0 = 0.75
3 / 4 * 0 = 0.0
expression "3 / 4 / 0" caused an exception: float division by zero
1 голос
/ 02 мая 2020

Вы можете использовать

nums = [34, 12, 6]

itertools.combinations_with_replacement(ops_lis, len(nums) - 1)

Вы можете использовать следующее, чтобы получить все перестановки для операндов:

itertools.permutations(nums)

Затем вы можете построить все возможные выражения, используя три вложенных цикла, и использовать eval () для оценки выражений.

для получения всех комбинаций операторов.

0 голосов
/ 02 мая 2020

вот мое быстрое решение. Я использую np.nan, который после любого вычисления останется как np.nan, чтобы мы могли идентифицировать комбинации, которые не удовлетворяют условию.

import numpy as np
import itertools

def plus(a, b):
    return a + b

def minus(a, b):
    return a - b

def mult(a, b):
    return a * b

def div(a, b):
    if b!=0:
        if a%b==0:
            return a//b
    return np.nan


def combinations(nums, funcs):
    t = []
    for i in range(len(nums)-1):
        t.append(nums)
        t.append(funcs)
    t.append(nums)
    return list(itertools.product(*t))

def solve(instance):
    instance = list(instance)
    for i in range(len(instance)//2):
        b = instance.pop()
        func = instance.pop()
        a = instance.pop()
        instance.append(func(a, b))
    return instance[0]

def main():
    a = [1, 3 ,4]
    func = [plus, minus, mult, div]
    combs = combinations(a, func)
    solutions = [solve(i) for i in combs]
    for i, j in zip(combs, solutions):
        print(i, j)



if __name__ == "__main__":
    main()

Это решение операций справа налево, но вы можете изменить решить функцию, как вы wi sh.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...