Find missing arrithmetic operations in arithmetic expression - python

I was given the following task:
Richard likes to ask his classmates questions like the following:
4 x 4 x 3 = 13
In order to solve this task, his classmates have to fill in the missing arithemtic operators (+, -, *, /) that the created equation is true. For this example the correct solution would be 4 * 4 - 3. Write a piece of code that creates similar questions to the one above. It should comply the following rules:
The riddles should only have one solution (e.g. not 3 x 4 x 3 = 15, which has two solutions)
The operands are numbers between 1-9
The solution is a positive integer
Point before dashes is taken into account (e.g. 1 + 3 * 4 = 13)
Every interim result is an integer (e.g. not 3 / 2 * 4 = 6)
Create riddles up to 15 arithmetic operands, which follow these rules.
My progress so far:
import random
import time
import sys
add = lambda a, b: a + b
sub = lambda a, b: a - b
mul = lambda a, b: a * b
div = lambda a, b: a / b if a % b == 0 else 0 / 0
operations = [(add, '+'),
(sub, '-'),
(mul, '*'),
(div, '/')]
operations_mul = [(mul, '*'),
(add, '+'),
(sub, '-'),
(div, '/')]
res = []
def ReprStack(stack):
reps = [str(item) if type(item) is int else item[1] for item in stack]
return ''.join(reps)
def Solve(target, numbers):
counter = 0
def Recurse(stack, nums):
global res
nonlocal counter
valid = True
end = False
for n in range(len(nums)):
stack.append(nums[n])
remaining = nums[:n] + nums[n + 1:]
pos = [position for position, char in enumerate(stack) if char == operations[3]]
for j in pos:
if stack[j - 1] % stack[j + 1] != 0:
valid = False
if valid:
if len(remaining) == 0:
# Überprüfung, ob Stack == target
solution_string = str(ReprStack(stack))
if eval(solution_string) == target:
if not check_float_division(solution_string)[0]:
counter += 1
res.append(solution_string)
if counter > 1:
res = []
values(number_ops)
target_new, numbers_list_new = values(number_ops)
Solve(target_new, numbers_list_new)
else:
for op in operations_mul:
stack.append(op)
stack = Recurse(stack, remaining)
stack = stack[:-1]
else:
if len(pos) * 2 + 1 == len(stack):
end = True
if counter == 1 and end:
print(print_solution(target))
sys.exit()
stack = stack[:-1]
return stack
Recurse([], numbers)
def simplify_multiplication(solution_string):
for i in range(len(solution_string)):
pos_mul = [position for position, char in enumerate(solution_string) if char == '*']
if solution_string[i] == '*' and len(pos_mul) > 0:
ersatz = int(solution_string[i - 1]) * int(solution_string[i + 1])
solution_string_new = solution_string[:i - 1] + solution_string[i + 1:]
solution_string_new_list = list(solution_string_new)
solution_string_new_list[i - 1] = str(ersatz)
solution_string = ''.join(str(x) for x in solution_string_new_list)
else:
return solution_string
return solution_string
def check_float_division(solution_string):
pos_div = []
solution_string = simplify_multiplication(solution_string)
if len(solution_string) > 0:
for i in range(len(solution_string)):
pos_div = [position for position, char in enumerate(solution_string) if char == '/']
if len(pos_div) == 0:
return False, pos_div
for j in pos_div:
if int(solution_string[j - 1]) % int(solution_string[j + 1]) != 0:
# Float division
return True, pos_div
else:
# No float division
return False, pos_div
def new_equation(number_ops):
equation = []
operators = ['+', '-', '*', '/']
ops = ""
if number_ops > 1:
for i in range(number_ops):
ops = ''.join(random.choices(operators, weights=(4, 4, 4, 4), k=1))
const = random.randint(1, 9)
equation.append(const)
equation.append(ops)
del equation[-1]
pos = check_float_division(equation)[1]
if check_float_division(equation):
if len(pos) == 0:
return equation
for i in pos:
equation[i] = ops
else:
'''for i in pos:
if equation[i+1] < equation[i-1]:
while equation[i-1] % equation[i+1] != 0:
equation[i+1] += 1'''
new_equation(number_ops)
else:
print("No solution with only one operand")
sys.exit()
return equation
def values(number_ops):
target = 0
equation = ''
while target < 1:
equation = ''.join(str(e) for e in new_equation(number_ops))
target = eval(equation)
numbers_list = list(
map(int, equation.replace('+', ' ').replace('-', ' ').replace('*', ' ').replace('/', ' ').split()))
return target, numbers_list
def print_solution(target):
equation_encrypted_sol = ''.join(res).replace('+', '○').replace('-', '○').replace('*', '○').replace('/', '○')
print("Try to find the correct operators " + str(equation_encrypted_sol) + " die Zahl " + str(
target))
end_time = time.time()
print("Duration: ", end_time - start_time)
input(
"Press random button and after that "ENTER" in order to generate result")
print(''.join(res))
if __name__ == '__main__':
number_ops = int(input("Number of arithmetic operators: "))
# number_ops = 10
target, numbers_list = values(number_ops)
# target = 590
# numbers_list = [9, 3, 5, 3, 5, 2, 6, 3, 4, 7]
start_time = time.time()
Solve(target, numbers_list)
Basically it's all about the "Solve(target, numbers)" method, which returns the correct solution. It uses Brute-Force in order to find that solution. It receives an equation and the corresponding result as an input, which has been generated in the "new_equation(number_ops)" method before. This part is working fine and not a big deal. My main issue is the "Solve(target, numbers)" method, which finds the correct solution using a stack. My aim is to make that program as fast as possible. Currently it takes about two hours until an arithmetic task with 15 operators has been found, which follows the rules above. Is there any way to make it faster or maybe another approach to the problem besides Brute-Force? I would really appreciate your help :)

