알고리즘/코드워

[python]Did I Finish my Sudoku?

(ㅇㅅㅎ) 2020. 4. 18. 02:21
728x90
반응형

https://www.codewars.com/kata/53db96041f1a7d32dc0004d2/train/python

 

Codewars: Train your coding skills

Codewars is where developers achieve code mastery through challenge. Train on kata in the dojo and reach your highest potential.

www.codewars.com

이번 제 코드는 효율적인 코드는 아닌 것 같습니다.

제 코드는 참고만 하시고 직접 풀어보시는 것을 추천드립니다.

풀이를 보시려면 더보기를 클릭하시면 됩니다.

더보기

Did I Finish my Sudoku?

스도쿠는 숫자 퍼즐로, 가로 9칸, 세로 9칸으로 이루어져 있는 표에 1부터 9까지의 숫자를 채워 넣는 퍼즐입니다.

퍼즐을 푸는 방법은 같은 줄에는 1에서 9까지의 숫자를 한 번만 넣고, 3x3칸의 작은 격자 또한 1에서 9까지의 숫자가 겹치지 않게 들어가야 합니다.

 

이번 문제는 주어진 리스트 안에서 스도쿠 규칙에 맞으면 'Finished!'를 반환하고  맞지 않으면 'Try again!'을 반환해야 합니다. 

 

이번 문제를 풀기 위해서 4가지 단계로 나누어서 생각했습니다.

1. 가로행에서 중복이 있으면 'Try again!'

2. 세로열에서 중복이 있으면 'Try again!'

3. 3x3칸에 중복이 있으면 'Try again!'

4. 위의 3가지가 아닐 경우 'Finished!'

 

문제 예시로 주어져 있는 이 스도쿠 판을 예로 들어보겠습니다.

 

board 변수에는 아래와 같은 값을 넣습니다.

board = [[5, 3, 4, 6, 7, 8, 9, 1, 2], [6, 7, 2, 1, 9, 5, 3, 4, 8], [1, 9, 8, 3, 4, 2, 5, 6, 7],

             [8, 5, 9, 7, 6, 1, 4, 2, 3], [4, 2, 6, 8, 5, 3, 7, 9, 1], [7, 1, 3, 9, 2, 4, 8, 5, 6],

             [9, 6, 1, 5, 3, 7, 2, 8, 4], [2, 8, 7, 4, 1, 9, 6, 3, 5], [3, 4, 5, 2, 8, 6, 1, 7, 9]]

 

1. 가로행에서 중복이 있으면 'Try again!'

row라는 변수에 가로 값들을 한 줄씩 담아보기 위해서는 for문을 이용하였습니다.

숫자 9 대신 len(board)를 사용하셔도 무관합니다.

for i in range(9):
    # 가로 값들
    row = board[i]
    print(i, ':', row)

아래는 출력 값입니다.

가로 값이 잘 출력되는 것을 확인했으니 for문과 count를 이용하여 1~9까지 중에서 중복되는 것이 있으면 'Try again!'을 출력하고 없으면 'Finished'를 출력하도록 했습니다.

