ฉันต้องการเพิ่มรายละเอียดเพิ่มเติมอีกเล็กน้อย ในคำตอบนี้แนวคิดที่สำคัญซ้ำแล้วซ้ำอีกก้าวช้าและจงใจซ้ำ ๆ วิธีการแก้ปัญหาที่ให้ไว้ที่นี่ไม่ได้มีขนาดกะทัดรัดที่สุด syntactically แต่มีไว้สำหรับผู้ที่ต้องการเรียนรู้ว่าการหมุนเมทริกซ์คืออะไรและการใช้งานที่เกิดขึ้น
ประการแรกเมทริกซ์คืออะไร? สำหรับจุดประสงค์ของคำตอบเมทริกซ์เป็นเพียงกริดที่ความกว้างและความสูงเท่ากัน หมายเหตุความกว้างและความสูงของเมทริกซ์อาจแตกต่างกัน แต่สำหรับความเรียบง่ายแบบฝึกหัดนี้จะพิจารณาเฉพาะเมทริกซ์ที่มีความกว้างและความสูงเท่ากัน ( เมทริกซ์สี่เหลี่ยม ) ใช่แล้วเมทริกซ์คือพหูพจน์ของเมทริกซ์
ตัวอย่างการฝึกอบรมคือ: 2 × 2, 3 × 3 หรือ 5 × 5 หรือโดยทั่วไปแล้ว N × N เมทริกซ์ 2 × 2 จะมี 4 กำลังสองเนื่องจาก 2 × 2 = 4 เมทริกซ์ 5 × 5 จะมี 25 กำลังสองเพราะ 5 × 5 = 25 แต่ละตารางเรียกว่าองค์ประกอบหรือรายการ เราจะแสดงแต่ละองค์ประกอบด้วยเครื่องหมายจุด ( .
) ในแผนภาพด้านล่าง:
เมทริกซ์ 2 × 2
. .
. .
เมทริกซ์ 3 × 3
. . .
. . .
. . .
เมทริกซ์ 4 × 4
. . . .
. . . .
. . . .
. . . .
ดังนั้นการหมุนเมทริกซ์หมายความว่าอย่างไร ลองเมทริกซ์ 2 × 2 และใส่จำนวนลงในแต่ละองค์ประกอบเพื่อให้สามารถสังเกตการหมุนได้:
0 1
2 3
การหมุนด้วย 90 องศาทำให้เรา:
2 0
3 1
เราเปลี่ยนเมทริกซ์ทั้งหมดไปทางขวาหนึ่งครั้งเหมือนกับการหมุนพวงมาลัยของรถยนต์ มันอาจช่วยให้คิดถึง“ การให้ทิป” เมทริกซ์ไปทางด้านขวาของมัน เราต้องการเขียนฟังก์ชันใน Python ที่ใช้เมทริกซ์และหมุนไปทางขวาหนึ่งครั้ง ลายเซ็นของฟังก์ชั่นจะเป็น:
def rotate(matrix):
# Algorithm goes here.
เมทริกซ์จะถูกกำหนดโดยใช้อาร์เรย์สองมิติ:
matrix = [
[0,1],
[2,3]
]
ดังนั้นตำแหน่งดัชนีแรกเข้าถึงแถว ตำแหน่งดัชนีที่สองเข้าถึงคอลัมน์:
matrix[row][column]
เราจะกำหนดฟังก์ชั่นยูทิลิตี้เพื่อพิมพ์เมทริกซ์
def print_matrix(matrix):
for row in matrix:
print row
วิธีหนึ่งในการหมุนเมทริกซ์คือการทำทีละชั้น แต่ชั้นคืออะไร คิดว่าหัวหอม เช่นเดียวกับเลเยอร์ของหัวหอมเนื่องจากแต่ละชั้นจะถูกลบออกเราย้ายไปที่กึ่งกลาง การเปรียบเทียบอื่น ๆ คือตุ๊กตา Matryoshkaหรือเกมส่งผ่านพัสดุ
ความกว้างและความสูงของเมทริกซ์กำหนดจำนวนของชั้นในเมทริกซ์นั้น มาใช้สัญลักษณ์ที่แตกต่างกันสำหรับแต่ละเลเยอร์:
เมทริกซ์ 2 × 2 มี 1 ชั้น
. .
. .
เมทริกซ์ 3 × 3 มี 2 ชั้น
. . .
. x .
. . .
เมทริกซ์ 4 × 4 มี 2 ชั้น
. . . .
. x x .
. x x .
. . . .
เมทริกซ์ขนาด 5 × 5 มี 3 ชั้น
. . . . .
. x x x .
. x O x .
. x x x .
. . . . .
เมทริกซ์ 6 × 6 มี 3 ชั้น
. . . . . .
. x x x x .
. x O O x .
. x O O x .
. x x x x .
. . . . . .
เมทริกซ์ขนาด 7 × 7 มี 4 ชั้น
. . . . . . .
. x x x x x .
. x O O O x .
. x O - O x .
. x O O O x .
. x x x x x .
. . . . . . .
คุณอาจสังเกตเห็นว่าการเพิ่มความกว้างและความสูงของเมทริกซ์ทีละหนึ่งจะไม่เพิ่มจำนวนเลเยอร์เสมอไป จากเมทริกซ์ด้านบนและการทำเป็นตารางเลเยอร์และมิติเราจะเห็นจำนวนเลเยอร์เพิ่มขึ้นหนึ่งครั้งสำหรับความกว้างและความสูงที่เพิ่มขึ้นทุกสองครั้ง:
+-----+--------+
| N×N | Layers |
+-----+--------+
| 1×1 | 1 |
| 2×2 | 1 |
| 3×3 | 2 |
| 4×4 | 2 |
| 5×5 | 3 |
| 6×6 | 3 |
| 7×7 | 4 |
+-----+--------+
อย่างไรก็ตามไม่ใช่ทุกชั้นที่ต้องหมุน เมทริกซ์ 1 × 1 จะเหมือนกันทั้งก่อนและหลังการหมุน เลเยอร์กลาง 1 × 1 นั้นจะเหมือนกันทั้งก่อนและหลังการหมุนเสมอไม่ว่าเมทริกซ์โดยรวมจะใหญ่แค่ไหน:
+-----+--------+------------------+
| N×N | Layers | Rotatable Layers |
+-----+--------+------------------+
| 1×1 | 1 | 0 |
| 2×2 | 1 | 1 |
| 3×3 | 2 | 1 |
| 4×4 | 2 | 2 |
| 5×5 | 3 | 2 |
| 6×6 | 3 | 3 |
| 7×7 | 4 | 3 |
+-----+--------+------------------+
กำหนด N × N matrix เราจะกำหนดจำนวนชั้นที่ต้องการหมุนได้อย่างไร หากเราหารความกว้างหรือความสูงเป็นสองส่วนและไม่สนใจส่วนที่เหลือเราจะได้ผลลัพธ์ดังนี้
+-----+--------+------------------+---------+
| N×N | Layers | Rotatable Layers | N/2 |
+-----+--------+------------------+---------+
| 1×1 | 1 | 0 | 1/2 = 0 |
| 2×2 | 1 | 1 | 2/2 = 1 |
| 3×3 | 2 | 1 | 3/2 = 1 |
| 4×4 | 2 | 2 | 4/2 = 2 |
| 5×5 | 3 | 2 | 5/2 = 2 |
| 6×6 | 3 | 3 | 6/2 = 3 |
| 7×7 | 4 | 3 | 7/2 = 3 |
+-----+--------+------------------+---------+
สังเกตว่าN/2
จับคู่กับจำนวนเลเยอร์ที่ต้องหมุนได้อย่างไร บางครั้งจำนวนชั้นที่หมุนได้นั้นน้อยกว่าจำนวนชั้นทั้งหมดในเมทริกซ์ สิ่งนี้เกิดขึ้นเมื่อชั้นในสุดถูกสร้างขึ้นเพียงองค์ประกอบเดียว (เช่นเมทริกซ์ 1 × 1) ดังนั้นจึงไม่จำเป็นต้องหมุน มันก็จะถูกละเว้น
เราไม่ต้องสงสัยข้อมูลในฟังก์ชันของเราเพื่อหมุนเมทริกซ์ดังนั้นเรามาเพิ่มทันที:
def rotate(matrix):
size = len(matrix)
# Rotatable layers only.
layer_count = size / 2
ตอนนี้เรารู้ว่าเลเยอร์คืออะไรและจะกำหนดจำนวนเลเยอร์ที่ต้องการการหมุนได้อย่างไรเราจะแยกเลเยอร์เดียวเพื่อให้เราหมุนได้อย่างไร ประการแรกเราตรวจสอบเมทริกซ์จากชั้นนอกสุดไปจนถึงชั้นในสุด เมทริกซ์ 5 × 5 มีทั้งหมดสามชั้นและสองชั้นที่ต้องการการหมุน:
. . . . .
. x x x .
. x O x .
. x x x .
. . . . .
ลองดูที่คอลัมน์ก่อน ตำแหน่งของคอลัมน์ที่กำหนดเลเยอร์นอกสุดสมมติว่าเรานับจาก 0 คือ 0 และ 4:
+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
| | . . . . . |
| | . x x x . |
| | . x O x . |
| | . x x x . |
| | . . . . . |
+--------+-----------+
0 และ 4 ยังเป็นตำแหน่งของแถวสำหรับเลเยอร์ชั้นนอกสุด
+-----+-----------+
| Row | |
+-----+-----------+
| 0 | . . . . . |
| 1 | . x x x . |
| 2 | . x O x . |
| 3 | . x x x . |
| 4 | . . . . . |
+-----+-----------+
กรณีนี้จะเป็นเช่นนี้เสมอเนื่องจากความกว้างและความสูงเท่ากัน ดังนั้นเราสามารถกำหนดตำแหน่งคอลัมน์และแถวของเลเยอร์ด้วยค่าเพียงสองค่า (แทนที่จะเป็นสี่)
การย้ายเข้าสู่ชั้นที่สองตำแหน่งของคอลัมน์คือ 1 และ 3 และใช่คุณเดาได้ว่ามันเหมือนกันสำหรับแถว สิ่งสำคัญคือต้องเข้าใจว่าเราต้องเพิ่มและลดตำแหน่งแถวและคอลัมน์เมื่อเลื่อนเข้าสู่ชั้นถัดไป
+-----------+---------+---------+---------+
| Layer | Rows | Columns | Rotate? |
+-----------+---------+---------+---------+
| Outermost | 0 and 4 | 0 and 4 | Yes |
| Inner | 1 and 3 | 1 and 3 | Yes |
| Innermost | 2 | 2 | No |
+-----------+---------+---------+---------+
ดังนั้นในการตรวจสอบแต่ละเลเยอร์เราต้องการลูปที่มีทั้งการเพิ่มและการลดเคาน์เตอร์ที่แสดงการเคลื่อนไหวเข้าด้านในโดยเริ่มจากเลเยอร์ชั้นนอกสุด เราจะเรียกสิ่งนี้ว่า 'layer loop' ของเรา
def rotate(matrix):
size = len(matrix)
layer_count = size / 2
for layer in range(0, layer_count):
first = layer
last = size - first - 1
print 'Layer %d: first: %d, last: %d' % (layer, first, last)
# 5x5 matrix
matrix = [
[ 0, 1, 2, 3, 4],
[ 5, 6, 6, 8, 9],
[10,11,12,13,14],
[15,16,17,18,19],
[20,21,22,23,24]
]
rotate(matrix)
โค้ดด้านบนวนลูปผ่านตำแหน่ง (แถวและคอลัมน์) ของเลเยอร์ใด ๆ ที่ต้องการการหมุน
Layer 0: first: 0, last: 4
Layer 1: first: 1, last: 3
ตอนนี้เรามีลูปที่แสดงตำแหน่งของแถวและคอลัมน์ของแต่ละเลเยอร์ ตัวแปรfirst
และlast
ระบุตำแหน่งดัชนีของแถวและคอลัมน์แรกและสุดท้าย อ้างอิงกลับไปที่ตารางแถวและคอลัมน์ของเรา:
+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
| | . . . . . |
| | . x x x . |
| | . x O x . |
| | . x x x . |
| | . . . . . |
+--------+-----------+
+-----+-----------+
| Row | |
+-----+-----------+
| 0 | . . . . . |
| 1 | . x x x . |
| 2 | . x O x . |
| 3 | . x x x . |
| 4 | . . . . . |
+-----+-----------+
ดังนั้นเราสามารถนำทางผ่านชั้นของเมทริกซ์ ตอนนี้เราต้องการวิธีการนำทางภายในเลเยอร์เพื่อให้เราสามารถย้ายองค์ประกอบรอบเลเยอร์นั้น หมายเหตุองค์ประกอบจะไม่ 'กระโดด' จากเลเยอร์หนึ่งไปยังอีกเลเยอร์ แต่จะย้ายภายในเลเยอร์ที่เกี่ยวข้อง
การหมุนแต่ละองค์ประกอบในชั้นจะหมุนทั้งชั้น การหมุนเลเยอร์ทั้งหมดในเมทริกซ์จะหมุนเมทริกซ์ทั้งหมด ประโยคนี้มีความสำคัญมากดังนั้นโปรดพยายามอย่างดีที่สุดที่จะเข้าใจก่อนที่จะดำเนินการต่อไป
ตอนนี้เราต้องการวิธีในการเคลื่อนย้ายองค์ประกอบจริง ๆ เช่นหมุนแต่ละองค์ประกอบแล้วต่อมาเลเยอร์และในที่สุดเมทริกซ์ เพื่อความง่ายเราจะเปลี่ยนกลับเป็นเมทริกซ์ 3x3 ซึ่งมีชั้นหนึ่งที่หมุนได้
0 1 2
3 4 5
6 7 8
เลเยอร์เลเยอร์ของเรามีดัชนีของคอลัมน์แรกและคอลัมน์สุดท้ายรวมถึงแถวแรกและแถวสุดท้าย:
+-----+-------+
| Col | 0 1 2 |
+-----+-------+
| | 0 1 2 |
| | 3 4 5 |
| | 6 7 8 |
+-----+-------+
+-----+-------+
| Row | |
+-----+-------+
| 0 | 0 1 2 |
| 1 | 3 4 5 |
| 2 | 6 7 8 |
+-----+-------+
เนื่องจากเมทริกซ์ของเรามีกำลังสองเสมอเราต้องการเพียงสองตัวแปรfirst
และlast
เนื่องจากตำแหน่งดัชนีเหมือนกันสำหรับแถวและคอลัมน์
def rotate(matrix):
size = len(matrix)
layer_count = size / 2
# Our layer loop i=0, i=1, i=2
for layer in range(0, layer_count):
first = layer
last = size - first - 1
# We want to move within a layer here.
ตัวแปรแรกและสุดท้ายสามารถใช้อ้างอิงมุมทั้งสี่ของเมทริกซ์ได้อย่างง่ายดาย นี่เป็นเพราะมุมตัวเองสามารถกำหนดได้โดยใช้วิธีเรียงสับเปลี่ยนของfirst
และlast
(โดยไม่มีการลบการบวกหรือชดเชยของตัวแปรเหล่านั้น):
+---------------+-------------------+-------------+
| Corner | Position | 3x3 Values |
+---------------+-------------------+-------------+
| top left | (first, first) | (0,0) |
| top right | (first, last) | (0,2) |
| bottom right | (last, last) | (2,2) |
| bottom left | (last, first) | (2,0) |
+---------------+-------------------+-------------+
ด้วยเหตุนี้เราจึงเริ่มการหมุนของเราที่มุมสี่ด้านนอก - เราจะหมุนมันก่อน *
ขอเน้นพวกเขาด้วย
* 1 *
3 4 5
* 7 *
เราต้องการสลับแต่ละอัน*
ด้วย*
ทางด้านขวาของมัน งั้นเราจะพิมพ์มุมของเราโดยใช้พีชคณิตfirst
และlast
:
def rotate(matrix):
size = len(matrix)
layer_count = size / 2
for layer in range(0, layer_count):
first = layer
last = size - first - 1
top_left = (first, first)
top_right = (first, last)
bottom_right = (last, last)
bottom_left = (last, first)
print 'top_left: %s' % (top_left)
print 'top_right: %s' % (top_right)
print 'bottom_right: %s' % (bottom_right)
print 'bottom_left: %s' % (bottom_left)
matrix = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
]
rotate(matrix)
ผลลัพธ์ควรเป็น:
top_left: (0, 0)
top_right: (0, 2)
bottom_right: (2, 2)
bottom_left: (2, 0)
ตอนนี้เราสามารถสลับมุมแต่ละมุมได้ง่ายจากภายในเลเยอร์ลูปของเรา:
def rotate(matrix):
size = len(matrix)
layer_count = size / 2
for layer in range(0, layer_count):
first = layer
last = size - first - 1
top_left = matrix[first][first]
top_right = matrix[first][last]
bottom_right = matrix[last][last]
bottom_left = matrix[last][first]
# bottom_left -> top_left
matrix[first][first] = bottom_left
# top_left -> top_right
matrix[first][last] = top_left
# top_right -> bottom_right
matrix[last][last] = top_right
# bottom_right -> bottom_left
matrix[last][first] = bottom_right
print_matrix(matrix)
print '---------'
rotate(matrix)
print_matrix(matrix)
เมทริกซ์ก่อนหมุนมุม:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
เมทริกซ์หลังจากหมุนมุม:
[6, 1, 0]
[3, 4, 5]
[8, 7, 2]
ที่ดี! เราหมุนแต่ละมุมของเมทริกซ์ได้สำเร็จ แต่เราไม่ได้หมุนองค์ประกอบตรงกลางของแต่ละชั้น เห็นได้ชัดว่าเราต้องการวิธีการวนซ้ำภายในเลเยอร์
ปัญหาคือลูปเดียวในฟังก์ชันของเราจนถึง (เลเยอร์เลเยอร์ของเรา) ย้ายไปยังเลเยอร์ถัดไปในการวนซ้ำแต่ละครั้ง เนื่องจากเมทริกซ์ของเรามีเลเยอร์ที่หมุนได้เพียงหนึ่งชั้นเลเยอร์ออกจากหลังจากหมุนมุมเท่านั้น ลองดูว่าเกิดอะไรขึ้นกับเมทริกซ์ขนาดใหญ่ 5 × 5 (ที่สองชั้นต้องการหมุน) รหัสฟังก์ชั่นถูกละไว้ แต่มันยังคงเหมือนเดิม:
matrix = [
[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]
]
print_matrix(matrix)
print '--------------------'
rotate(matrix)
print_matrix(matrix)
ผลลัพธ์คือ:
[20, 1, 2, 3, 0]
[ 5, 16, 7, 6, 9]
[10, 11, 12, 13, 14]
[15, 18, 17, 8, 19]
[24, 21, 22, 23, 4]
ไม่น่าแปลกใจที่มุมของชั้นนอกสุดได้รับการหมุน แต่คุณอาจสังเกตเห็นว่ามุมของชั้นถัดไป (ด้านใน) ถูกหมุนเช่นกัน มันสมเหตุสมผลแล้ว เราได้เขียนโค้ดเพื่อนำทางเลเยอร์และเพื่อหมุนมุมของแต่ละเลเยอร์ สิ่งนี้ให้ความรู้สึกก้าวหน้า แต่น่าเสียดายที่เราต้องถอยห่างออกไป มันไม่สามารถเคลื่อนที่ไปสู่เลเยอร์ถัดไปได้ดีจนกระทั่งเลเยอร์ก่อนหน้า (ด้านนอก) หมุนเต็มแล้ว นั่นคือจนกระทั่งแต่ละองค์ประกอบในเลเยอร์ถูกหมุน หมุนมุมเท่านั้นจะไม่ทำ!
หายใจลึก ๆ. เราต้องการวงอื่น วนซ้ำไม่น้อย วนซ้ำใหม่ที่ซ้อนกันจะใช้first
และlast
ตัวแปรรวมถึงออฟเซ็ตเพื่อนำทางภายในเลเยอร์ เราจะเรียกวงใหม่นี้ว่า 'วนรอบองค์ประกอบ' ห่วงองค์ประกอบจะเข้าสู่แต่ละองค์ประกอบตามแถวด้านบนแต่ละองค์ประกอบลงทางด้านขวาแต่ละองค์ประกอบตามแถวด้านล่างและแต่ละองค์ประกอบขึ้นทางด้านซ้าย
- การย้ายไปข้างหน้าตามแถวด้านบนจะต้องมีการเพิ่มดัชนีคอลัมน์
- การเลื่อนลงทางด้านขวาจะต้องมีการเพิ่มดัชนีแถว
- การเลื่อนไปด้านหลังตามด้านล่างจะต้องทำให้ดัชนีคอลัมน์ลดลง
- การเลื่อนขึ้นทางด้านซ้ายจะต้องมีการลดค่าดัชนีแถว
สิ่งนี้ฟังดูซับซ้อน แต่มันง่ายมากเพราะจำนวนครั้งที่เราเพิ่มและลดลงเพื่อให้ได้ตามข้างต้นยังคงเหมือนเดิมตลอดทั้งสี่ด้านของเมทริกซ์ ตัวอย่างเช่น:
- ย้าย 1 องค์ประกอบข้ามแถวบนสุด
- ย้ายองค์ประกอบ 1 ลงด้านขวา
- ย้ายองค์ประกอบ 1 ไปข้างหลังตามแถวด้านล่าง
- ย้าย 1 องค์ประกอบขึ้นไปทางซ้าย
ซึ่งหมายความว่าเราสามารถใช้ตัวแปรเดียวร่วมกับfirst
และlast
ตัวแปรเพื่อย้ายภายในชั้น มันอาจช่วยให้ทราบว่าการย้ายข้ามแถวบนและด้านขวาลงทั้งสองต้องการการเพิ่ม ในขณะที่เคลื่อนที่ไปด้านหลังตามด้านล่างและด้านซ้ายทั้งคู่ต้องลดระดับลง
def rotate(matrix):
size = len(matrix)
layer_count = size / 2
# Move through layers (i.e. layer loop).
for layer in range(0, layer_count):
first = layer
last = size - first - 1
# Move within a single layer (i.e. element loop).
for element in range(first, last):
offset = element - first
# 'element' increments column (across right)
top_element = (first, element)
# 'element' increments row (move down)
right_side = (element, last)
# 'last-offset' decrements column (across left)
bottom = (last, last-offset)
# 'last-offset' decrements row (move up)
left_side = (last-offset, first)
print 'top: %s' % (top)
print 'right_side: %s' % (right_side)
print 'bottom: %s' % (bottom)
print 'left_side: %s' % (left_side)
ตอนนี้เราเพียงแค่ต้องกำหนดด้านบนไปทางด้านขวาด้านขวาไปด้านล่างด้านล่างด้านซ้ายและด้านซ้ายไปด้านบน เรานำสิ่งนี้มารวมกัน:
def rotate(matrix):
size = len(matrix)
layer_count = size / 2
for layer in range(0, layer_count):
first = layer
last = size - first - 1
for element in range(first, last):
offset = element - first
top = matrix[first][element]
right_side = matrix[element][last]
bottom = matrix[last][last-offset]
left_side = matrix[last-offset][first]
matrix[first][element] = left_side
matrix[element][last] = top
matrix[last][last-offset] = right_side
matrix[last-offset][first] = bottom
รับเมทริกซ์:
0, 1, 2
3, 4, 5
6, 7, 8
rotate
ผลการทำงานของเราใน:
6, 3, 0
7, 4, 1
8, 5, 2