This is mostly brute force but it only takes a few seconds for a 15 operation formula.
In order to check the result, I first made a solve function (recursive iterator) that will produce the solutions:
def multiplyDivide(numbers):
if len(numbers) == 1: # only one number, output it directly
yield numbers[0],numbers
return
product,n,*numbers = numbers
if product % n == 0: # can use division.
for value,ops in multiplyDivide([product//n]+numbers):
yield value, [product,"/",n] + ops[1:]
for value,ops in multiplyDivide([product*n]+numbers):
yield value, [product,"*",n] + ops[1:]
def solve(target,numbers,canGroup=True):
*others,last = numbers
if not others: # only one number
if last == target: # output it if it matches target
yield [last]
return
yield from ( sol + ["+",last] for sol in solve(target-last,others)) # additions
yield from ( sol + ["-",last] for sol in solve(target+last,others)) # subtractions
if not canGroup: return
for size in range(2,len(numbers)+1):
for value,ops in multiplyDivide(numbers[-size:]): # multiplicative groups
for sol in solve(target,numbers[:-size]+[value],canGroup=False):
yield sol[:-1] + ops # combined multipicative with rest
The solve function recurses through the numbers building an addition or subtraction with the last number and recursing to solve a smaller problem with the adjusted target and one less number.
In addition to the additions and subtraction, the solve function groups the numbers (from the end) into consecutive multiplications/divisions and processes them (recursing into solve) using the resulting value that will have calculation precedence over additions/subtractions.
The multiplyDivide function (also a recursive generator) combines the group of numbers it is given with multiplications and divisions performed from left to right. Divisions are only added when the current product divided by the additional number produces an integer intermediate result.
Using the solve iterator, we can find a first solution and know if there are more by iterating one additional time:
def findOper(S):
expression,target = S.split("=")
target = int(target.strip())
numbers = [ int(n.strip()) for n in expression.split("x") ]
iSolve = solve(target,numbers)
solution = next(iSolve,["no solution"])
more = " and more" * bool(next(iSolve,False))
return " ".join(map(str,solution+ ["=",target])) + more
Output:
print(findOper("10 x 5 = 2"))
# 10 / 5 = 2
print(findOper("10 x 5 x 3 = 6"))
# 10 / 5 * 3 = 6
print(findOper("4 x 4 x 3 = 13"))
# 4 * 4 - 3 = 13
print(findOper("1 x 3 x 4 = 13"))
# 1 + 3 * 4 = 13
print(findOper("3 x 3 x 4 x 4 = 25"))
# 3 * 3 + 4 * 4 = 25
print(findOper("2 x 6 x 2 x 4 x 4 = 40"))
# 2 * 6 * 2 + 4 * 4 = 40 and more
print(findOper("7 x 6 x 2 x 4 = 10"))
# 7 + 6 * 2 / 4 = 10
print(findOper("2 x 2 x 3 x 4 x 5 x 6 x 3 = 129"))
# 2 * 2 * 3 + 4 * 5 * 6 - 3 = 129
print(findOper("1 x 2 x 3 x 4 x 5 x 6 = 44"))
# 1 * 2 + 3 * 4 + 5 * 6 = 44
print(findOper("1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 8 x 7 x 6 x 5 x 4 x 1= 1001"))
# 1 - 2 - 3 + 4 * 5 * 6 * 7 / 8 * 9 + 8 * 7 - 6 + 5 + 4 + 1 = 1001 and more
print(findOper("1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 8 x 7 x 6 x 5 x 4 x 1= 90101"))
# no solution = 90101 (15 seconds, worst case is not finding any solution)
In order to create a single solution riddle, the solve function can be converted to a result generator (genResult) that will produce all possible computation results. This will allow us to find a result that only has one combination of operations for a given list of random numbers. Given that the multiplication of all numbers is very likely to be a unique result, this will converge rapidly without having to go through too many random lists:
import random
def genResult(numbers,canGroup=True):
*others,last = numbers
if not others:
yield last
return
for result in genResult(others):
yield result - last
yield result + last
if not canGroup: return
for size in range(2,len(numbers)+1):
for value,_ in multiplyDivide(numbers[-size:]):
yield from genResult(numbers[:-size]+[value],canGroup=False)
def makeRiddle(size=5):
singleSol = []
while not singleSol:
counts = dict()
numbers = [random.randint(1,9) for _ in range(size)]
for final in genResult(numbers):
if final < 0 : continue
counts[final] = counts.get(final,0) + 1
singleSol = [n for n,c in counts.items() if c==1]
return " x ".join(map(str,numbers)) + " = " + str(random.choice(singleSol))
The reason we have to loop for singleSol (single solution results) is that there are some cases where even the product of all numbers is not a unique solution. For example: 1 x 2 x 3 = 6 could be the product 1 * 2 * 3 = 6 but could also be the sum 1 + 2 + 3 = 6. There aren't too many of those cases but it's still a possibility, hence the loop. In a test generating 1000 riddles with 5 operators, this occurred 4 times (e.g. 4 x 1 x 1 x 4 x 2 has no unique solution). As you increase the size of the riddle, the occurrence of these no-unique-solution patterns becomes more frequent (e.g. 6 times generating 20 riddles with 15 operators).
output:
S = makeRiddle(15) # 7 seconds
print(S)
# 1 x 5 x 2 x 5 x 8 x 2 x 3 x 4 x 1 x 2 x 8 x 9 x 7 x 3 x 2 = 9715
print(findOper(S)) # confirms that there is only one solution
# 1 * 5 * 2 * 5 * 8 * 2 * 3 * 4 - 1 + 2 + 8 * 9 + 7 * 3 * 2 = 9715

Related

What I am doing wrong here - dynamic problem

PROBLEM STATEMENT:
You are about to go shopping for some candy in a local store. They sell candy for either $1 or $2 pieces. You would like to know in how many unique ways you can purchase candy based on the amount of money you have.
def buying_candy(amount_of_money):
if amount_of_money < 2:
return 1
dp = {0: 1, 1: 1}
x = 1
while x < amount_of_money:
if x not in dp:
dp[x] = 1
dp[x] = dp[x] + dp[x - 1]
x += 1
return dp[amount_of_money - 1]
print(buying_candy(4))
OUTPUT: 5
EXPLANATION:
1 + 1 + 1 + 1
2 + 2
2 + 1 + 1
1 + 2 + 1
1 + 1 + 2
UPDATE:
SOLUTION of Problem
def buying_candy(amount_of_money):
if amount_of_money < 2:
return 1
dp = {
0: 1,
1: 1
}
x = 2
while x < amount_of_money + 1:
if x not in dp:
dp[x] = 1
dp[x] = dp[x - 1] + dp[x - 2]
x += 1
return dp[amount_of_money]
This problem does not require dynamic programming. Denote the amount of money by n. There are between 0 and ⌊n/2⌋ twos. If the number of twos is k, then the number of ones is n−2k, and the total number of ones and twos is n-2k+k = n-k. Each solution with k twos corresponds to choosing k out of the n-k positions for the twos. So the total number of solutions with k twos is (n-k choose k), and the total number of solutions is the sum of this expression over k from 0 and ⌊n/2⌋.
In Python:
import math
n = 4 # amount of money
total = sum(math.comb(n-k,k) for k in range(n//2+1)) # total number of solutions
print(total)
If the rules of the game require using dynamic programming, here is a correct version:
def buying_candies(n):
if n < 2:
return 1
dp = [1, 1] # use list instead of dictionary, as keys are 0,1,2,...
for i in range(2, n+1):
dp.append(dp[i-1] + dp[i-2])
return dp[n]
print(buying_candies(4))
It is all just Fibonacci sequence, in fact :)
So there is in fact a closed formula.

How can I make the output print out a list of numbers? [duplicate]

This question already has an answer here:
Dividing multiple numbers in a list
(1 answer)
Closed 1 year ago.
So what I'm trying to do is find the total number of 1 dollar, 2 dollar, 5 dollar and 10 dollar bills needed to equal a number v: int and i'm kinda stuck on how to do it...
Here is my code so far..
def coinChange(v: int):
while x == 1 and x == 2 and x == 5 and x == 10:
if x + x + x + x == v:
return x
Like its definitely wrong, so what am I doing wrong here and what should I do to fix it? Btw, the output should be a list, so like... if coinChange(38) is the input, the output should be [10,10,10,5,2,1] What is the right code to make sure I get the right output?
You can use integer division (//) and modulus (%) operations to determine the number of each denomination required.
def coinChange(v: int):
tens = v // 10
remainder = v % 10
fives = remainder // 5
remainder = remainder % 5
twos = remainder // 2
ones = remainder % 2
coins = [10 for n in range(tens)]
coins += [5 for n in range(fives)]
coins += [2 for n in range(twos)]
coins += [1 for n in range(ones)]
return coins
The code can be reduced using a loop:
def coinChange(v: int):
coins = [10, 5, 2, 1]
change = []
for c in coins:
change += [c for n in range(v // c)]
v = v % c
return change
The result for both implementations will be the same:
coinChange(38) # [10, 10, 10, 5, 2, 1]
Try using this code instead.
def change(amount):
money = ()
for coin in [10,5,2,1]:
num = amount/coin
money += (coin,) * num
amount -= coin * num
return money
Easiest logic and you can understand it make sure to start with greater like check 10 first then 5 and then 2 and then 1
def coinChange(x):
coins = []
while x != 0:
if x >= 10:
x = x - 10
coins.append(10)
elif x >= 5:
x = x - 5
coins.append(5)
elif x >= 2:
x = x - 2
coins.append(2)
elif x >= 1:
x = x - 1
coins.append(1)
print(coins)
coinChange(38)

How would I implement backtracking here?

I've been struggling to find the right logic for my sudoku solver. So far I've created a function to take (x,y) coordinates and the number and check if it is a legal move.
Though I don't understand how I can iterate through the grid replacing every 0 with a legal number, then go back and fix numbers that make the solve incorrect.
For example sometimes I will be left with 0's on the grid because some numbers can no longer be played. I took a deeper look at it and realized that numbers played previously in the same row were legal but ruined the puzzle. Instead, if those numbers were a bit higher the puzzle would be solved.
I understand backtracking would be my friend here but, I have no clue on how to implement it. So far here is what I have.
solve.py
import numpy as np
import time
class sodoku:
def __init__(self,grid,boxRange):
self.grid = grid
self.boxRange = boxRange
def show(self):
for row in self.grid:
print(row)
def solve(self):
def possible(num,x,y):
def box(x,y):
board = np.array(self.grid)
result = {}
size = 3
for i in range(len(board) // size):
for j in range(len(board) // size):
values = board[j * size:(j + 1) * size, i * size:(i + 1) * size]
result[i * size + j + 1] = values.flatten()
if y <= 2 and x <= 2:
squareBox = result[1]
if (y <= 5 and y > 2) and x <= 2:
squareBox = result[2]
if (y <= 8 and y > 5) and x <= 2:
squareBox = result[3]
if (y <= 2 ) and (x <= 5 and x > 2):
squareBox = result[4]
if (y <= 5 and y > 2)and (x <= 5 and x > 2):
squareBox = result[5]
if (y <= 8 and y > 5)and (x <= 5 and x > 2):
squareBox = result[6]
if (y <= 2) and (x <= 8 and x > 5):
squareBox = result[7]
if (y <= 5 and y > 2)and (x <= 8 and x > 5):
squareBox = result[8]
if (y <= 8 and y > 5)and (x <= 8 and x > 5):
squareBox = result[9]
return squareBox
row = self.grid[y]
column= [r[x] for r in self.grid]
square = box(x,y)
if (num not in row) and (num not in column) and (num not in square):
return True
else:
return False
y = 0
for row in self.grid:
x = 0
for number in row:
if number == 0:
for i in range(1,10):
if possible(i,x,y):
row[x] = i
elif i == 9 and possible(i,x,y) == False: pass
#what would I do here now
x += 1
y += 1
boxRange = "3x3"
bxd = []
with open('board.txt', 'r') as f:
for line in f:
line = line.strip()
line = line.split(' ')
bLine = [int(x) for x in line]
bxd.append(bLine)
# brd = [[3,0,0,2],[0,4,1,0],[0,3,2,0],[4,0,0,1]]
brd = sodoku(bxd,boxRange)
brd.show()
brd.solve()
print('-----Solved------')
brd.show()
board.txt
5 3 0 0 7 0 1 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9
Recursive pseudocode backtracing sudoku solver:
#solve will return a solved board, or None if it fails
def solve(board):
#case 1: board is solved
if board.is_solved: #simple check for leftover empty spaces
return board #board is solved. unzip the call stack
pos = board.next_empty_space()
valid = [i for i in range(1,10) if board.is_valid(pos, i)]
#case 2: not solved and no more valid moves
if not valid:
return None #no valid moves left
new_board = copy(board) #don't step on the original data in case this isn't the solution
for value in valid:
new_board[pos] = value
result = solve(new_board)
#case 3: one of the valid moves led to a valid solution
if result is not None: #we found a fully solved board
return result #here's where we unzip the call stack
#case 4: none of the valid moves led to a valid solution
return None #none of the valid moves panned out
Basically you consider each empty space on the board as a branch in a tree, and sub-branches from each branch you insert a new number which is currently valid at that point in the tree. If you get to the end of a branch, and there are no more valid moves left (sub-branches) you have either successfully filled in all the blank spaces or one of the numbers is wrong. When None gets returned, and execution goes back to the caller (up a frame in the call stack), the local position in the for loop going over valid moves is what "remembers" where you're at, and what the next possible valid move should be. It's basically a depth-first tree search algorithm for a correct board state.

Python for loops for repeated addition

I am trying to create a program that creates a multiplication table of n x n.
It's required for the assignment to use repeated addition instead of the multiplication function.
This is the code I have so far:
def main():
import math
print('Hello!')
n = (abs(eval(input("Enter n for the multiplication table n x n: "))))
n = int(n)
a = 0
for i in range(1,n+1):
for x in range(1,n+1):
a = i+a
print(i,' * ',x,' = ',a)
main()
It gives me an output like this:
Hello!
Enter n for the multiplication table n x n: 4
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
1 * 4 = 4
2 * 1 = 6
2 * 2 = 8
2 * 3 = 10
2 * 4 = 12
3 * 1 = 15
3 * 2 = 18
3 * 3 = 21
3 * 4 = 24
4 * 1 = 28
4 * 2 = 32
4 * 3 = 36
4 * 4 = 40
The output is obviously incorrect, so what can I change/add to fix the calculations?
You have a variable a inside of your nested for loops that you continuously add values to for different values of the multiplication table. Instead of adding i to a each iteration, let a = i*x. This will give you the correct value of multiplication each time. However, if you really want to do it with repeated addition, set a = 0 outside of the second for loop, but inside the first, like so:
for i in range(1,n+1):
for x in range(1,n+1):
a = i+a
print(i,' * ',x,' = ',a)
a = 0
For the print statement try using instead:
print(i,' * ',x,' = ',i*x)
I'm not certain what you are using the 'a' variable for, but if you'd like to output the multiplication of i and x still using a, instead keep your code the same and just change what you have for a in your nest for loop:
a = i*x
Hope this helps!
In your for loop, you're always incrementing the variable 'a' by itself added to 'i' each time, instead you should multiply i*x
print("Hello!")
n = input("Enter n for the multiplication table n x n: ")
n = int(n)
result = 0
for i in range(1,n+1):
for j in range(1, n+1):
result = i*j
print(i," * ", j, " = ", result)

Adding Consecutive integers in Python, with a twist

Here is my original code:
x = input("Please input an integer: ")
x = int(x)
i = 1
sum = 0
while x >= i:
sum = sum + i
i += 1
print(sum)
Here is what the second part is:
b) Modify your program by enclosing your loop in another loop so that you can find consecutive sums. For example , if 5 is entered, you will find five sum of consecutive numbers so that:
1 = 1
1 + 2 = 3
1 + 2 + 3 = 6
1 + 2 + 3 + 4 = 10
1 + 2 + 3 + 4 + 5 = 15
I have been stuck on this for 3 days now, and I just can't understand how to do it. I have tried this but to no avail.
while x >= i:
sum_numbers = sum_numbers + i
past_values = range(i)
for ints in past_values:
L = []
L.append(ints)
print(L, "+", i, "=", sum_numbers)
i += 1
Can anyone just help steer my in the correct direction? BTW. it is python 3.3
You could do this in one loop, by using range to define your numbers, and sum to loop through the numbers for you.
>>> x = input("Please input an integer: ")
Please input an integer: 5
>>> x = int(x)
>>>
>>> for i in range(1, x+1):
... nums = range(1, i+1)
... print(' + '.join(map(str, nums)), '=', sum(nums))
...
1 = 1
1 + 2 = 3
1 + 2 + 3 = 6
1 + 2 + 3 + 4 = 10
1 + 2 + 3 + 4 + 5 = 15
range(1, x+1) would give me [1, 2, 3, 4, 5], this acts as the controller for how many times we want to print out a sum. So, this for loop will happen 5 times for your example.
nums = range(1, i+1) notice we are using i instead here, (taken from the range above), which I am using to define which number I am up to in the sequence.
' + '.join(map(str, nums)):
map(str, nums) is used to convert all elements of nums into strings using str, since the join method expects an iterable filled with strings.
' + '.join is used to "join" elements together with a common string, in this case, ' + '. In cases where there is only 1 element, join will just return that element.
sum(nums) is giving you the sum of all numbers defined in range(1, i+1):
When nums = range(1, 2), sum(nums) = 1
When nums = range(1, 3), sum(nums) = 3
Etc...
reduce(lambda x,y:x+y,range(x+1))
You don't have to use a while loop, 2 for will do the trick nicely and with a more natural feeling.
x = input("Please input an integer : ")
x = int(x)
item = range(1, x + 1)
for i in item:
sum = 0
for j in range(1, i + 1):
sum = sum + j
print(str(sum))
Using list comprehension in python:
x = input("Please input an integer: ")
x = int(x)
i = 1
sums = [(((1+y)*y)//2) for y in range(i, x+1)] # creates list of integers
print(sums) # prints list of sums on one line
OR
[print(((1+y)*y)//2) for y in range(i, x+1)] # prints sums on separate line

Categories

Resources