NQueens - Recursive BackTracking Question - python

I'm currently learning BackTracking algorithms with Python and the first question everyone typically starts with is NQueens. NQueens is where you take a board of size N x N and you have to determine where to place N queens, in such an order they are not attacked by any other queen.
Example:
N = 5
['Q', 0, 0, 0, 0]
[0, 0, 'Q', 0, 0]
[0, 0, 0, 0, 'Q']
[0, 'Q', 0, 0, 0]
[0, 0, 0, 'Q', 0]
Currently, my algorithm returns ALL Possible Solutions. How do I produce ONE outcome. For instance, when N = 8, there are 92 optimal outcomes, how do I just return One Outcome instead of printing 92 separate Outcomes.
#This is our main recursive function that will determine optimal outcome
def NQueens(row,Current,N):
#this tells us when to stop
if row == N:
print("\n")
return printBoard(Current)
for choice in range(0,N):
if isValid(row,choice,Current,N):
#Place Queen in appropriate spot
Current[row][choice] = "Q"
#Go to next row
NQueens(row+1,Current,N)
Current[row][choice] = 0
return "All Solutions Found"
#This function determines whether we can put a Queen in a certain orientation on the board
def isValid(row,col,Current,N):
#check whether current state of game has queen in row/column
for i in range(0,N):
#checks column/row and sees whether a queen exists already
if Current[row][i] == "Q" or Current[i][col] == "Q":
return False
# Check upper diagonal on right side
i = row-1
j = col + 1
#Do while row is greater than 0 and column is less than N
while i >= 0 and j < N:
if Current[i][j] == "Q":
return False
i -= 1
j += 1
# Check upper diagonal on left side
#Do while row is greater than 0 and column is greater than N
i = row-1
j = col - 1
while i >= 0 and j >= 0:
if Current[i][j] == "Q":
return False
i -= 1
j -= 1
#If we pass the diagonal/row/column tests, we can then determine this is a valid move
return True
###############################################################################################################
#These functions deal with board creation and printing them in a proper format
def generateBoard(N):
#generate board based on User Input N in Main()
Board = [[0 for i in range(0,N)] for j in range(0,N)]
return Board
def printBoard(arr):
#Loop through each row to print an organized board
for row in arr:
print(row)
def main():
#ask number from user
print("What sized board would you like?"
" Please input a number higher that 3: ")
#user input used to determine size of board
N = int(input())
#generate Board that will be used
Board = generateBoard(N)
#this is the current status of the board
printBoard(Board)
print("\n")
#Runs Algorithm
print(NQueens(0,Board,N))
if __name__ == "__main__":
main()

NQueens needs to communicate to its caller that a solution has been found; a simple return True will do if that's the case. I made the following changes to your code (your previous lines are commented):
def NQueens(row,Current,N):
#this tells us when to stop
if row == N:
print("\n")
#return printBoard(Current)
return True # found a solution, say so
for choice in range(0,N):
if isValid(row,choice,Current,N):
#Place Queen in appropriate spot
Current[row][choice] = "Q"
#Go to next row
# NQueens(row+1,Current,N)
if NQueens(row+1,Current,N): # recursive call found a solution, let's stop
return True
Current[row][choice] = 0
#return "All Solutions Found"
And in main():
# ...
#Runs Algorithm
#print(NQueens(0,Board,N))
if NQueens(0,Board,N):
printBoard(Board) # found a solution, print it

