[Algorithm] - 알고리즘 : 스도쿠


Joma class python에서 마지막 문제가 스도쿠 backtracking 문제였다.

단순히 재귀함수를 이용해 스도쿠 판을 채우는 문제였는데 오랜만에 풀어서 조금 시간이 걸렸다.

재귀에는 항상 탈출 조건이 있어야 한다. (해결이던 실패던)

스도쿠 조건


해결 조건

  • 모든 보드가 다 채워져야 한다
    • 총 합이 405이어야 한다. (아니면 실패)
  • 3가지 조건을 모두 만족하여야 한다.
    • 열에 숫자가 1~9까지 다 들어가 있어야 한다
    • 행에 숫자가 1~9까지 다 들어가 있어야 한다
    • 3X3칸에 숫자가 1~9까지 다 들어가 있어야 한다

재귀로 이걸 해결해보자.

이미지

def solve_helper(board, pos_y, pos_x):
	...

조건 체크


  1. 보드가 채워졌는지?
total = 0
isFill = True # True면 판이 다 채워진것

for col in range(len(board)):
	for row in range(len(board[0])):
		if board[col][row] == 0 : # 비어 있으면
			isFill = False
		total += board[col][row]

# 조건 완료 (다 채워짐)
if isFill:
	if total == 405:
		return board
	else:
		return None
  1. 보드 범위 벗어남
# 2. 보드 범위 벗어남 9*9
# 0 <= pos_x <= 8
if pos_x > len(board[0]):
  return None
# 0 <= pos_y <= 8
if pos_y > len(board):
  return None

재귀 반복부


  1. 해당 칸의 가능한 숫자 찾기
  • 행과 열, 그리고 3x3칸에 중복된 숫자를 제거하는 방식으로 해보자
# 반복부
# 3. 해당칸 숫자 범위 찾기
nums = [1,2,3,4,5,6,7,8,9]
# 3-1. 행에서 있는 숫자 빼기
for col in range(len(board)):
  if board[col][pos_x] != 0 and board[col][pos_x] in nums:
    nums.remove(board[col][pos_x])

# 3-2. 열에서 있는 숫자 빼기
for row in range(len(board[0])):
  if board[pos_y][row] != 0 and board[pos_y][row] in nums:
    nums.remove(board[pos_y][row])

# 3-3. 3x3에 있는 숫자 빼기
# 현재 내가 있는 칸의 위치
# 0,1,2 => 0
# 3,4,5 => 1
# 6,7,8 => 2
section_x = int(pos_x / 3)
section_y = int(pos_y / 3)

for row in range(3):
  for col in range(3):
    if board[section_y*3 + col][section_x*3 + row] != 0 and board[section_y*3 + col][section_x*3 + row] in nums:
      nums.remove(board[section_y*3 + col][section_x*3 + row])

# 4. 가능한 숫자가 없으면 None
if len(nums) < 1:
  return None
  1. 다음 칸 찾기
  • 채울 숫자를 찾았으니 다음 숫자를 채울 칸을 찾아보자
next_y = pos_y
next_x = pos_x

row_count = pos_x + 1

# 5. 다음 칸 찾기
for col in range(pos_y, 9):
  for row in range(row_count, 9):
    print(f'finding... {col} {row}')
    if board[col][row] == 0:
      print(f'찾았다! 다음 숫자!!')
      next_y = col
      next_x = row
      break
  else:
    row_count = 0
    continue
  break
  1. 재귀 반복 부
  • 가능한 숫자를 넣고 재귀를 진행
# 5. 가능한 숫자가 있으면 숫자들 넣으며 재귀
for num in nums:
  # 숫자 넣기
  board[pos_y][pos_x] = num
  # 다음 칸 찾기
  new_board = solve_helper(board, next_y, next_x)
  if new_board is not None:
    return new_board

  board[pos_y][pos_x] = 0

return None

진행 이미지


이미지

열심히 반복하더니 정답을 찾아냈다.