Biraz daha ayrıntı eklemek istiyorum. Bu cevapta, anahtar kavramlar tekrarlanır, hız yavaş ve kasıtlı olarak tekrarlanır. Burada sağlanan çözüm en sözdizimsel olarak kompakt değildir, ancak matris dönüşünün ne olduğunu ve sonuçta ortaya çıkan uygulamayı öğrenmek isteyenler için tasarlanmıştır.
İlk olarak, matris nedir? Bu cevabın amaçları doğrultusunda, bir matris sadece genişlik ve yüksekliğin aynı olduğu bir ızgaradır. Bir matrisin genişliği ve yüksekliği farklı olabilir, ancak basitlik açısından bu öğretici yalnızca eşit genişlik ve yüksekliğe sahip matrisleri ( kare matrisler ) dikkate alır. Ve evet, matrisler matrisin çoğuludur.
Örnek matrisler şunlardır: 2 × 2, 3 × 3 veya 5 × 5. Veya daha genel olarak N × N'dir. 2 × 2 matris 4 kareye sahip olacaktır, çünkü 2 × 2 = 4. 5 × 5 matris 25 kareye sahip olacaktır, çünkü 5 × 5 = 25. Her kareye bir eleman veya giriş denir. Her öğeyi bir nokta ile temsil edeceğiz (.
aşağıdaki diyagramlarda ) :
2 × 2 matris
. .
. .
3 × 3 matris
. . .
. . .
. . .
4 × 4 matris
. . . .
. . . .
. . . .
. . . .
Peki, bir matrisi döndürmek ne anlama geliyor? 2 × 2 matrisini alalım ve her öğeye bazı sayılar koyalım, böylece dönme gözlemlenebilir:
0 1
2 3
Bunu 90 derece döndürmek bize şunu verir:
2 0
3 1
Kelimenin tam anlamıyla bir arabanın direksiyonunu çevirmek gibi bir kez sağa çevirdik. Matrisi sağ tarafına "yatırmayı" düşünmek yardımcı olabilir. Python'da, bir matris alan ve bir kez sağa dönen bir fonksiyon yazmak istiyoruz. İşlev imzası:
def rotate(matrix):
# Algorithm goes here.
Matris iki boyutlu bir dizi kullanılarak tanımlanacaktır:
matrix = [
[0,1],
[2,3]
]
Bu nedenle, ilk dizin konumu satıra erişir. İkinci dizin konumu sütuna erişir:
matrix[row][column]
Bir matris yazdırmak için bir yardımcı program işlevi tanımlayacağız.
def print_matrix(matrix):
for row in matrix:
print row
Bir matrisi döndürmenin bir yöntemi, her seferinde bir katman yapmaktır. Peki katman nedir? Bir soğan düşünün. Tıpkı bir soğan tabakası gibi, her tabaka çıkarıldıkça merkeze doğru hareket ediyoruz. Diğer analojiler bir Matryoshka bebeği veya parselin bir geçiş oyunudur.
Bir matrisin genişliği ve yüksekliği, o matristeki katman sayısını belirler. Her katman için farklı semboller kullanalım:
2 × 2 matrisin 1 katmanı vardır
. .
. .
3 × 3 matrisin 2 katmanı vardır
. . .
. x .
. . .
4 × 4 matrisin 2 katmanı vardır
. . . .
. x x .
. x x .
. . . .
5 × 5 matrisin 3 katmanı vardır
. . . . .
. x x x .
. x O x .
. x x x .
. . . . .
6 × 6 matrisin 3 katmanı vardır
. . . . . .
. x x x x .
. x O O x .
. x O O x .
. x x x x .
. . . . . .
7 × 7 matrisin 4 katmanı vardır
. . . . . . .
. x x x x x .
. x O O O x .
. x O - O x .
. x O O O x .
. x x x x x .
. . . . . . .
Bir matrisin genişliğini ve yüksekliğini birer birer artırmanın, katman sayısını her zaman artırmadığını fark edebilirsiniz. Yukarıdaki matrisleri alarak katmanları ve boyutları tablo haline getirerek, genişlik ve yüksekliğin her iki artışı için katman sayısının bir kez arttığını görüyoruz:
+-----+--------+
| N×N | Layers |
+-----+--------+
| 1×1 | 1 |
| 2×2 | 1 |
| 3×3 | 2 |
| 4×4 | 2 |
| 5×5 | 3 |
| 6×6 | 3 |
| 7×7 | 4 |
+-----+--------+
Ancak, tüm katmanların dönmesine gerek yoktur. 1 × 1 matris, dönmeden önce ve sonra aynıdır. Merkezi 1 × 1 katman, genel matris ne kadar büyük olursa olsun, döndürmeden önce ve sonra her zaman aynıdır:
+-----+--------+------------------+
| 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 matrisi verildiğinde, döndürmemiz gereken katman sayısını programsal olarak nasıl belirleyebiliriz? Genişliği veya yüksekliği ikiye bölerek kalanını görmezden gelirsek aşağıdaki sonuçları elde ederiz.
+-----+--------+------------------+---------+
| 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 |
+-----+--------+------------------+---------+
Nasıl olduğunu fark et N/2
Döndürülmesi gereken katman sayısıyla eşleştiğine ? Bazen dönebilen katman sayısı, matristeki toplam katman sayısının bir katıdır. Bu, en içteki katman sadece bir elemandan (yani 1 × 1 matris) oluştuğunda ve dolayısıyla döndürülmeleri gerekmediğinde meydana gelir. Sadece görmezden gelinir.
Şüphesiz bir matrisi döndürmek için işlevimizde bu bilgilere ihtiyacımız olacak, bu yüzden şimdi ekleyelim:
def rotate(matrix):
size = len(matrix)
# Rotatable layers only.
layer_count = size / 2
Şimdi hangi katmanların ne olduğunu ve gerçekten dönmesi gereken katman sayısını nasıl belirleyeceğimizi biliyoruz, tek bir katmanı nasıl döndürebiliriz, böylece onu döndürebiliriz? İlk olarak, en dıştaki katmandan içe, en içteki katmana kadar bir matrisi inceliyoruz. 5 × 5 matrisin toplam üç katmanı ve dönmesi gereken iki katmanı vardır:
. . . . .
. x x x .
. x O x .
. x x x .
. . . . .
Önce sütunlara bakalım. 0'dan saydığımızı varsayarak, en dış katmanı tanımlayan sütunların konumu 0 ve 4'tür:
+--------+-----------+
| Column | 0 1 2 3 4 |
+--------+-----------+
| | . . . . . |
| | . x x x . |
| | . x O x . |
| | . x x x . |
| | . . . . . |
+--------+-----------+
O ve 4, en dıştaki kat için sıraların konumlandırmasıdır.
+-----+-----------+
| Row | |
+-----+-----------+
| 0 | . . . . . |
| 1 | . x x x . |
| 2 | . x O x . |
| 3 | . x x x . |
| 4 | . . . . . |
+-----+-----------+
Genişlik ve yükseklik aynı olduğu için bu her zaman geçerli olacaktır. Bu nedenle, bir katmanın sütun ve satır konumlarını yalnızca dört değerle (dört yerine) tanımlayabiliriz.
İkinci katmana doğru ilerlerken, sütunların konumu 1 ve 3'tür. Ve evet, tahmin ettiniz, satırlar için aynı. Bir sonraki katmana içe doğru hareket ederken satır ve sütun konumlarını hem artırmamız hem de azaltmamız gerektiğini anlamak önemlidir.
+-----------+---------+---------+---------+
| Layer | Rows | Columns | Rotate? |
+-----------+---------+---------+---------+
| Outermost | 0 and 4 | 0 and 4 | Yes |
| Inner | 1 and 3 | 1 and 3 | Yes |
| Innermost | 2 | 2 | No |
+-----------+---------+---------+---------+
Bu nedenle, her bir katmanı incelemek için, en dış katmandan başlayarak içeri doğru hareket etmeyi temsil eden hem artan hem de azalan sayaçlara sahip bir döngü istiyoruz. Buna 'katman döngüsü' diyeceğiz.
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)
Yukarıdaki kod, döndürülmesi gereken katmanların (satır ve sütun) konumlarında dolaşır.
Layer 0: first: 0, last: 4
Layer 1: first: 1, last: 3
Artık her katmanın satır ve sütunlarının konumlarını sağlayan bir döngümüz var. Değişkenler first
ve last
ilk ve son satır ve sütunların dizin konumunu tanımlar. Satır ve sütun tablolarımıza bakın:
+--------+-----------+
| 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 | . . . . . |
+-----+-----------+
Böylece bir matrisin katmanları arasında gezinebiliriz. Şimdi, bir katman içinde gezinme yoluna ihtiyacımız var, böylece elemanları o katmanın etrafında hareket ettirebiliriz. Not: Öğeler hiçbir zaman bir katmandan diğerine 'zıplamaz', ancak ilgili katmanları içinde hareket ederler.
Katmandaki her öğeyi döndürmek tüm katmanı döndürür. Bir matristeki tüm katmanları döndürmek tüm matrisi döndürür. Bu cümle çok önemlidir, bu yüzden lütfen devam etmeden önce anlamak için elinizden gelenin en iyisini yapın.
Şimdi, elemanları hareket ettirmenin bir yoluna ihtiyacımız var, yani her bir elemanı ve daha sonra katmanı ve sonuçta matrisi döndürmek. Basitlik için, döndürülebilir bir katmanı olan 3x3'lük bir matrise geri döneceğiz.
0 1 2
3 4 5
6 7 8
Katman döngümüz, ilk ve son sütunların yanı sıra ilk ve son satırların dizinlerini sağlar:
+-----+-------+
| 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 |
+-----+-------+
Matrislerimiz her zaman kare olduğu için sadece iki değişkene ihtiyacımız var first
ve last
dizin konumları satırlar ve sütunlar için aynı olduğundan.
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.
İlk ve son değişkenler bir matrisin dört köşesine başvuruda bulunmak için kolayca kullanılabilir. Bu, muhtelif permütasyon kullanılarak tanımlanabilir köşeleri için first
ve last
(bir çıkarma, ekleme ya da bu değişkenlerin ofset):
+---------------+-------------------+-------------+
| 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) |
+---------------+-------------------+-------------+
Bu nedenle rotasyonumuza dış dört köşeden başlıyoruz - önce bunları döndüreceğiz. Onları ile vurgulayalım *
.
* 1 *
3 4 5
* 7 *
Her birini sağındaki *
ile değiştirmek istiyoruz *
. Yani sadece çeşitli permütasyon kullanılarak tanımlanır Şimdi devam eden köşeleri dışarı baskı izin first
ve 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)
Çıktı şöyle olmalıdır:
top_left: (0, 0)
top_right: (0, 2)
bottom_right: (2, 2)
bottom_left: (2, 0)
Artık her bir köşeyi katman döngümüzden kolayca değiştirebiliriz:
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)
Köşeleri döndürmeden önce matris:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
Köşeleri döndürdükten sonra matris:
[6, 1, 0]
[3, 4, 5]
[8, 7, 2]
Harika! Matrisin her köşesini başarıyla döndürdük. Ancak, her katmanın ortasındaki öğeleri döndürmedik. Açıkça bir katman içinde yineleme yöntemine ihtiyacımız var.
Sorun şu ki, fonksiyonumuzdaki tek döngü (katman döngümüz), her yinelemede bir sonraki katmana geçiyor. Matrisimiz yalnızca bir dönebilir katmana sahip olduğundan, katman döngüsü sadece köşeleri döndürdükten sonra çıkar. Daha büyük, 5 × 5 bir matrisle (iki katın dönmesi gereken yerlerde) neler olduğuna bakalım. İşlev kodu atlandı, ancak yukarıdakiyle aynı kaldı:
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)
Çıktı:
[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]
En dış katmanın köşelerinin döndürülmüş olması şaşırtıcı olmamalı, ancak bir sonraki katmanın (içe doğru) köşelerinin de döndürüldüğünü fark edebilirsiniz. Bu mantıklı. Katmanlar arasında gezinmek ve her katmanın köşelerini döndürmek için kod yazdık. Bu ilerleme gibi hissettiriyor, ancak maalesef geri adım atmalıyız. Önceki (dış) katman tamamen döndürülünceye kadar bir sonraki katmana geçmek iyi değildir. Yani, katmandaki her eleman döndürülünceye kadar. Sadece köşeleri döndürmek işe yaramaz!
Derin bir nefes al. Başka bir döngüye ihtiyacımız var. Yuvalanmış bir döngü daha az değil. Yeni, iç içe döngü, first
ve last
değişkenlerini artı bir katman içinde gezinmek için bir uzaklık kullanır. Bu yeni döngüye 'element loop' diyoruz. Eleman döngüsü, her bir elemanı üst sıra boyunca, her bir eleman sağ tarafa, her bir eleman alt sıra boyunca ve her bir eleman sol tarafa doğru ziyaret edecektir.
- Üst sıra boyunca ileriye doğru hareket etmek için sütun dizininin artırılması gerekir.
- Sağ tarafa gitmek için satır dizininin artırılması gerekir.
- Altta geriye doğru hareket etmek için sütun dizininin azaltılması gerekir.
- Sol tarafı yukarı taşımak satır sırasının azaltılmasını gerektirir.
Bu karmaşık görünüyor, ancak bu kolaylaştı çünkü yukarıdakilere ulaşmak için arttırma ve azaltma sayımız matrisin dört tarafında da aynı kalıyor. Örneğin:
- 1 elemanı üst sıra boyunca hareket ettirin.
- 1 elemanı sağ tarafa doğru hareket ettirin.
- 1 elemanı alt satır boyunca geriye doğru hareket ettirin.
- 1 elemanı sol tarafa doğru hareket ettirin.
Bu, bir katman içinde hareket etmek için first
ve last
değişkenleriyle birlikte tek bir değişken kullanabileceğimiz anlamına gelir . Üst sıra boyunca ve sağ tarafta aşağı doğru hareket etmenin her ikisinin de artırılması gerektiğini belirtmek yardımcı olabilir. Altta ve sol tarafta yukarı doğru hareket ederken her ikisi de azaltılmasını gerektirir.
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)
Şimdi sadece üst tarafı sağ tarafa, sağ tarafı alt tarafa, alt taraf sol tarafa ve sol tarafı üst tarafa atamamız gerekiyor. Tüm bunları bir araya getirdiğimizde:
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
Matris verildiğinde:
0, 1, 2
3, 4, 5
6, 7, 8
Bizim rotate
fonksiyonumuz:
6, 3, 0
7, 4, 1
8, 5, 2