I have fixed your code to have well-perfoming:
I merge this code https://www.geeksforgeeks.org/n-queen-problem-backtracking-3/ and yours to get it.
count = 1
def printOut(arrayMap):
global count
print('{}: -'.format(count))
count += 1
for i in range(0, len(arrayMap)):
print(arrayMap[i])
# This is our main recursive function that will determine optimal outcome
def NQueens(currentRow, arrayMap, matrixSize):
# this tells us when to stop
if currentRow == matrixSize:
print()
printOut(arrayMap)
return True # found a solution, say so
res = False
for col in range(0, matrixSize):
if isValid(currentRow, col, arrayMap, matrixSize):
# Place Queen in appropriate spot
arrayMap[currentRow][col] = 0
# Go to next row
# NQueens(row+1,Current,N)
res = NQueens(currentRow + 1, arrayMap, matrixSize) or res # recursive call found a solution, let's stop
arrayMap[currentRow][col] = 1
# return "All Solutions Found"
return res
# This function determines whether we can put a Queen in a certain orientation on the board
def isValid(row, col, arrayMap, matrixSize):
# check whether current state of game has queen in row/column
for i in range(0, matrixSize):
# checks column/row and sees whether a queen exists already
if arrayMap[row][i] == 0 or arrayMap[i][col] == 0:
return False
# Check upper diagonal on right side
i = row - 1
j = col + 1
# Do while row is greater than 0 and column is less than N
while i >= 0 and j < matrixSize:
if arrayMap[i][j] == 0:
return False
i -= 1
j += 1
# Check upper diagonal on left side
# Do while row is greater than 0 and column is greater than N
i = row - 1
j = col - 1
while i >= 0 and j >= 0:
if arrayMap[i][j] == 0:
return False
i -= 1
j -= 1
# If we pass the diagonal/row/column tests, we can then determine this is a valid move
return True
###############################################################################################################
if __name__ == "__main__":
# ask number from user
print("What sized board would you like?")
N = int(input('Choose your size: '))
# generate Board that will be used
Board = [[1 for i in range(0, N)] for j in range(0, N)]
count = 0
NQueens(0, Board, N)

Related

Cant properly update cell status in my Game Of Life

cells not "dying" in may logocal loop. New cells geretion is normal, i think..
so cells not die
all calculations are into one numpy array, i dont have "second/old/new etc" arrays
sory for my bad English
def updateCells(grid: np.ndarray):
for i in range(GRID_WIDTH):
for j in range(GRID_HEIGHT):
#neighbours list contains 0, 1
neighbours = getNeighbours(i, j, grid)
#sum of alive neighbours
count = sum(neighbours)
if grid[i][j] == 1:
if count < 2:
grid[i][j] = 0
continue
if count == 2 or count == 3:
grid[i][j] = 1
continue
if count > 3:
grid[i][j] = 0
continue
if grid[i][j] == 0:
if count == 3:
grid[i][j] = 1
continue
function for getting neighbours of one cell:
def getNeighbours(i, j, arr):
ax_0_y = np.size(arr, 1)
ax_1_x = np.size(arr, 0)
n = []
ofssets = [-1, 0, 1]
for x in ofssets:
for y in ofssets:
if x == 0 and y == 0: continue
if y+j < 0 or y+j == ax_0_y: continue
if x+i < 0 or x+i == ax_1_x: continue
n.append(arr[x+i][y+j])
return n
main loop:
def main():
grid = np.zeros((GRID_WIDTH, GRID_HEIGHT), dtype=numpy.short)
....
drawGrid(display) #drawing cells line separators
randomise(grid) #random gereation alive cells
while True:
for i in pg.event.get():
if i.type == pg.QUIT:
quit()
updateCells(grid)
drawCells(display, grid) #drawing squares
pg.display.update()
fpsClock.tick(UPDATE_TIME)
if __name__ == "__main__":
main()
You can’t update the grid as you go, because then cells you get to later in the scan will have some neighbors from the current generation and some from the new one and will get the wrong neighbor count.
You have to do the whole grid as if it all updates at the same time, which means either having a second grid to build the new generation in or creating a list of changes to make to the grid at the end of the scan.
Some of the cells might not die due to you changing the field before the checks, like mentioned in the previous answer. You also seem to have quiet a bit of unneccessary code, like
if count == 2 or count == 3:
grid[i][j] = 1
continue
For example.
If there is no dying cells at all, i would like you to show us the code that calls your function.

Can't get out of While loop(Python 3.9)

