Brute Force Sudoku Recursion in Python - python

I have tried to implement the brute force approach to solving a Sudoku puzzle using Python, as explained in the following link: http://en.wikipedia.org/wiki/Sudoku_solving_algorithms#Brute-force_algorithm
I am aware there are other threads with the same question. I have read through all of them and still keep having problems.
Here is my code for recursion:
def recurse(x, k, grid):
if x > 8:
for line in grid:
print line
raw_input()
if grid[x][k] == '0':
for number in '123456789':
if clear(number, x, k, grid):
grid[x] = grid[x][0:k] + number + grid[x][k+1:]
k += 1
if k > 8:
x += 1
k = 0
recurse(x, k, grid)
k -= 1
if k < 0:
x -= 1
k = 8
else:
k += 1
if k > 8:
x += 1
k = 0
recurse(x, k, grid)
Basically I keep the Sudoku in an array of 9x9 slots, where grid[x] accesses the xth line of the entire Sudoku and grid[x][k] accesses the kth number at the xth line.
There is a function up there called 'clear' which finds out whether a certain number can fit in that slot or not. I have tested it many times and it works properly. Problem here is that the value 'x' never goes above 8, which means the Sudoku never reaches the end. The recursion stops before all the slots are filled in correctly. I am quite inexperienced in writing recursive methods so please bear with me.
I figured, if a number fits in a slot, then that number is placed within that slot and then k is incremented. If k is above 8, then it means it is end of the line so we skip to the next line. Then the recurse function is called again. If a recurse function ends without being able to call itself again, then this means no number fits in that slot so we have to go back. In that case, k is decremented.
So what is the problem here exactly?

You forgot to reset the cells before backtracking. You're leaving wrong numbers all over the place, and at some point there is no valid number to fill in any more, and execution stops.
Add grid[x] = grid[x][0:k] + '0' + grid[x][k+1:] after
k -= 1
if k < 0:
x -= 1
k = 8
to fix this.
To stop recursing when a solution has been found:
def recurse(x, k, grid):
if x>8:
return grid
if grid[x][k] == '0':
for number in '123456789':
if clear(number, x, k, grid):
grid[x] = grid[x][0:k] + number + grid[x][k+1:]
k += 1
if k > 8:
x += 1
k = 0
solution= recurse(x, k, grid)
if solution:
return solution
k -= 1
if k < 0:
x -= 1
k = 8
grid[x] = grid[x][0:k] + '0' + grid[x][k+1:]
else:
k += 1
if k > 8:
x += 1
k = 0
return recurse(x, k, grid)

Related

while infinite loop after 2 loops

I basically want to put each letter to a different list so that the first letter goes in the first list until the kth letter. Then the k+1th letter should go in the k-1th list and the next to the k-2th list etc. The fact is that after the second loop the while enters in an infinite loop
def f(s,k):
l = [[] for i in range(k)]
i = 0
while i < len(s):
for j in range(0,k):
l[j].append(s[i])
print(l)
i += 1
for k in range(k-2,-1,-1):
l[k].append(s[i])
print(l)
i += 1
return l
Don't use the same variable k for the second iteration variable and your function parameter. When the second loop is done, k will be set to 0. The next iteration of the while loop will then do for j in range(0, 0):, which doesn't do anything, followed by for k in range(-2, -1, -1):, which also doesn't do anything. So i never gets incremented any more, and you get stuck in the infinite loop.
Change for k to for m and you won't get the infinite loop. But you'll get an IndexError when accessing s[i] if len(s) is not a multiple of 2 * k - 1.
It would be better to iterate over the characters in s.
def f(s,k):
l = [[] for i in range(k)]
i = 0
inc = 1
for c in s:
l[i].append(c)
i += inc
# Check if we need to reverse direction
if inc == 1 and i == k:
inc = -1
i = k - 2
elif inc == -1 and i == -1:
inc = 1
i = 0
return l
As #Barmar already stated, the reason why your algorithm does not work are the for-loops. So you effectively skip the first letter, at least.
Also the usage of k is problematic.
However his proposed algorithm doesn't work like you want it to work, though.
In the end this is what #Barmar had in mind:
def f(string, k):
result = [[] for _ in range(k)]
bin_index = -1
inc = 1
for i in range(len(string)):
bin_index += inc
# reverse by setting the bin_index and changing the increment-step
if bin_index == k:
bin_index = (k - 1) - 1
inc = -1
# going forward again
if bin_index == 0 and i != 0:
inc = 1
result[bin_index].append(string[i])
return result
#Barmar:
You have a bug in your program when i returns from the back-pass. It will not be set to another bin-index so that always the character on index k+1 and k+2 (or multiples of them) will be in the same bin (the first list). But they should be in two different bins. So this would get rid of the bug:
def f(s, k):
l = [[] for i in range(k)]
i = 0
inc = 1
for c in s:
l[i].append(c)
i += inc
# Check if we need to reverse direction
if inc == 1 and i == k:
inc = -1
i = k - 2
elif inc == -1 and i == -1:
inc = 1
i = 1 # this should be 1 not 0
return l

