How to implement Hanoi sort in python? Rules of Hanoi sort: http://www.dangermouse.net/esoteric/hanoisort.html
My code:
def hanoy_sorted(arr, x, y):
if len(arr) == 1:
print(arr[0], x, y)
elif len(arr) > 1:
hanoy_sorted(arr[1:], x, 6 - x - y)
print(arr[0], x, y)
hanoy_sorted(arr[1:], 6 - x - y, y)
def merge(arr1, x, arr2, y):
if len(arr2) == 0:
hanoy(arr1, x, y)
elif len(arr1) > 0:
n = arr1[-1]
j = len(arr2) - 1
if n < arr2[-1]:
print(arr1[-1], x, y)
j = len(arr2)
else:
for i in range(0, len(arr2)):
if n > arr2[i]:
j = i + 1
break
hanoy_sorted(arr2[j - 1:], y, x)
hanoy_sorted([n] + arr2[j - 1:], x, y)
arr2.insert(j, n)
merge(arr1[:len(arr1) - 1], x, sorted(arr2)[::-1], y)
def hanoy(arr, x, y):
if len(arr) == 1:
print(arr[0], x, y)
if len(arr) > 1:
mi = arr.index(max(arr))
up = arr[mi + 1:]
z.append(up[:])
hanoy(z[-1], x, 6 - x - y)
print(arr[mi], x, y)
merge(arr[:mi], x, sorted(up)[::-1], 6 - x - y)
arr.remove(arr[mi])
hanoy_sorted(sorted(arr)[::-1], 6 - x - y, y)
del z[-1]
n = int(input())
arr = list(map(int, input().split()))[::-1]
z = []
hanoy(arr, 1, 3)
There is an error in function merge: It sometimes puts bigger disk on smaller. How to fix that?
This programme prints what disk to move from rod1, to rod2.
Updated:
So I rewrote all my code and now I have that:
from sys import setrecursionlimit
setrecursionlimit(10000000)
def gen_moves(pos, prev_move):
moves = []
not_to_go = [prev_move[0], prev_move[2], prev_move[1]]
not_to_go2 = [prev_move[0], prev_move[2], 6 - prev_move[2] - prev_move[1]]
first, second, third = pos
if first != []:
if second != []:
if second[-1] > first[-1]:
moves.append([first[-1], 1, 2])
else:
moves.append([second[-1], 2, 1])
else:
moves.append([first[-1], 1, 2])
if third != []:
if third[-1] > first[-1]:
moves.append([first[-1], 1, 3])
else:
moves.append([third[-1], 3, 1])
else:
moves.append([first[-1], 1, 3])
else:
if second != []:
moves.append([second[-1], 2, 1])
if third != []:
moves.append([third[-1], 3, 1])
if second != []:
if third != []:
if second[-1] > third[-1]:
moves.append([third[-1], 3, 2])
else:
moves.append([second[-1], 2, 3])
else:
moves.append([second[-1], 2, 3])
else:
if third != []:
moves.append([third[-1], 3, 2])
if not_to_go in moves:
moves.remove(not_to_go)
if not_to_go2 in moves:
moves.remove(not_to_go2)
return moves
def do_move(arr, move):
pos = arr[:]
disk, from_rod, to_rod = move
pos[from_rod - 1] = pos[from_rod - 1][:-1]
pos[to_rod - 1] = pos[to_rod - 1] + [disk]
return pos
def hanoi(arr, solved, prev_move):
if not solved:
moves = gen_moves(arr, prev_move)
for move in moves:
pos = arr[:]
pos = do_move(pos, move)
sorted_arr = sorted(arr[0] + arr[1] + arr[2])[::-1]
for i in range(3):
if pos[i] == sorted_arr:
solved = True
solved = solved or hanoi(pos, solved, move)
if solved:
moves_to_solve.append(move)
break
return solved
n = int(input())
arr = [list(map(int, input().split()))[::-1], [], []]
solved = False
moves_to_solve = []
sorted_arr = sorted(arr[0] + arr[1] + arr[2])[::-1]
for i in range(3):
if arr[i] == sorted_arr:
solved = True
if n > 1 and not solved:
solved = hanoi(arr, solved, [0, 0, 0])
moves_to_solve = moves_to_solve[::-1]
for move in moves_to_solve:
print(*move)
It works right but if there are eleven disks programme prints segmentation fault. How to fix that?
You can simply use a recursive approach with a helper function to implement the tower of hanoi. Something like this;
def moveTower(height,fromPole, toPole, withPole):
if height >= 1:
moveTower(height-1,fromPole,withPole,toPole)
moveDisk(fromPole,toPole)
moveTower(height-1,withPole,toPole,fromPole)
def moveDisk(fp,tp):
print("moving disk from",fp,"to",tp)
Related
Problem link = https://leetcode.com/problems/minimum-operations-to-reduce-x-to-zero/
Was practicing on leetcode and came around the above the Q. Wrote the code written below, no idea why it is not running!
def min_num(nums,x):
f_ele = nums[0]
l_ele = nums[-1]
count = 0
if min(x - f_ele, x - l_ele ) >= 0:
count += 1
#modifying x and nums
if x - f_ele == min(x - f_ele, x - l_ele ):
x = x - f_ele
nums.remove(f_ele)
else:
x = x - l_ele
nums.remove(l_ele)
#Comparing x to use recursion or return the count
if x != 0:
min_num(nums,x)
else:
return count
elif x == 0:
return count
else:
return -1
Please help!!
When you do recursion and want to return something you need to also return the result from the recursion itself.
You also can't set the count to zero in the min_num function because then the count will constantly be set to zero when you run the recursion.
Here is your code with these two small changes:
def min_num(nums, x, count=0):
f_ele = nums[0]
l_ele = nums[-1]
if min(x - f_ele, x - l_ele) >= 0:
count += 1
# modifying x and nums
if x - f_ele == min(x - f_ele, x - l_ele):
x = x - f_ele
nums.remove(f_ele)
else:
x = x - l_ele
nums.remove(l_ele)
# Comparing x to use recursion or return the count
if x != 0:
return min_num(nums, x, count)
else:
return count
elif x == 0:
return count
else:
return -1
nums = [1, 1, 4, 2, 3]
x = 5
print(min_num(nums, x))
This returns 2
def find_min(x,arrayin):
if (len(arrayin)==0 or x==-1):
return -1
elif x==0:
return len(arrayin)
left=arrayin[0]
right=arrayin[len(arrayin)-1]
if x-min(left,right)<0:
return find_min(-1,arrayin)
if x-max(left,right)>=0:
if left>right: ##Remove left
return find_min(x-left,arrayin[1:len(arrayin)])
else:##Remove right
return find_min(x-right,arrayin[0:len(arrayin)-1])
elif x-min(left,right)>=0:
if left>right: ##Remove left
return find_min(x-right,arrayin[0:len(arrayin)-1])
else:
return find_min(x-left,arrayin[1:len(arrayin)])
nums = [3,2,20,1,1,3]
x = 10
res=find_min(x,nums)
if res==-1:
print("-1")
else:
Answer=len(nums)-res
print(str(Answer))
I am trying to solve the J5/S2 Escape Room Problem from the CCC'2020. I can't figure out why the recursion does not stop at a return statement.
Here is the link to the problems:
https://cemc.uwaterloo.ca/contests/computing/2020/ccc/juniorEF.pdf
def find_route(start_num, grid, visited=[]):
factors = []
result = 'no'
for num in range(1, start_num + 1):
if start_num % num == 0:
paired_factor = int(start_num/num)
if num <= len(grid) and paired_factor <= len(grid[0]):
factors.append((int(num), paired_factor))
print('xxxx', factors, start_num)
for m, n in factors:
x = m - 1
y = n - 1
if m == len(grid) and n == len(grid[0]):
return 'yes'
raise SystemExit(0)
else:
if (x, y) in visited:
continue
else:
visited.append((x, y))
start_num = grid[x][y]
find_route(start_num, grid, visited=visited)
print('why continue')
return result
def main():
grid = [[3, 10, 8, 14],
[1, 11, 12, 12],
[6, 2, 3, 9]]
start_num = grid[0][0]
result = find_route(start_num, grid)
print(result)
if __name__ == "__main__":
main()
You need to return your recursion.
This is the relevant snippet:
else:
visited.append((x, y))
start_num = grid[x][y]
> return find_route(start_num, grid, visited=visited)
Personally I would consider starting from the end, as it would be more deterministic to find the right product than the right factors.
I'm trying to solve the 15-Puzzle problem using IDA* algorithm and Manhattan heuristic.
I already implemented the algorithm from the pseudocode in this Wikipedia page (link).
Here's my code so far :
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
path = [initial_node]
while 1:
tmp = search(path, goal_state, 0, threshold)
if tmp == True:
return path, threshold
elif tmp == float('inf'):
return False
else:
threshold = tmp
def search(path, goal_state, g, threshold):
node = path[-1]
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.append(n)
tmp = search(path, goal_state, g + 1, threshold)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
path.pop()
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
I'm testing this code with a 3x3 Puzzle and here's the infinite loop! Due to the recursion I have some trouble testing my code...
I think the error might be here : tmp = search(path, goal_state, g + 1, threshold) (in the search function). I'm adding only one to the g cost value. It should be correct though, because I can only move a tile 1 place away.
Here's how to call the IDA() function:
initial_state = np.array([8 7 3],[4 1 2],[0 5 6])
goal_state = np.array([1 2 3],[8 0 4],[7 6 5])
IDA(initial_state, goal_state)
Can someone help me on this ?
There are couple of issues in your IDA* implementation. First, what is the purpose of the variable path? I found two purposes of path in your code:
Use as a flag/map to keep the board-states that is already been visited.
Use as a stack to manage recursion states.
But, it is not possible to do both of them by using a single data structure. So, the first modification that your code requires:
Fix-1: Pass current node as a parameter to the search method.
Fix-2: flag should be a data structure that can perform a not in query efficiently.
Now, fix-1 is easy as we can just pass the current visiting node as the parameter in the search method. For fix-2, we need to change the type of flag from list to set as:
list's average case complexity for x in s is: O(n)
set's
Average case complexity for x in s is: O(1)
Worst case complexity for x in s is: O(n)
You can check more details about performance for testing memberships: list vs sets for more details.
Now, to keep the Node information into a set, you need to implement __eq__ and __hash__ in your Node class. In the following, I have attached the modified code.
import timeit
import numpy as np
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
#print("heuristic threshold: {}".format(threshold))
loop_counter = 0
while 1:
path = set([initial_node])
tmp = search(initial_node, goal_state, 0, threshold, path)
#print("tmp: {}".format(tmp))
if tmp == True:
return True, threshold
elif tmp == float('inf'):
return False, float('inf')
else:
threshold = tmp
def search(node, goal_state, g, threshold, path):
#print("node-state: {}".format(node.state))
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.add(n)
tmp = search(n, goal_state, g + 1, threshold, path)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def __repr__(self):
return np.array_str(self.state.flatten())
def __hash__(self):
return hash(self.__repr__())
def __eq__(self, other):
return self.__hash__() == other.__hash__()
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
initial_state = np.array([[8, 7, 3],[4, 1, 2],[0, 5, 6]])
goal_state = np.array([[1, 2, 3],[8, 0, 4],[7, 6, 5]])
start = timeit.default_timer()
is_found, th = IDA(initial_state, goal_state)
stop = timeit.default_timer()
print('Time: {} seconds'.format(stop - start))
if is_found is True:
print("Solution found with heuristic-upperbound: {}".format(th))
else:
print("Solution not found!")
Node: Please double check your Node.nextnodes() and manhattan_heuristic() methods as I did not pay much attention in those areas. You can check this GitHub repository for other algorithmic implementations (i.e., A*, IDS, DLS) to solve this problem.
References:
Python Wiki: Time Complexity
TechnoBeans: Performance for testing memberships: list vs tuples vs sets
GitHub: Puzzle Solver (by using problem solving techniques)
I'm working on developing a minesweeper clone in python and am having trouble getting the reveal function to work. Currently, I get the following infinite error messages:
File "/Users/home/Desktop/minesweeper.py", line 79, in uncover_cells
uncover_cells(i - 1, j, board)
where uncover_cells is defined as follows (EDIT: added simpler example of problem at end of post):
def uncover_cells(i, j, board):
length = len(board)
width = len(board[0])
if i > -1 and j > -1 and i < length and j < width and not board[i][j].visited:
board[i][j].visited = True
# unproblematic code removed
uncover_cells(i + 1, j, board)
uncover_cells(i - 1, j, board)
uncover_cells(i, j + 1, board)
uncover_cells(i, j - 1, board)
uncover_cells(i + 1, j + 1, board)
uncover_cells(i + 1, j - 1, board)
uncover_cells(i - 1, j + 1, board)
uncover_cells(i - 1, j - 1, board)
return
The original call is:
b, locs = setup_game(100, 100, 50)
uncover_cells(0, 0, b)
I don't think that the recursion limit has been reached and fear there might be a logic bug. Any input would be greatly appreciated.
Other code that might be important: each element of board is of type Cell:
class Cell:
def __init__(self, isMine, loc, visited = False, flagged = False):
self.visited = visited # think of visited as 'uncovered'
self.flagged = flagged
self.isMine = isMine
self.x = loc[0]
self.y = loc[1]
self.label = 0
Here's how the board is setup:
def setup_game(length, width, n_mines):
idx = [(i, j) for j in range(width) for i in range(length)]
board = [[None for j in range(width)] for i in range(length)]
mine_locs = random.sample(idx, n_mines)
for i, j in idx:
if (i, j) in mine_locs:
board[i][j] = Cell(isMine = True, loc = (i, j))
else:
board[i][j] = Cell(isMine = False, loc = (i, j))
return board, mine_locs
EDIT: here's the simplest instance of my problem:
def simple_fill(i, j, b):
length = len(b)
width = len(b[0])
if i > -1 and j > -1 and i < length and j < width and b[i][j] != 1:
b[i][j] == 1
simple_fill(i + 1, j, b)
simple_fill(i - 1, j, b)
simple_fill(i, j + 1, b)
simple_fill(i, j - 1, b)
simple_fill(i + 1, j + 1, b)
simple_fill(i + 1, j - 1, b)
simple_fill(i - 1, j + 1, b)
simple_fill(i - 1, j - 1, b)
return
original call:
b = [[0 for j in range(100)] for i in range(100)]
simple_fill(0, 0, b)
In simple_fill():
b[i][j] == 1 # You have.
b[i][j] = 1 # Should be.
Using this code, your uncover_cells() works ... but only for small n. After that, we hit maximum recursion depth.
class Cell(object):
def __init__(self, i, j):
self.i = i
self.j = j
self.visited = False
def main():
n = 30 # Works ... but not, for example, for 40.
board = [[Cell(i,j) for j in range(n)] for i in range(n)]
uncover_cells(0, 0, board)
for row in board:
for cell in row:
assert cell.visited
main()
I reimplemented simple_fill with a stack:
def simple_fill(x, y, b):
length = len(b)
width = len(b[0])
stack = [(x,y)]
while len(stack) > 0:
i, j = stack.pop()
if i > -1 and j > -1 and i < length and j < width :
if b[i][j] != 1:
b[i][j] = 1
stack.append((i + 1, j))
stack.append((i - 1, j))
stack.append((i, j + 1))
stack.append((i, j - 1))
stack.append((i + 1, j + 1))
stack.append((i + 1, j - 1))
stack.append((i - 1, j + 1))
stack.append((i - 1, j - 1))
Hopefully this is helpful for someone in the future (https://xkcd.com/979/)
In Z3Py, how can I check if equation for given constraints have only one solution?
If more than one solution, how can I enumerate them?
You can do that by adding a new constraint that blocks the model returned by Z3.
For example, suppose that in the model returned by Z3 we have that x = 0 and y = 1. Then, we can block this model by adding the constraint Or(x != 0, y != 1).
The following script does the trick.
You can try it online at: http://rise4fun.com/Z3Py/4blB
Note that the following script has a couple of limitations. The input formula cannot include uninterpreted functions, arrays or uninterpreted sorts.
from z3 import *
# Return the first "M" models of formula list of formulas F
def get_models(F, M):
result = []
s = Solver()
s.add(F)
while len(result) < M and s.check() == sat:
m = s.model()
result.append(m)
# Create a new constraint the blocks the current model
block = []
for d in m:
# d is a declaration
if d.arity() > 0:
raise Z3Exception("uninterpreted functions are not supported")
# create a constant from declaration
c = d()
if is_array(c) or c.sort().kind() == Z3_UNINTERPRETED_SORT:
raise Z3Exception("arrays and uninterpreted sorts are not supported")
block.append(c != m[d])
s.add(Or(block))
return result
# Return True if F has exactly one model.
def exactly_one_model(F):
return len(get_models(F, 2)) == 1
x, y = Ints('x y')
s = Solver()
F = [x >= 0, x <= 1, y >= 0, y <= 2, y == 2*x]
print get_models(F, 10)
print exactly_one_model(F)
print exactly_one_model([x >= 0, x <= 1, y >= 0, y <= 2, 2*y == x])
# Demonstrate unsupported features
try:
a = Array('a', IntSort(), IntSort())
b = Array('b', IntSort(), IntSort())
print get_models(a==b, 10)
except Z3Exception as ex:
print "Error: ", ex
try:
f = Function('f', IntSort(), IntSort())
print get_models(f(x) == x, 10)
except Z3Exception as ex:
print "Error: ", ex
The python function below is a generator of models for formulas that contain both constants and functions.
import itertools
from z3 import *
def models(formula, max=10):
" a generator of up to max models "
solver = Solver()
solver.add(formula)
count = 0
while count<max or max==0:
count += 1
if solver.check() == sat:
model = solver.model()
yield model
# exclude this model
block = []
for z3_decl in model: # FuncDeclRef
arg_domains = []
for i in range(z3_decl.arity()):
domain, arg_domain = z3_decl.domain(i), []
for j in range(domain.num_constructors()):
arg_domain.append( domain.constructor(j) () )
arg_domains.append(arg_domain)
for args in itertools.product(*arg_domains):
block.append(z3_decl(*args) != model.eval(z3_decl(*args)))
solver.add(Or(block))
x, y = Ints('x y')
F = [x >= 0, x <= 1, y >= 0, y <= 2, y == 2*x]
for m in models(F):
print(m)
Referencing http://theory.stanford.edu/~nikolaj/programmingz3.html#sec-blocking-evaluations
def all_smt(s, initial_terms):
def block_term(s, m, t):
s.add(t != m.eval(t))
def fix_term(s, m, t):
s.add(t == m.eval(t))
def all_smt_rec(terms):
if sat == s.check():
m = s.model()
yield m
for i in range(len(terms)):
s.push()
block_term(s, m, terms[i])
for j in range(i):
fix_term(s, m, terms[j])
yield from all_smt_rec(terms[i:])
s.pop()
yield from all_smt_rec(list(initial_terms))
This indeed performs quite better from Leonardo's own answer (considering his answer is quite old)
start_time = time.time()
v = [BitVec(f'v{i}',3) for i in range(6)]
models = get_models([Sum(v)==0],8**5)
print(time.time()-start_time)
#211.6482105255127s
start_time = time.time()
s = Solver()
v = [BitVec(f'v{i}',3) for i in range(6)]
s.add(Sum(v)==0)
models = list(all_smt(s,v))
print(time.time()-start_time)
#13.375828742980957s
Splitting the search space into disjoint models creates a huge difference as far as I have observed
The answer given by Himanshu Sheoran cites the paper https://theory.stanford.edu/%7Enikolaj/programmingz3.html#sec-blocking-evaluations
Unfortunately there was a bug in the implementation given in the paper at that time which was quoted in that answer. The function has since been corrected.
For posterity, here's the correct version of the code:
def all_smt(s, initial_terms):
def block_term(s, m, t):
s.add(t != m.eval(t, model_completion=True))
def fix_term(s, m, t):
s.add(t == m.eval(t, model_completion=True))
def all_smt_rec(terms):
if sat == s.check():
m = s.model()
yield m
for i in range(len(terms)):
s.push()
block_term(s, m, terms[i])
for j in range(i):
fix_term(s, m, terms[j])
yield from all_smt_rec(terms[i:])
s.pop()
yield from all_smt_rec(list(initial_terms))