I'm a new at programming, I like solving this euler questions and I know there are solutions for this problem but it's not about the problem at all actually.
So, i managed to create a working function for finding example: 33. triangular number. It works but i couldn't manage to properly desing my while loop. I wanted to make it like, it starts from first triangular checks it's divisors make list of it's divisors, checks the length of the divisors, because problem wants "What is the value of the first triangle number to have over five hundred divisors?" . But i never managed to work the while loop. Thank you for reading.
nums = [1]
triangles = [1]
divisors = []
def triangularcreator(x):
if x == 1:
return 1
n = 1
sum = 0
while n!=0:
n += 1
nums.append(n)
for i in range(len(nums)):
sum += nums[i]
triangles.append(sum)
sum = 0
if x == len(triangles):
n = 0
return triangles[-1]
counter = 1
while True:
for i in range(1, triangularcreator(counter) + 1):
if triangularcreator(counter) % i == 0:
divisors.append(i)
if len(divisors) == 500:
print(triangularcreator(counter))
break
counter +=1
divisors.clear()
You should try to change a few things, starting with calculating just once the value of triangularcreator(counter) and assigning this value to a variable that you can use in different points of your code.
Second, you create a loop which will be calculate always triangularcreator(1). At the end of each iteration you increase the value of counter+=1, but then at the beginign of the new iteration you assignt it again value 1, so it will not progress as you expect. Try this few things:
counter = 1
while True:
triangle = triangularcreator(counter)
for i in range(1, triangle + 1):
if triangle % i == 0:
divisors.append(i)
if len(divisors) == 500:
print(triangle )
break
counter +=1
Also these two arrays nums = [1], triangles = [1] should be declared and initialized inside the def triangularcreator. Otherwise you would be appending elements in each iteration
Edit: I believe it is better to give you my own answer to the problem, since you are doing some expensive operations which will make code to run for a long time. Try this solution:
import numpy as np
factor_num = 0
n = 0
def factors(n):
cnt = 0
# You dont need to iterate through all the numbers from 1 to n
# Just to the sqrt, and multiply by two.
for i in range(1,int(np.sqrt(n)+1)):
if n % i == 0:
cnt += 1
# If n is perfect square, it will exist a middle number
if (np.sqrt(n)).is_integer():
return (cnt*2)-1
else:
return (cnt*2)-1
while factor_num < 500:
# Here you generate the triangle, by summing all elements from 1 to n
triangle = sum(list(range(n)))
# Here you calculate the number of factors of the triangle
factor_num = factors(triangle)
n += 1
print(triangle)
Turns out that both of your while loop are infinite either in triangularcreatorin the other while loop:
nums = [1]
triangles = [1]
divisors = []
def triangularcreator(x):
if x == 1:
return 1
n = 1
sum = 0
while n:
n += 1
nums.append(n)
for i in range(len(nums)):
sum += nums[i]
triangles.append(sum)
sum = 0
if len(triangles) >= x:
return triangles[-1]
return triangles[-1]
counter = 1
while True:
check = triangularcreator(counter)
for i in range(1, check + 1):
if check % i == 0:
divisors.append(i)
if len(divisors) >= 500:
tr = triangularcreator(counter)
print(tr)
break
counter +=1
Solution
Disclaimer: This is not my solution but is #TamoghnaChowdhury, as it seems the most clean one in the web. I wanted to solve it my self but really run out of time today!
import math
def count_factors(num):
# One and itself are included now
count = 2
for i in range(2, int(math.sqrt(num)) + 1):
if num % i == 0:
count += 2
return count
def triangle_number(num):
return (num * (num + 1) // 2)
def divisors_of_triangle_number(num):
if num % 2 == 0:
return count_factors(num // 2) * count_factors(num + 1)
else:
return count_factors((num + 1) // 2) * count_factors(num)
def factors_greater_than_triangular_number(n):
x = n
while divisors_of_triangle_number(x) <= n:
x += 1
return triangle_number(x)
print('The answer is', factors_greater_than_triangular_number(500))

Problem trying to implement 'Game of Life' in python as a beginner

So I am an absolute beginner in python and tried my hand at implementing Conway's game of life.
I am not using any libraries at all, my grid is just a 50x50 matrix of 1s and 0s.
The next_gen output I get is not matching the expected output but I couldn't figure out why, any help would be appreciated.
Here's my code:
def alive_neighbours(grid, r, c):
count = 0
if grid[r-1][c-1] == 1:
count += 1
if grid[r-1][c] == 1:
count += 1
if grid[r-1][c+1] == 1:
count += 1
if grid[r][c-1] == 1:
count += 1
if grid[r][c+1] == 1:
count += 1
if grid[r+1][c-1] == 1:
count += 1
if grid[r+1][c] == 1:
count += 1
if grid[r+1][c+1] == 1:
count += 1
return count
grid = [[0 for i in range(50)] for j in range(50)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1
def next_gen(grid):
new_grid = grid[:]
for r in range(1, 49):
for c in range(1, 49):
neighbour = alive_neighbours(grid, r, c)
if (r == 0 or c == 0) or (r == 49 or c == 49):
pass # I am yet to define edge case
else:
if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
new_grid[r][c] = 0
continue
elif grid[r][c] == 1 and (neighbour == 2 or 3):
continue
elif grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
continue
else:
continue
grid = new_grid[:]
def printf(grid):
for r in range(50):
for c in range(50):
if grid[r][c] == 1:
print("*", end=" ")
else:
print(" ", end=" ")
print("")
x = 0
while x != '-1':
x = (input("x: "))
printf(grid)
next_gen(grid)
I also tried rewriting my next_gen function, but using that there is absolutely no change in the matrix
next_gen:
def next_gen(grid):
new_grid = grid[:]
for r in range(1, 49):
for c in range(1, 49):
neighbour = alive_neighbours(grid, r, c)
if (r == 0 or c == 0) or (r == 49 or c == 49):
pass
else:
if grid[r][c] == 1 and neighbour == 2 or 3:
continue
if grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
continue
if grid[r][c] == 1:
new_grid[r][c] = 0
continue
grid = new_grid[:]
As bruno said in his answer there are a few issues in your code, He already told about your issue with grid and how allocating to it in the function actually points the local scope version to the new grid and not the global scope one. He also covers how to resolve this.
The other issue you will have is that you have undertood that just doing new_grid = grid will mean that new_grid and grid point at the same list. So to prevent this you have correctly done new_grid = grid[:] as this will create a new list in memory and copy the data from the grid list. However thats a shallow copy, so you will create a new list object but copy all the list references inside your list. we can demonstrate this by doing a shallow copy of a list and then changing a value in the new list.
grid_size = 2
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
new_grid = grid[:]
new_grid[1][1] = "R"
print("grid:", grid)
print("newg:", new_grid)
#output#
grid: [[0, 0], [0, 'R']]
newg: [[0, 0], [0, 'R']]
So you can see that changing the inner list in one will change the inner list in the other. so you need to do a deep copy of the list so that your not changing the original grid as you go. Since conways states are based on the original grid state and squares changing shouldnt impact the others. I think your already aware of this concept.
I also made a change to the alive neighbours to simplyfy it. Below is a quick draft adaptation. when you run it you should see your glider heading off to the bottom right corner
from copy import deepcopy
def alive_neighbours(grid, r, c):
differences = (0, -1, +1)
cells_in_square = [(r + a, c + b) for a in differences for b in differences]
total = 0
for x,y in cells_in_square[1:]:
try:
if x >=0 and y>=0:
total += grid[x][y]
except IndexError as ie:
pass #ignore index errors as at the edge of the grid
return total
def next_gen(grid):
new_grid = deepcopy(grid)
for r in range(len(grid)):
for c in range(len(grid)):
neighbour = alive_neighbours(grid, r, c)
if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
new_grid[r][c] = 0
elif grid[r][c] == 0 and neighbour == 3:
new_grid[r][c] = 1
return new_grid
def printf(grid):
for r in grid:
for c in r:
if c == 1:
print("*", end=" ")
else:
print(" ", end=" ")
print("")
grid_size = 50
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1
while True:
x = (input("press enter to see next grid: "))
if x:
break
printf(grid)
grid = next_gen(grid)
UPDATE
other then the glider you started with the below is a nice start for a cool exploder
grid_size = 50
grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
grid[25][25] = 1
grid[26][24] = 1
grid[26][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][26] = 1
grid[28][25] = 1
There are actually quite a few issues with your code, but the first and main problem is with your updated grid never being returned to the caller.
Here:
def next_gen(grid):
new_grid = grid[:]
# ...
# code modifying new_grid
# ...
grid = new_grid[:]
within the function, grid is a local name. Rebinding this name at the end of the function only affects the local name, it doesn't do anything to the global one. you should read this reference article for more in-depth explanations.
What you want is to return the grid to the caller instead:
def next_gen(grid):
new_grid = grid[:]
# ...
# code modifying new_grid
# ...
# return the new grid to the caller
return new_grid
x = 0
while x != '-1':
x = (input("x: "))
printf(grid)
# replace previous grid with the new one
grid = next_gen(grid)
For some other issues, this:
if grid[r][c] == 1 and neighbour == 2 or 3:
doesn't do what you think it does.
The neighbour == 2 or 3 part is actually executed as (neighbour == 2) or 3. Now the or operator returns either the first of it's operands that is not false, or the last of it's operand. Note that "that is not false" means "that does not have a false value in a boolean context" (all Python objects have a "truth" value, and for numbers, all numbers are true expect for zeros). So in the end, if the neighbours is different from 2, the value of neighbour == 2 or 3is3, whatever the value ofneighbour`:
>>> foo
1
>>> foo == 1 or 3
True
>>> foo == 2 or 3
3
>>>
And since 3 is true, the expression will have a true value even if neighbours is actually 1 or 4 or 5 or etc...
TL;DR: you want either:
`neighbour == 2 or neighbour == 3`
or more simply:
`neighbour in (2, 3)`

Finding the number of paths in a maze with obstacles

I've been working on this leetcode problem, which is essentially finding the number of valid paths in a maze given some obstacleGrid matrix. If obstacleGrid[i][j] == 1, then we have an obstacle at (i,j) and we have zero otherwise, which a valid spot. We can only move down and right with the goal of starting from the upper left to the bottom right.
I have written the code below:
def uniquePathsWithObstacles(self, obstacleGrid):
# obstruction at the start
if (obstacleGrid[0][0] == 1): return 0
# obstruction at the end
if (obstacleGrid[-1][-1] == 1): return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
memo = [[0] * n] * m
# starting move
memo[0][0] = 1
# now check the first row
for j in range(1, n):
memo[0][j] = 1 if (obstacleGrid[0][j] == 0 and memo[0][j-1] != 0) else 0
# now check the first column
for i in range(1, m):
memo[i][0] = 1 if (obstacleGrid[i][0] == 0 and memo[i-1][0] != 0) else 0
# now check everything else
for i in range(1, m):
for j in range(1, n):
if (obstacleGrid[i][j] == 1): memo[i][j] = 0
else: memo[i][j] = memo[i-1][j] + memo[i][j-1]
return memo[-1][-1]
I took the obvious DP approach and I know the idea works but something is wrong with the code; for some reason I don't think my memo matrix is being updated properly? I feel like the problem is staring at me in the face but for some reason I can't see it. Any help appreciated!
Edit: For obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] and if I had a print(memo) right before the return statement, I get [[1, 1, 2], [1, 1, 2], [1, 1, 2]]. This happens to give me the right answer, but the memo matrix is wrong!
One problem lies in the line memo = [[0] * n] * m.
This does not really create mcopies of the same list, but instead, it only creates the [0] * n list once and then creates memo as a list of m references to this list. Any change to any of these lists therefore modifies all other lists!
You can try this yourself:
memo = [[0] * 3] * 4
memo[0][1] = 1
print(memo)
This gives [[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0]].
Instead, you have to initialize each list on their own, e.g.,
memo = []
for i in range(m):
memo.append([0] * n)
I just tried to do this with recursion as an comparison rather than an answer.
import numpy as np
def number_of_paths(obstacles):
"""
Calculate the number of paths available in a maze with obstacles, with only right and down moves, from top left
to bottom right.
Args:
obstacles (ndarray): binary matrix with 1 representing obstacle
Returns:
int: the number of paths
"""
if obstacles[0,0] == 1:
raise ValueError # cannot start on an obstacle
count = 0
if obstacles.shape == (2,1):
return 1
if obstacles.shape == (1,2):
return 1
if obstacles.shape[1] > 1 and obstacles[0,1] == 0:
count += number_of_paths(obstacles[:,1:])
if obstacles.shape[0] > 1 and obstacles[1,0] == 0:
count += number_of_paths(obstacles[1:,:])
return count
your code is correct and 1 line must be updated per the below:
def uniquePathsWithObstacles(self, obstacleGrid):
# obstruction at the start
if (obstacleGrid[0][0] == 1): return 0
# obstruction at the end
if (obstacleGrid[-1][-1] == 1): return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
memo = [[0] * n for i in range(m)]
# starting move
memo[0][0] = 1
# now check the first row
for j in range(1, n):
#memo[0][j] = 1 if (obstacleGrid[0][j] == 0 and memo[0][j-1] != 0) else 0
memo[0][j] = 1 if (obstacleGrid[0][j] == 0 and memo[0][j-1] != 0) else 0
# now check the first column
for i in range(1, m):
memo[i][0] = 1 if (obstacleGrid[i][0] == 0 and memo[i-1][0] != 0) else 0
# now check everything else
for i in range(1, m):
for j in range(1, n):
if (obstacleGrid[i][j] == 1): memo[i][j] = 0
else: memo[i][j] = memo[i-1][j] + memo[i][j-1]
return memo[-1][-1]

Sudoku Verifications blocking any move attempt

So I'm trying to make a sudoku game and it's coming along fairly well except at the state I'm at the program gives me an error for every move I try to make. isRow checks if a move is possible on the row and so on and isPossible combines the 3 to check if a move is possible or not. Can anyone tell me what is wrong with my verifications?
board = list(range(81)) # Creates a board with 9*9 elements
board2 = board # Duplicates this board for later verfications
def showLine(start, end): # Prints each line of the sudoku puzzle
for i in range(start, end):
if i < end-1:
print(str(board[i]), '|', end =" ")
else:
print(str(board[i]))
def showGroup(start): # Prints each 9*3 group of regions (1/3 of the puzzle)
for i in range(3):
end = start + 9
showLine(start, end)
start += 9
if start != 81:
print('----------+-----------+-----------')
def show(): # Prints whole puzzle
for i in range(0, 55, 27):
showGroup(i)
rows = []
columns = []
groups = []
for i in range(0, 80, 9): # Cretes a list of rows
rows.append(board[i:i+9])
for i in range(9): # Creates a list of columns
columns.append(board[i::9])
temp = 0
for x in range(0, 61, 3): # Creates a list of groups (3*3 regions)
group = []
for i in range(3):
for j in range(3):
temp = x + 9*i + j
group.append(temp)
groups.append(group)
#Duplicating rows columns and groups for verifications
rows2 = rows
columns2 = columns
groups2 = groups
def isRow(cell, number): # Checking if an introduces number isn't repeating in the row
x = 0
for row in rows2:
if cell in row:
x = rows2.index(row)
if number in rows[x]:
return False
else:
return True
def isColumn(cell, number):): # Checking if an introduces number isn't repeating in the column
x = 0
for column in columns2:
if cell in column:
x = columns2.index(column)
if number in columns[x]:
return False
else:
return True
def isGroup(cell, number):): # Checking if an introduces number isn't repeating in the group
x = 0
for group in groups2:
if cell in group:
x = groups2.index(group)
if number in groups[x]:
return False
else:
return True
def isPossible(cell, number): # Combines the last 3 verifications
if isRow(cell, number) and isColumn(cell, number) and isGroup(cell, number):
return True
else:
return False
for i in board: # Fills the board with 0's to not interfere with the game
board[i] = 0
while True:
show()
selected_cell = int(input("Please select an available cell: "))
number = int(input("Please select a number for that cell: "))
if isPossible(selected_cell, number):
board[selected_cell] = number
else:
print("Error, please try again")

Categories

Resources