プログラミングの勉強をしている時に、以下のような問題に出会いました。
問題の内容
N × N マスの盤面がある。
この盤面を90度単位で M 回回転させた後の盤面を出力せよ。
具体例で考えてみよう
具体的に以下のような「4 × 4 マス」で考えてみます。
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
まず、この盤面が時計回りに90度回転して、
以下のようになることを考えてみます。
13 | 9 | 5 | 1 |
14 | 10 | 6 | 2 |
15 | 11 | 7 | 3 |
16 | 12 | 8 | 4 |
2次元 list で表現してみよう
まず最初の盤面を2次元 list
で1マスずつ表示すると以下のようになります。
見比べやすいように最初の盤面を再度載せておきますので、必要に応じて表示させてください。
最初の盤面(クリックすると開きます)
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
### 最初の盤面を2次元 list で1マスずつ表示
# 1行目
example_list[0][0] = 1
example_list[0][1] = 2
example_list[0][2] = 3
example_list[0][3] = 4
# 2行目
example_list[1][0] = 5
example_list[1][1] = 6
example_list[1][2] = 7
example_list[1][3] = 8
# 3行目
example_list[2][0] = 9
example_list[2][1] = 10
example_list[2][2] = 11
example_list[2][3] = 12
# 4行目
example_list[3][0] = 13
example_list[3][1] = 14
example_list[3][2] = 15
example_list[3][3] = 16
同様に、時計回りに90度回転させた盤面を2次元 list
で1マスずつ表現すると以下のようになります。
見比べやすいように90度回転させた盤面を再度載せておきますので、必要に応じて表示させてください。
90度回転させた盤面(クリックすると開きます)
13 | 9 | 5 | 1 |
14 | 10 | 6 | 2 |
15 | 11 | 7 | 3 |
16 | 12 | 8 | 4 |
### 90度回転させた盤面を2次元 list で1マスずつ表示
# 1行目
example_list[0][0] = 13
example_list[0][1] = 9
example_list[0][2] = 5
example_list[0][3] = 1
# 2行目
example_list[1][0] = 14
example_list[1][1] = 10
example_list[1][2] = 6
example_list[1][3] = 2
# 3行目
example_list[2][0] = 15
example_list[2][1] = 11
example_list[2][2] = 7
example_list[2][3] = 3
# 4行目
example_list[3][0] = 16
example_list[3][1] = 12
example_list[3][2] = 8
example_list[3][3] = 4
各数字と
それぞれの2次元 list
の index 値 を表にまとめると、
以下のようになります。
数字 | 2次元 list の index 値 | |
---|---|---|
最初の盤面 | 90度回転した盤面 | |
1 | [0][0] | [0][3] |
2 | [0][1] | [1][3] |
3 | [0][2] | [2][3] |
4 | [0][3] | [3][3] |
5 | [1][0] | [0][2] |
6 | [1][1] | [1][2] |
7 | [1][2] | [2][2] |
8 | [1][3] | [3][2] |
9 | [2][0] | [0][1] |
10 | [2][1] | [1][1] |
11 | [2][2] | [2][1] |
12 | [2][3] | [3][1] |
13 | [3][0] | [0][0] |
14 | [3][1] | [1][0] |
15 | [3][2] | [2][0] |
16 | [3][3] | [3][0] |
盤面の回転をコード化してみよう
90度回転した盤面の index 値は、
回転前の盤面の index 値を使って、以下のように表すことができます。
・X 座標(1つ目の index 値)は、「元の盤面の Y 座標(2つ目の index 値)」と同じ
・Y 座標(2つ目の index 値)は、「N(マスの大きさ)-元の盤面の X 座標(1つ目の index 値)-1 」
コード化してみると、以下のようになります。
# pprint モジュールをインポート
import pprint
# 盤面の大きさ(N × N マス)
N = 4
# 元の盤面
example_list = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
# 結果保存用 list (N × N マスの "." 詰めで準備)
result_list = [["." for i in range(N)] for j in range(N)]
# 元の盤面を時計回りに90度回転させる
for Y in range(N):
for X in range(N):
result_list[Y][N -X -1] = example_list[X][Y]
# 結果を見やすく出力
print("時計回りに90度回転させた結果")
pprint.pprint(result_list, width = 20)
出力結果はこちら
時計回りに90度回転させた結果
[[13, 9, 5, 1],
[14, 10, 6, 2],
[15, 11, 7, 3],
[16, 12, 8, 4]]
90度回転させた盤面(クリックすると開きます)
13 | 9 | 5 | 1 |
14 | 10 | 6 | 2 |
15 | 11 | 7 | 3 |
16 | 12 | 8 | 4 |
時計回りに90度回転させることができました。
180度、270度回転させた場合も考えてみよう
時計回りに180度回転した盤面は以下の通り。
16 | 15 | 14 | 13 |
12 | 11 | 10 | 9 |
8 | 7 | 6 | 5 |
4 | 3 | 2 | 1 |
同様に270度回転した盤面は以下の通り。
4 | 8 | 12 | 16 |
3 | 7 | 11 | 15 |
2 | 6 | 10 | 14 |
1 | 5 | 9 | 13 |
各数字と
それぞれを2次元 list
にした場合の index 値を表にまとめると、
以下のようになります。
数字 | 2次元 list の index 値 | |||
---|---|---|---|---|
最初の盤面 | 90度回転した盤面 | 180度回転した盤面 | 270度回転した盤面 | |
1 | [0][0] | [0][3] | [3][3] | [3][0] |
2 | [0][1] | [1][3] | [3][2] | [2][0] |
3 | [0][2] | [2][3] | [3][1] | [1][0] |
4 | [0][3] | [3][3] | [3][0] | [0][0] |
5 | [1][0] | [0][2] | [2][3] | [3][1] |
6 | [1][1] | [1][2] | [2][2] | [2][1] |
7 | [1][2] | [2][2] | [2][1] | [1][1] |
8 | [1][3] | [3][2] | [2][0] | [0][1] |
9 | [2][0] | [0][1] | [1][3] | [3][2] |
10 | [2][1] | [1][1] | [1][2] | [2][2] |
11 | [2][2] | [2][1] | [1][1] | [1][2] |
12 | [2][3] | [3][1] | [1][0] | [0][2] |
13 | [3][0] | [0][0] | [0][3] | [3][3] |
14 | [3][1] | [1][0] | [0][2] | [2][3] |
15 | [3][2] | [2][0] | [0][1] | [1][3] |
16 | [3][3] | [3][0] | [0][0] | [0][3] |
2次元 list
の index 値の比較において、
180度回転した盤面の index 値は以下のように表すことができます。
・X 座標(1つ目の index 値)は、「N(マスの大きさ)-元の盤面の X 座標(1つ目の index 値)-1 」
・Y 座標(2つ目の index 値)は、「N(マスの大きさ)-元の盤面の Y 座標(2つ目の index 値)-1 」
同様に270度回転した盤面の index 値は以下のように表すことができます。
・X 座標(1つ目の index 値)は、「N(マスの大きさ)-元の盤面の Y 座標(2つ目の index 値)-1 」
・Y 座標(2つ目の index 値)は、「元の盤面の X 座標(1つ目の index 値))」と同じ
回転する部分を関数化してコード化すると、以下のようになります。
import pprint
# 盤面を回転させる関数
def rotate_board(N, M, input_list, output_list):
# 時計回りに90度回転した場合
if M == 1:
for Y in range(N):
for X in range(N):
output_list[Y][N -X -1] = input_list[X][Y]
# 時計回りに180度回転した場合
elif M == 2:
for Y in range(N):
for X in range(N):
output_list[N-X -1][N -Y -1] = input_list[X][Y]
# 時計回りに270度回転した場合
elif M == 3:
for Y in range(N):
for X in range(N):
output_list[N -Y -1][X] = input_list[X][Y]
# 処理結果を返す
return(output_list)
# 盤面の大きさ(N × N マス)
N = 4
# 元の盤面
example_list = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
# 結果保存用 list (N × N マスの "." 詰めで準備)
result_list = [["." for i in range(N)] for j in range(N)]
# 回転数(1回で時計回りに90度回転)
M = 2
# 盤面を回転させる関数を呼び出し
result_list = rotate_board(N, M, example_list, result_list)
# 時計回りに180度回転させた結果出力
print("時計回りに180度回転させた結果")
pprint.pprint(result_list, width = 20)
print("")
# 回転数(1回で時計回りに90度回転)
M = 3
# 盤面を回転させる関数を呼び出し
result_list = rotate_board(N, M, example_list, result_list)
# 結果出力
print("時計回りに270度回転させた結果")
pprint.pprint(result_list, width = 20)
出力結果は以下の通り。
時計回りに180度回転させた結果
[[16, 15, 14, 13],
[12, 11, 10, 9],
[8, 7, 6, 5],
[4, 3, 2, 1]]
時計回りに270度回転させた結果
[[4, 8, 12, 16],
[3, 7, 11, 15],
[2, 6, 10, 14],
[1, 5, 9, 13]]
180度回転させた盤面(クリックすると開きます)
16 | 15 | 14 | 13 |
12 | 11 | 10 | 9 |
8 | 7 | 6 | 5 |
4 | 3 | 2 | 1 |
270度回転させた盤面(クリックすると開きます)
4 | 8 | 12 | 16 |
3 | 7 | 11 | 15 |
2 | 6 | 10 | 14 |
1 | 5 | 9 | 13 |
時計回りに180度、270度回転させることもできました。
【2023/04/25 追記】
@shiracamus さんにコメントでzip
関数と内包表記を使った美しいコードを教えて頂きました。
各行の意味についても、コメントで丁寧に教えて頂いたので、興味のある方コメント欄をご覧ください。
from pprint import pprint
def rotate0(board: list) -> list:
return board
def rotate90(board: list) -> list:
return [[*row][::-1] for row in zip(*board)]
def rotate180(board: list) -> list:
return [row[::-1] for row in board[::-1]]
def rotate270(board: list) -> list:
return [[*row] for row in zip(*board)][::-1]
def rotate(board: list, m: int) -> list:
return [rotate0, rotate90, rotate180, rotate270][m % 4](board)
# 元の盤面
example: list = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
print("時計回りに90度回転させた結果")
result: list = rotate(example, 1)
pprint(result, width=20)
print()
print("時計回りに180度回転させた結果")
result: list = rotate(example, 2)
pprint(result, width=20)
print()
print("時計回りに270度回転させた結果")
result: list = rotate(example, 3)
pprint(result, width=20)