How to make this faster
MRAB
python at mrabarnett.plus.com
Fri Jul 5 12:25:54 EDT 2013
On 05/07/2013 16:17, Helmut Jarausch wrote:
> On Fri, 05 Jul 2013 15:45:25 +0100, Oscar Benjamin wrote:
>
>> Presumably then you're now down to the innermost loop as a bottle-neck:
>>
>> Possibilities= 0
>> for d in range(1,10) :
>> if Row_Digits[r,d] or Col_Digits[c,d] or Sqr_Digits[Sq_No,d] : continue
>> Possibilities+= 1
>>
>> If you make it so that e.g. Row_Digits[r] is a set of indices rather
>> than a list of bools then you can do this with something like
>>
>> Possibilities = len(Row_Digits[r] | Col_Digits[c] | Sqr_Digits[Sq_No])
>>
>> or perhaps
>>
>> Possibilities = len(set.union(Row_Digits[r], Col_Digits[c],
>> Sqr_Digits[Sq_No]))
>>
>> which I would expect to be a little faster than looping over range
>> since the loop is then performed under the hood by the builtin
>> set-type.
>>
>> It just takes practice.
>
> indeed
>
>> It's a little less obvious in Python than in
>> low-level languages where the bottlenecks will be and which operations
>> are faster/slower but optimisation always involves a certain amount of
>> trial and error anyway.
>>
>>
>> Oscar
>
> I've tried the following version
>
> def find_good_cell() :
> Best= None
> minPoss= 10
> for r,c in Grid :
> if Grid[(r,c)] > 0 : continue
> Sq_No= (r//3)*3+c//3
> Possibilities= 9-len(Row_Digits[r] | Col_Digits[c] | Sqr_Digits[Sq_No])
> if ( Possibilities < minPoss ) :
> minPoss= Possibilities
> Best= (r,c)
>
> if minPoss == 0 : Best=(-1,-1)
> return Best
>
> All_digits= set((1,2,3,4,5,6,7,8,9))
>
> def Solve(R_Cells) :
> if R_Cells == 0 :
> print("\n\n++++++++++ S o l u t i o n ++++++++++\n")
> Print_Grid()
> return True
>
> r,c= find_good_cell()
> if r < 0 : return False
> Sq_No= (r//3)*3+c//3
>
> for d in All_digits - (Row_Digits[r] | Col_Digits[c] | Sqr_Digits[Sq_No]) :
> # put d into Grid
> Grid[(r,c)]= d
> Row_Digits[r].add(d)
> Col_Digits[c].add(d)
> Sqr_Digits[Sq_No].add(d)
>
> Success= Solve(R_Cells-1)
>
> # remove d again
> Grid[(r,c)]= 0
> Row_Digits[r].remove(d)
> Col_Digits[c].remove(d)
> Sqr_Digits[Sq_No].remove(d)
>
> if Success :
> Zuege.append((d,r,c))
> return True
>
> return False
>
> which turns out to be as fast as the previous "dictionary only version".
> Probably, set.remove is a bit slow
>
For comparison, here's my solution:
from collections import Counter
problem = '''
_________
_____3_85
__1_2____
___5_7___
__4___1__
_9_______
5______73
__2_1____
____4___9
'''
# Build the grid.
digits = "123456789"
grid = []
for row in problem.splitlines():
if not row:
continue
new_row = []
for cell in row:
if cell.isdigit():
new_row.append({cell})
else:
new_row.append(set(digits))
grid.append(new_row)
# Solve the grid.
changed = True
while changed:
changed = False
# Look for cells that contain only one digit.
for r in range(9):
for c in range(9):
if len(grid[r][c]) == 1:
digit = list(grid[r][c])[0]
# Remove from other cells in same row.
for c2 in range(9):
if c2 != c and digit in grid[r][c2]:
grid[r][c2].remove(digit)
changed = True
# Remove from other cells in same column.
for r2 in range(9):
if r2 != r and digit in grid[r2][c]:
grid[r2][c].remove(digit)
changed = True
# Remove from other cells in the same block of 9.
start_row = r - r % 3
start_column = c - c % 3
for r2 in range(start_row, start_row + 3):
for c2 in range(start_column, start_column + 3):
if (r2, c2) != (r, c) and digit in grid[r2][c2]:
grid[r2][c2].remove(digit)
changed = True
# Look for digits that occur in only one cell in a row.
for r in range(9):
counts = Counter()
for c in range(9):
counts += Counter(grid[r][c])
unique = {digit for digit, times in counts.items() if times == 1}
for c in range(9):
if len(grid[r][c]) > 1 and len(grid[r][c] & unique) == 1:
grid[r][c] &= unique
changed = True
# Look for digits that occur in only one cell in a column.
for c in range(9):
counts = Counter()
for r in range(9):
counts += Counter(grid[r][c])
unique = {digit for digit, times in counts.items() if times == 1}
for r in range(9):
if len(grid[r][c]) > 1 and len(grid[r][c] & unique) == 1:
grid[r][c] &= unique
changed = True
# Look for digits that occur in only one cell in a block of 9.
for start_row in range(0, 9, 3):
for start_column in range(0, 9, 3):
counts = Counter()
for r in range(start_row, start_row + 3):
for c in range(start_column, start_column + 3):
counts += Counter(grid[r][c])
unique = {digit for digit, times in counts.items() if times == 1}
for r in range(start_row, start_row + 3):
for c in range(start_column, start_column + 3):
if len(grid[r][c]) > 1 and len(grid[r][c] & unique) == 1:
grid[r][c] &= unique
changed = True
# Display the solution.
for row in grid:
for cell in row:
print("".join(sorted(cell)), end=" ")
print()
More information about the Python-list
mailing list