How can I regroup my two for loops into one with only a tiny difference between the two?

I have the following code with the only difference being the j and the I position in my list. Is there any way to make it better by writing a function or something like that because I can't quite figure it out?
for i in range(dimension):
for j in range(dimension - 4):
k = 1
while k < 5 and gameboard[i][j+k] == gameboard[i][j]:
k += 1
if k == 5:
winner = gameboard[i][j]
for i in range(dimension):
for j in range(dimension - 4):
k = 1
while k < 5 and gameboard[j+k][i] == gameboard[j][i]:
k += 1
if k == 5:
winner = gameboard[j][i]
You could merge the two loops by inserting an additional nested loop that handles the permutations of i and j and the corresponding dimension deltas:
for i in range(dimension):
for j in range(dimension - 4):
for i,j,di,dj in [ (i,j,0,1), (j,i,1,0) ]:
k = 1
while k < 5 and gameboard[i+k*di][j+k*dj] == gameboard[i][j]:
k += 1
if k == 5:
winner = gameboard[i][j]
Generalized for all directions
Alternatively you could create a function that tells you if there is a win in a given direction and use that in a loop.
def directionWinner(board,i,j,di,dj):
player,pi,pj = board[i][j],i,j
# if player == empty: return
for _ in range(4):
pi,pj = pi+di, pj+dj
if pi not in range(len(board)): return
if pj not in range(len(board)): return
if board[pi][pj] != player: return
return player
Then use it to check all directions:
for pos in range(dimensions*dimensions):
i,j = divmod(pos,dimensions)
for direction in [ (0,1),(1,0),(1,1),(1,-1) ]:
winner = directionWinner(gameboard,i,j,*direction)
if winner is not None: break
else: continue; break
The directions are represented by the increase/decrease in vertical and horizontal coordinates (deltas) for each step of one. So [ (0,1),(1,0),(1,1),(1,-1) ] gives you "down", "across", "diagonal 1", diagonal 2" respectively.
Checking only from last move
The same idea can be used to check for a winner from a specific position (e.g. checking if last move is a win):
# count how may consecutive in a given direction (and its inverse)
def countDir(board,i,j,di,dj,inverted=False):
player,pi,pj = board[i][j],i,j
count = 0
while pi in range(len(board) and pj in range(len(board)):
if board[pi][pj] == player: count += 1
else: break
pi, pj = pi+di, pj+dj
if not inverted:
count += countDir(board,i,j,-di,-dj,True)-1
return count
def winnerAt(board,i,j):
for direction in [ (0,1),(1,0),(1,1),(1,-1) ]:
if countDir(board,i,j,*direction)>=5:
return board[i,j]
Then, after playing at position i,j, you can immediately know if the move won the game:
if winnerAt(gameboard,i,j) is not None:
print(gameboard[i][j],"wins !!!")
It's maybe a trivial change, but why don't you just put the second check in the first loop just by swapping indices and using another variable for the while part? I mean something like this:
def check_winner(gameboard, dimension):
for i in range(dimension):
for j in range(dimension - 4):
# perform the first check
k = 1
while k < 5 and gameboard[i][j+k] == gameboard[i][j]:
k += 1
if k == 5:
return gameboard[i][j]
# and the second check
l = 1
while l < 5 and gameboard[j + k][i] == gameboard[j][i]:
l += 1
if l == 5:
return gameboard[j][i]
Anyways, it's cleaner to just return if you found the winner, and avoid redundancy.

is there a way to improve the function that find a substring of a very large function

I tried to run this code but this function indeed consumes more time. I want to improve this code:
def minion_game(string):
k = 0
s = 0
for i in range(len(string)):
for j in range(i + 1, len(string) + 1):
ss = string[i:j]
if ss[0] in ['A', 'E', 'I', 'O', 'U']:
k += 1
else:
s += 1
if len(string) in range(0, 10 ** 6):
if string.isupper():
if k > s:
print(f"Kevin {k}")
if s > k:
print(f"Stuart {s}")
if k == s:
print("Draw")
Using the Counter class is usually pretty efficient in a case like this. This should be mostly similar to what you have done in terms of results, but hopefully much quicker.
from collections import Counter
k_and_s = Counter('k' if c in 'AEIOU' else 's' for c in string)
k, s = k_and_s['k'], k_and_s['s']
if k > s:
print(f'Kevin {k}')
elif k < s:
print(f'Stuart {s}')
else
print(f'Draw')
Zooming in on k_and_s = Counter('k' if c in 'AEIOU' else 's' for c in string), this uses comprehension in place of a loop. It is roughly equivalent to this:
k_and_s = Counter()
for c in string:
if c in 'AEIOU':
k_and_s['k'] += 1
else
k_and_s['s'] += 1
The answer by #jamie-deith is good and fast. It will process the complete works of Shakespeare in about 0.56 seconds on my computer. I gave up timing the original answer and modifications of it as it simply goes on and on.
This version is simple and produces the same answer in 0.26 seconds. I'm sure them are likely even faster answers:
with open("shakespeare.txt", encoding="utf-8") as file_in:
shakespeare = file_in.read().upper()
kevin = len([character for character in shakespeare if character in 'AEIOU'])
stuart = len(shakespeare) - kevin
if kevin > stuart:
print(f'Kevin {kevin}')
elif kevin < stuart:
print(f'Stuart {stuart}')
else:
print(f'Draw')
Taking the (perhaps doubtful) position that your code is doing what you intend, but slowly, I note:
The amount added to k or s for any value of i depends on how many times we go around the j loop. You're repeatedly testing the character at i (with the same result every time of course) and adding one to either s or k, as many times as we go around the loop.
So we don't need to actually go around the j loop; we can just add that amount on a single test. For the first character you go the same number of times around the loop as the length of the string, then reducing by one as you shift along the string.
So we can lose i and iterate through the string characters directly.
Then finally we don't report anything if the string is too long or not upper case, so we can just do that test first, and not even calculate in those circumstances.
def minion_game(string):
if len(string) < 10**6 and string.isupper():
k = 0
s = 0
j = len(string)
for ss in string:
if ss in 'AEIOU':
k += j
else:
s += j
j -= 1 # reducing amount to add
if k > s:
print(f"Kevin {k}")
elif s > k:
print(f"Stuart {s}")
else:
print("Draw")
As a hint for even faster options, I'll note that k+s is constant depending on the length of the string.
def minion_game(string):
k = 0
s = 0
l = len(string) # save length in a variable
for i in range(l):
for j in range(i + 1, l + 1):
ss = string[i] # take only the first
if ss in ['A', 'E', 'I', 'O', 'U']:
k += 1
else:
s += 1
if l in range(0, 10 ** 6):
if string.isupper():
if k > s: # change from three if's to if, elif, else
print(f"Kevin {k}")
elif s > k:
print(f"Stuart {s}")
else:
print("Draw")
I made a few edits that should speed up your code. They are described in comments on the lines. There seems to be some logic missing in the j-loop.
I'm not sure what you are doing on the line if l in range(0, 10 ** 6):. If you wanted to remove it, then it'd look like:
def minion_game(string):
k = 0
s = 0
l = len(string) # save length in a variable
for i in range(l):
for j in range(i + 1, l + 1):
ss = string[i] # take only the first
if ss in ['A', 'E', 'I', 'O', 'U']:
k += 1
else:
s += 1
# removed loop, which definitely saves time
if string.isupper():
if k > s: # change from thee if's to if, elif, else
print(f"Kevin {k}")
elif s > k:
print(f"Stuart {s}")
else:
print("Draw")

Decrease value in Python

Stuck on a problem where I am supposed to decrease the value of n by a value of k using Python Recursion.
If n = 10 and k = 2, my output should be:
10,8,6,4,2,0,2,4,6,8,10
n should decrease by k and then increase by k again.
I wrote the below code:
def pattern(n):
k = 2
if n <=0:
print(n, end= ",")
#pattern(n+k)
else:
print(n, end = ",")
pattern(n-k)
pattern(5)
but my output is:
5, 3, 1,-1
My answer should be:
5,3,1,-1,1,3,5
Here I've corrected your code:
def pattern(n, k, outer=True):
print(n, end=",")
if n > 0:
pattern(n - k, k, False)
if outer:
print(n)
else:
print(n, end=",")
pattern(10, 2)
pattern(5, 3)
I've made k an argument to pattern.
I've also fixed the recursive algorithm. I think the most crucial part is that
you need to print the number again after the recursive step.
There is also some logic to make sure no comma is printed at the end. This is a
little convoluted, and if you're willing to use generators there's a much nicer
way (that also removes the side-effects of the function, which is a plus):
def gen_pattern(n, k):
yield n
if n > 0:
yield from gen_pattern(n - k, k)
yield n
def fmt_gen_pattern(n, k):
return ",".join(map(str, gen_pattern(n, k)))
print(fmt_gen_pattern(10, 2))
print(fmt_gen_pattern(5, 3))
Here is the output generated by either of these:
10,8,6,4,2,0,2,4,6,8,10
5,2,-1,2,5
This is not the same output as you spelled out, but I'm pretty sure this is what
you meant.
Your method of recursion lacks the base case that will stop the recursion. Hence, it'll recurse infinitely and abort eventually.
Here are two correct approaches to do it.
def method1(current,direction,n,k):
if current<=0:
direction = 'Up'
if direction=='Up' and current>=n:
print(current,end=' ')
return
if direction=='Down':
print(current,end=' ')
method1(current-k,direction,n,k)
else:
print(current,end=' ')
method1(current+k,direction,n,k)
def method2(n,k):
if n<=0:
print(n,end=' ')
return
print(n,end=' ') # This print shows decreasing n
method2(n-k,k) # Recurse
print(n,end=' ') # This print shows increasing n
n = 5
k = 2
# assert k<=n, "k cannot be greater than n" # Optional check
print('Method 1')
method1(n,'Down',n,k)
print('\nMethod 2')
method2(n,k)
Output
Method 1
5 3 1 -1 1 3 5
Method 2
5 3 1 -1 1 3 5

smallest window contains all the elements in an array

I need to write a function to find the smallest window that contains all the elements in an array. Below is what I have tried:
def function(item):
x = len(set(item))
i = 0
j = len(item) - 1
result = len(item)
while i <= j:
if len(set(item[i + 1: j + 1])) == x:
result = min(result, len(item[i + 1: j + 1]))
i += 1
elif len(set(item[i:j])) == x:
result = min(result, len(item[i:j]))
j -= 1
else:
return result
return result
print(function([8,8,8,8,1,2,5,7,8,8,8,8]))
The time complexity is in O(N^2), Can someone help me to improve it to O(N) or better? Thanks.
You can use the idea from How to find smallest substring which contains all characters from a given string? for this specific case and get a O(N) solution.
Keep a counter for how many copies of each unique number is included in the window and move the end of the window to the right until all unique numbers are included at least once. Then move the start of the window until one unique number disappears. Then repeat:
from collections import Counter
def smallest_window(items):
element_counts = Counter()
n_unique = len(set(items))
characters_included = 0
start_enumerator = enumerate(items)
min_window = len(items)
for end, element in enumerate(items):
element_counts[element] += 1
if element_counts[element] == 1:
characters_included += 1
while characters_included == n_unique:
start, removed_element = next(start_enumerator)
min_window = min(end-start+1, min_window)
element_counts[removed_element] -= 1
if element_counts[removed_element] == 0:
characters_included -= 1
return min_window
>>> smallest_window([8,8,8,8,1,2,5,7,8,8,8,8])
5
This problem can be solved as below.
def lengthOfLongestSublist(s):
result = 0
#set a dictionary to store item in s as the key and index as value
d={}
i=0
j=0
while (j < len(s)):
#if find the s[j] value is already exist in the dictionary,
#move the window start point from i to i+1
if (s[j] in d):
i = max(d[s[j]] + 1,i)
#each time loop, compare the current length of s to the previouse one
result = max(result,j-i+1)
#store s[j] as key and the index of s[j] as value
d[s[j]] = j
j = j + 1
return result
lengthOfLongestSubstring([8,8,8,8,8,5,6,7,8,8,8,8,])
Output: 4
Set a dictionary to store the value of input list as key and index
of the list as the value. dic[l[j]]=j
In the loop, find if the current value exists in the dictionary. If
exist, move the start point from i to i + 1.
Update result.
The complexity is O(n).

Categories