for i in range(9):
    # 가로 값들
    row = board[i]
    for j in range(9):
        if row.count(j + 1> 1:
           print('Try again!')
print('Finished')
  

 

2. 세로열에서 중복이 있으면 'Try again!'

col라는 변수에 세로 값들을 한 줄씩 담아보기 위해서는 zip과 for문을 이용하였습니다.

 

zip(*iterable)은 동일한 개수로 이루어진 자료형을 묶어 주는 역할을 하는 함수입니다. 여기서 사용한 * iterable은 반복 가능한 자료형입니다. 

 

이런 zip을 list 값으로 변경하기 위해 map을 이용했습니다.

for i in range(9):
    # 세로 값들
    col = list(map(list, zip(*board)))[i]
    print(i, ':', col)

아래는 출력 값입니다. 

세로 값이 잘 출력되는 것을 확인했으니 for문과 count를 이용하여 1~9까지 중에서 중복되는 것이 있으면 'Try again!'을 출력하고 없으면 'Finished'를 출력하도록 했습니다.

for i in range(9):
    # 세로 값들
    col = list(map(list, zip(*board)))[i]
    for j in range(9):
        if col.count(j + 1> 1:
            print('Try again!')
print('Finished')

 

3. 3x3칸에 중복이 있으면 'Try again!'

3x3칸은 동그라미 친 값들 중에서 중복이 없어야 합니다.

값들을 0부터 시작하는 위치 값으로 나타내어 보면 아래와 같습니다.

(0, 0) (0, 1) (0, 2) | (0, 3) (0, 4) (0, 5) | (0, 6) (0, 7) (0, 8)

(1, 0) (1, 1) (1, 2) | (1, 3) (1, 4) (1, 5) | (1, 6) (1, 7) (1, 8)

(2, 0) (2, 1) (2, 2) | (2, 3) (2, 4) (2, 5) | (2, 6) (2, 7) (2, 8)

------------------ || ------------------ || ------------------

(3, 0) (3, 1) (3, 2) | (3, 3) (3, 4) (3, 5) | (3, 6) (3, 7) (3, 8)

(4, 0) (4, 1) (4, 2) | (4, 3) (4, 4) (4, 5) | (4, 6) (4, 7) (4, 8)

(5, 0) (5, 1) (5, 2) | (5, 3) (5, 4) (5, 5) | (5, 6) (5, 7) (5, 8)

------------------ || ------------------ || ------------------

(6, 0) (6, 1) (6, 2) | (6, 3) (6, 4) (6, 5) | (6, 6) (6, 7) (6, 8)

(7, 0) (7, 1) (7, 2) | (7, 3) (7, 4) (7, 5) | (7, 6) (7, 7) (7, 8)

(8, 0) (8, 1) (8, 2) | (8, 3) (8, 4) (8, 5) | (8, 6) (8, 7) (8, 8)

 

그러면 아래와 같은 식을 얻을 수 있습니다.

동그라미 한 덩어리 가로 시작 값 : (가로 값 // 3) * 3

동그라미 한 덩어리 세로 시작 값 : (세로 값 // 3) * 3

위의 두 식을 이용하여 reg 변수에 담아서 값을 출력하도록 했습니다.

for i in range(9):
    # 3x3 가로 시작 값
    rt = 3 * (i // 3)
    for j in range(9):
        # 3x3 세로 시작 값
        ct = 3 * (j // 3)
        # 3x3 값들
        reg = [board[r][c] for r, c in product(range(rt, rt + 3), range(ct, ct + 3))]
        print(reg)
 

이렇게 출력을 하면 81줄이 출력됩니다. 중복되는 부분이 많아서 좋은 코드는 아니라고 생각됩니다. 다른 방법은 다음에 생각해 보겠습니다.

효율은 좋지 않지만 값이 잘 출력되는 것을 확인했으니 for문과 count를 이용하여 1~9까지 중에서 중복되는 것이 있으면 'Try again!'을 출력하고 없으면 'Finished'를 출력하도록 했습니다.

for i in range(9):
    # 3x3 가로 시작 값
    rt = 3 * (i // 3)
    for j in range(9):
        # 3x3 세로 시작 값
        ct = 3 * (j // 3)
        # 3x3 값들
        reg = [board[r][c] for r, c in product(range(rt, rt + 3), range(ct, ct + 3))]
        # count를 이용하여 1개 이상일 경우 Try again! 반환
        if reg.count(j + 1> 1:
            print('Try again!')
print('Finished!')
 

 

4. 위의 3가지가 아닐 경우 'Finished!'를 출력하면 됩니다. 

 

전체 코드

 
# My Code
from itertools import product
def done_or_not(board):
    for i in range(9):
        # 가로 값들
        row = board[i]
        # 세로 값들
        col = list(map(list, zip(*board)))[i]
        # 3x3 가로 시작 값
        rt = 3 * (i // 3)
        for j in range(9):
            # 3x3 세로 시작 값
            ct = 3 * (j // 3)
            # 3x3 값들
            reg = [board[r][c] for r, c in product(range(rt, rt + 3), range(ct, ct + 3))]
            # count를 이용하여 1개 이상일 경우 Try again! 반환
            if row.count(j + 1) > 1 or col.count(j + 1) > 1 or reg.count(j + 1) > 1:
                return 'Try again!'
    return 'Finished!'
 
if __name__ == '__main__':
    answer = done_or_not([[132579468],
                          [498261375],
                          [756384219],
                          [643158792],
                          [521793846],
                          [987426531],
                          [214935687],
                          [365817924],
                          [879642135]])
    print(answer)
 

 

반응형

'알고리즘 > 코드워' 카테고리의 다른 글

[python]Expressions Matter  (0) 2020.04.20
[python]Abbreviate a Two Word Name  (0) 2020.04.19
[python]Sudoku Solver  (0) 2020.04.17
[python]How do I compare numbers?  (0) 2020.04.16
[python]Sum of positive  (0) 2020.04.15