Terinspirasi oleh posting Raymond Chen , katakan Anda memiliki array dua dimensi 4x4, tulis fungsi yang memutarnya 90 derajat. Link Raymond ke solusi dalam kode pseudo, tapi saya ingin melihat beberapa hal dunia nyata.
[1][2][3][4]
[5][6][7][8]
[9][0][1][2]
[3][4][5][6]
Menjadi:
[3][9][5][1]
[4][0][6][2]
[5][1][7][3]
[6][2][8][4]
Pembaruan : Jawaban Nick adalah yang paling mudah, tetapi apakah ada cara untuk melakukannya lebih baik daripada n ^ 2? Bagaimana jika matriksnya 10000x10000?
algorithm
matrix
multidimensional-array
swilliams
sumber
sumber
Jawaban:
Ini dia dalam C #
sumber
ret[i][j] = matrix[j][n - i - 1]
O (n ^ 2) waktu dan O (1) algoritma ruang (tanpa solusi dan hal-hal saputangan!)
Putar +90:
Putar oleh -90:
Metode 1:
Metode 2:
Putar oleh +180:
Metode 1 : Putar oleh +90 dua kali
Metode 2 : Membalikkan setiap baris dan lalu membalikkan setiap kolom (Transpose)
Putar dengan -180:
Metode 1 : Putar oleh -90 dua kali
Metode 2 : Balikkan setiap kolom lalu balikkan setiap baris
Metode 3 : Putar oleh +180 karena sama
sumber
rotateCW = map reverse . transpose
danrotateCCW = transpose . map reverse
Saya ingin menambahkan sedikit lebih detail. Dalam jawaban ini, konsep-konsep kunci diulangi, langkahnya lambat dan sengaja berulang. Solusi yang diberikan di sini bukan yang paling kompak secara sintaksis, namun, ditujukan untuk mereka yang ingin mempelajari apa itu rotasi matriks dan implementasi yang dihasilkan.
Pertama, apa itu matriks? Untuk keperluan jawaban ini, sebuah matriks hanyalah sebuah kotak dengan lebar dan tinggi yang sama. Catatan, lebar dan tinggi matriks bisa berbeda, tetapi untuk kesederhanaan, tutorial ini hanya mempertimbangkan matriks dengan lebar dan tinggi yang sama ( matriks persegi ). Dan ya, matriks adalah bentuk jamak dari matriks.
Contoh matriks adalah: 2 × 2, 3 × 3 atau 5 × 5. Atau, lebih umum, N × N. Matriks 2 × 2 akan memiliki 4 kotak karena 2 × 2 = 4. Matriks 5 × 5 akan memiliki 25 kotak karena 5 × 5 = 25. Setiap kotak disebut elemen atau entri. Kami akan mewakili setiap elemen dengan tanda titik (
.
) pada diagram di bawah ini:Matriks 2 × 2
Matriks 3 × 3
4 × 4 matriks
Jadi, apa artinya memutar matriks? Mari kita mengambil matriks 2 × 2 dan meletakkan beberapa angka di setiap elemen sehingga rotasi dapat diamati:
Memutar ini 90 derajat memberi kita:
Kami benar-benar mengubah seluruh matriks satu kali ke kanan seperti memutar setir mobil. Mungkin membantu untuk memikirkan “membalik” matriks ke sisi kanannya. Kami ingin menulis sebuah fungsi, dengan Python, yang mengambil matriks dan berputar sekali ke kanan. Tanda tangan fungsi adalah:
Matriks akan didefinisikan menggunakan array dua dimensi:
Oleh karena itu posisi indeks pertama mengakses baris. Posisi indeks kedua mengakses kolom:
Kami akan mendefinisikan fungsi utilitas untuk mencetak matriks.
Salah satu metode memutar matriks adalah dengan melakukannya satu lapis pada satu waktu. Tapi apa itu layer? Pikirkan bawang. Sama seperti lapisan bawang, saat setiap lapisan dihilangkan, kami bergerak ke tengah. Analogi lainnya adalah boneka Matryoshka atau permainan pass-the-parcel.
Lebar dan tinggi matriks menentukan jumlah lapisan dalam matriks itu. Mari kita gunakan simbol yang berbeda untuk setiap layer:
Matriks 2 × 2 memiliki 1 lapisan
Matriks 3 × 3 memiliki 2 lapisan
Matriks 4 × 4 memiliki 2 lapisan
Matriks 5 × 5 memiliki 3 lapisan
Matriks 6 × 6 memiliki 3 lapisan
Matriks 7 × 7 memiliki 4 lapisan
Anda mungkin memperhatikan bahwa penambahan lebar dan tinggi matriks satu per satu, tidak selalu menambah jumlah lapisan. Mengambil matriks di atas dan mentabulasi lapisan dan dimensi, kita melihat jumlah lapisan meningkat satu kali untuk setiap dua penambahan lebar dan tinggi:
Namun, tidak semua lapisan perlu diputar. Matriks 1 × 1 adalah sama sebelum dan sesudah rotasi. Lapisan pusat 1 × 1 selalu sama sebelum dan sesudah rotasi tidak peduli seberapa besar keseluruhan matriks:
Dengan matriks N × N, bagaimana kita dapat secara program menentukan jumlah lapisan yang perlu kita putar? Jika kita membagi lebar atau tinggi dengan dua dan mengabaikan sisanya kita mendapatkan hasil berikut.
Perhatikan seberapa
N/2
cocok dengan jumlah layer yang perlu diputar? Kadang-kadang jumlah lapisan yang dapat diputar adalah kurang dari jumlah total lapisan dalam matriks. Ini terjadi ketika lapisan terdalam hanya terbentuk dari satu elemen (yaitu matriks 1 × 1) dan karenanya tidak perlu diputar. Itu hanya diabaikan.Kami pasti akan membutuhkan informasi ini dalam fungsi kami untuk memutar matriks, jadi mari kita tambahkan sekarang:
Sekarang kita tahu apa itu lapisan dan bagaimana menentukan jumlah lapisan yang sebenarnya perlu dirotasi, bagaimana kita mengisolasi satu lapisan sehingga kita bisa memutarnya? Pertama, kami memeriksa matriks dari lapisan terluar, ke dalam, ke lapisan terdalam. Matriks 5 × 5 memiliki tiga lapisan total dan dua lapisan yang perlu dirotasi:
Mari kita lihat kolom terlebih dahulu. Posisi kolom menentukan lapisan terluar, dengan asumsi kita menghitung dari 0, adalah 0 dan 4:
0 dan 4 juga merupakan posisi baris untuk lapisan terluar.
Ini akan selalu terjadi karena lebar dan tinggi adalah sama. Oleh karena itu kita dapat mendefinisikan posisi kolom dan baris pada layer hanya dengan dua nilai (bukan empat).
Pindah ke dalam ke lapisan kedua, posisi kolom adalah 1 dan 3. Dan, ya, Anda dapat menebaknya, itu sama untuk baris. Penting untuk dipahami bahwa kami harus menambah dan mengurangi posisi baris dan kolom saat bergerak ke dalam ke lapisan berikutnya.
Jadi, untuk memeriksa setiap lapisan, kami ingin loop dengan penghitung yang meningkat dan menurun yang mewakili gerakan ke dalam, mulai dari lapisan terluar. Kami akan menyebutnya 'loop layer' kami.
Kode di atas memotong melalui posisi (baris dan kolom) dari setiap lapisan yang perlu dirotasi.
Kami sekarang memiliki lingkaran yang menyediakan posisi baris dan kolom dari setiap lapisan. Variabel
first
danlast
mengidentifikasi posisi indeks dari baris dan kolom pertama dan terakhir. Mengacu kembali ke tabel baris dan kolom kami:Jadi kita dapat menavigasi melalui lapisan-lapisan matriks. Sekarang kita membutuhkan cara menavigasi di dalam lapisan sehingga kita dapat memindahkan elemen di sekitar lapisan itu. Catatan, elemen tidak pernah 'melompat' dari satu layer ke layer lain, tetapi mereka bergerak di dalam layer masing-masing.
Memutar setiap elemen dalam suatu lapisan akan memutar seluruh lapisan. Memutar semua lapisan dalam matriks akan memutar seluruh matriks. Kalimat ini sangat penting, jadi cobalah yang terbaik untuk memahaminya sebelum melanjutkan.
Sekarang, kita membutuhkan cara untuk benar-benar memindahkan elemen, yaitu memutar setiap elemen, dan selanjutnya layer, dan akhirnya matriks. Untuk mempermudah, kami akan kembali ke matriks 3x3 - yang memiliki satu lapisan yang dapat diputar.
Lingkaran lapisan kami menyediakan indeks kolom pertama dan terakhir, serta baris pertama dan terakhir:
Karena matriks kita selalu kuadrat, kita hanya perlu dua variabel,
first
danlast
, karena posisi indeks sama untuk baris dan kolom.Variabel pertama dan terakhir dapat dengan mudah digunakan untuk merujuk empat sudut matriks. Ini karena sudut-sudut itu sendiri dapat didefinisikan menggunakan berbagai permutasi dari
first
danlast
(tanpa pengurangan, penambahan atau offset variabel-variabel tersebut):Untuk alasan ini, kami memulai rotasi kami di empat sudut terluar - kami akan memutarnya terlebih dahulu. Mari kita sorot dengan mereka
*
.Kami ingin bertukar masing
*
- masing dengan di*
sebelah kanannya. Jadi mari kita lanjutkan mencetak sudut-sudut kita yang didefinisikan hanya menggunakan berbagai permutasi darifirst
danlast
:Output harus:
Sekarang kita bisa dengan mudah menukar masing-masing sudut dari dalam loop layer kita:
Matriks sebelum memutar sudut:
Matriks setelah sudut berputar:
Bagus! Kami telah berhasil memutar setiap sudut matriks. Namun, kami belum memutar elemen di tengah setiap lapisan. Jelas kita membutuhkan cara iterasi di dalam layer.
Masalahnya adalah, satu-satunya loop dalam fungsi kita sejauh ini (loop layer kita), bergerak ke lapisan berikutnya pada setiap iterasi. Karena matriks kami hanya memiliki satu lapisan yang dapat diputar, loop lapisan keluar setelah hanya memutar sudut-sudutnya. Mari kita lihat apa yang terjadi dengan matriks 5x5 yang lebih besar (di mana dua lapisan perlu diputar). Kode fungsi telah dihilangkan, tetapi tetap sama seperti di atas:
Outputnya adalah:
Seharusnya tidak mengherankan bahwa sudut-sudut lapisan terluar telah diputar, tetapi, Anda juga dapat melihat sudut-sudut lapisan berikutnya (ke dalam) juga telah diputar. Ini masuk akal. Kami telah menulis kode untuk menavigasi lapisan dan juga untuk memutar sudut setiap lapisan. Ini terasa seperti kemajuan, tetapi sayangnya kita harus mengambil langkah mundur. Tidak ada gunanya pindah ke lapisan berikutnya sampai lapisan (luar) sebelumnya telah diputar penuh. Yaitu, sampai setiap elemen dalam lapisan telah diputar. Hanya memutar sudut tidak akan berhasil!
Ambil napas dalam-dalam. Kami membutuhkan loop lain. Loop bersarang tidak kurang. Lingkaran bersarang yang baru, akan menggunakan variabel
first
danlast
, ditambah offset untuk menavigasi dalam lapisan. Kami akan menyebut loop baru ini sebagai 'loop elemen'. Lingkaran elemen akan mengunjungi setiap elemen di sepanjang baris atas, setiap elemen di sisi kanan, setiap elemen di sepanjang baris bawah dan setiap elemen di sisi kiri.Ini kedengarannya rumit, tetapi itu dibuat mudah karena berapa kali kita menambah dan mengurangi untuk mencapai hal di atas tetap sama di sepanjang keempat sisi matriks. Sebagai contoh:
Ini berarti kita dapat menggunakan variabel tunggal dalam kombinasi dengan
first
danlast
variabel untuk bergerak di dalam layer. Mungkin membantu untuk mencatat bahwa bergerak melintasi baris atas dan ke bawah di sisi kanan keduanya membutuhkan penambahan. Saat bergerak mundur di sepanjang bagian bawah dan di sisi kiri keduanya membutuhkan penurunan.Sekarang kita hanya perlu menetapkan bagian atas ke sisi kanan, sisi kanan ke bawah, bawah ke sisi kiri, dan sisi kiri ke atas. Menyatukan semua ini kita dapatkan:
Mengingat matriks:
rotate
Fungsi kami menghasilkan:sumber
list(zip(*reversed(your_list_of_lists)))
Python:
dan berlawanan arah jarum jam:
Bagaimana ini bekerja:
zip(*original)
akan menukar sumbu array 2d dengan menumpuk item yang sesuai dari daftar ke daftar baru. (*
Operator memberi tahu fungsi untuk mendistribusikan daftar yang ada ke dalam argumen)The
[::-1]
elemen membalikkan pernyataan array (lihat Diperpanjang Slices atau pertanyaan ini ):Akhirnya, menggabungkan keduanya akan menghasilkan transformasi rotasi.
Perubahan penempatan
[::-1]
akan membalik daftar di berbagai tingkat matriks.sumber
zip(*reversed(original))
alih-alihzip(*original[::-1])
menghindari membuat salinan tambahan dari daftar asli.Berikut adalah salah satu yang melakukan rotasi di tempat daripada menggunakan array yang sama sekali baru untuk menahan hasilnya. Saya telah mengabaikan inisialisasi array dan mencetaknya. Ini hanya berfungsi untuk array persegi tetapi ukurannya bisa berapa saja. Memori overhead sama dengan ukuran satu elemen array sehingga Anda dapat melakukan rotasi array sebesar yang Anda inginkan.
sumber
Ada banyak kode bagus di sini tapi saya hanya ingin menunjukkan apa yang terjadi secara geometris sehingga Anda dapat memahami logika kode sedikit lebih baik. Inilah cara saya mendekati ini.
pertama-tama, jangan bingung dengan transposisi yang sangat mudah ..
ide basica adalah untuk memperlakukannya sebagai lapisan dan kami memutar satu lapisan pada satu waktu ..
misalkan kita punya 4x4
setelah kita putar searah jarum jam dengan 90 kita dapatkan
jadi mari kita uraikan ini, pertama kita memutar 4 sudut dasarnya
lalu kita putar berlian berikut yang agak miring
dan kemudian berlian miring ke-2
sehingga menjaga tepi luar jadi pada dasarnya kita melakukan itu satu per satu sampai
akhirnya alun-alun (atau jika itu aneh hanya elemen terakhir yang tidak bergerak)
jadi sekarang mari kita cari tahu indeks masing-masing lapisan, anggap kita selalu bekerja dengan lapisan terluar, kita lakukan
seterusnya dan seterusnya sampai kita berada di tengah jalan
jadi secara umum polanya
sumber
Seperti yang saya katakan di posting saya sebelumnya, inilah beberapa kode dalam C # yang mengimplementasikan rotasi matriks O (1) untuk matriks ukuran apa pun. Untuk singkatnya dan mudah dibaca tidak ada pengecekan error atau pengecekan jangkauan. Kode:
OK, saya akan mengangkat tangan saya, itu tidak benar-benar melakukan modifikasi pada array asli ketika berputar. Tapi, dalam sistem OO yang tidak masalah selama objeknya terlihat diputar ke klien kelas. Saat ini, kelas Matrix menggunakan referensi ke data array asli sehingga mengubah nilai m1 juga akan mengubah m2 dan m3. Perubahan kecil ke konstruktor untuk membuat array baru dan menyalin nilai-nilai itu akan memilahnya.
sumber
Sementara memutar data di tempat mungkin diperlukan (mungkin untuk memperbarui representasi yang tersimpan secara fisik), menjadi lebih sederhana dan mungkin lebih banyak pemain untuk menambahkan lapisan tipuan ke akses array, mungkin sebuah antarmuka:
Jika Anda
Matrix
sudah mengimplementasikan antarmuka ini, maka itu dapat diputar melalui kelas dekorator seperti ini:Memutar + 90 / -90 / 180 derajat, membalik secara horizontal / vertikal dan penskalaan semua dapat dicapai dengan cara ini juga.
Kinerja perlu diukur dalam skenario spesifik Anda. Namun operasi O (n ^ 2) sekarang telah diganti dengan panggilan O (1). Ini adalah metode panggilan virtual yang merupakan lebih lambat dari akses array langsung, sehingga tergantung pada seberapa sering array diputar digunakan setelah rotasi. Jika digunakan sekali, maka pendekatan ini pasti akan menang. Jika diputar kemudian digunakan dalam sistem yang berjalan lama selama berhari-hari, maka rotasi di tempat mungkin berkinerja lebih baik. Itu juga tergantung apakah Anda dapat menerima biaya di muka.
Seperti halnya semua masalah kinerja, ukur, ukur, ukur!
sumber
Ini versi yang lebih baik di Jawa: Saya telah membuatnya untuk matriks dengan lebar dan tinggi yang berbeda
Kode ini didasarkan pada posting Nick Berardi.
sumber
Cara Ruby:
.transpose.map &:reverse
sumber
array.reverse.transpose
memutar array searah jarum jam, sementaraarray.transpose.reverse
memutarnya berlawanan arah jarum jam. Tidak perlumap
.Ada banyak jawaban, dan saya menemukan dua yang mengklaim kompleksitas waktu O (1). The nyata O (1) algoritma adalah untuk meninggalkan penyimpanan array yang tak tersentuh, dan mengubah cara Anda indeks unsur-unsurnya. Tujuannya di sini adalah tidak mengkonsumsi memori tambahan, juga tidak memerlukan waktu tambahan untuk mengulang data.
Rotasi 90, -90 dan 180 derajat adalah transformasi sederhana yang dapat dilakukan selama Anda tahu berapa banyak baris dan kolom dalam array 2D Anda; Untuk memutar vektor apa pun hingga 90 derajat, tukar sumbu dan negasikan sumbu Y. Untuk -90 derajat, tukar sumbu dan meniadakan sumbu X. Untuk 180 derajat, negasikan kedua sumbu tanpa bertukar.
Transformasi lebih lanjut dimungkinkan, seperti mirroring horizontal dan / atau vertikal dengan meniadakan sumbu secara mandiri.
Ini dapat dilakukan melalui misalnya metode accessor. Contoh di bawah ini adalah fungsi JavaScript, tetapi konsepnya berlaku sama untuk semua bahasa.
Kode ini mengasumsikan array array bertingkat, di mana setiap array bagian dalam adalah sebuah baris.
Metode ini memungkinkan Anda untuk membaca (atau menulis) elemen (bahkan dalam urutan acak) seolah-olah array telah diputar atau diubah. Sekarang tinggal pilih fungsi yang tepat untuk dipanggil, mungkin dengan referensi, dan pergilah!
Konsep ini dapat diperluas untuk menerapkan transformasi secara aditif (dan non-destruktif) melalui metode accessor. Termasuk rotasi sudut dan penskalaan yang sewenang-wenang.
sumber
Beberapa orang telah memasang contoh yang melibatkan pembuatan array baru.
Beberapa hal lain yang perlu dipertimbangkan:
(A) Alih-alih benar-benar memindahkan data, cukup melintasi array "diputar" secara berbeda.
(B) Melakukan rotasi di tempat bisa sedikit rumit. Anda membutuhkan sedikit tempat goresan (mungkin kira-kira sama dengan satu baris atau kolom). Ada sebuah makalah ACM kuno tentang melakukan transpos di tempat ( http://doi.acm.org/10.1145/355719.355729 ), tetapi kode contoh mereka adalah FORTRAN sarat muatan goto.
Tambahan:
http://doi.acm.org/10.1145/355611.355612 adalah algoritma transpos in-place yang dianggap lebih unggul.
sumber
Jawaban Nick akan bekerja untuk array NxM juga hanya dengan sedikit modifikasi (sebagai lawan dari NxN).
Salah satu cara untuk memikirkan hal ini adalah Anda telah memindahkan bagian tengah sumbu (0,0) dari sudut kiri atas ke sudut kanan atas. Anda hanya memindahkan dari satu ke yang lain.
sumber
Waktu - O (N), Spasi - O (1)
sumber
Ini versi Ruby saya (perhatikan bahwa nilainya tidak ditampilkan sama, tetapi masih berputar seperti yang dijelaskan).
Hasil:
sumber
inilah metode rotate dalam-ruang, dengan java, hanya untuk persegi. untuk array 2d non-square, Anda harus membuat array baru.
kode untuk memutar berbagai ukuran array 2d dengan membuat array baru:
sumber
Implementasi pseudocode +90 dimple (mis. Transpos lalu balikkan setiap baris) dalam JavaScript:
sumber
Anda dapat melakukan ini dalam 3 langkah mudah :
1 ) Misalkan kita memiliki matriks
2 ) Ambil transpos matriks
3 ) Pertukarkan baris untuk mendapatkan matriks yang diputar
Kode sumber Java untuk ini:
Keluaran:
sumber
Ini adalah implementasi saya, dalam kompleksitas memori C, O (1), dalam rotasi tempat, 90 derajat searah jarum jam:
sumber
Ini adalah versi Java:
metode pertama-tama memutar lapisan mostouter, kemudian pindah ke lapisan dalam secara kuadrat.
sumber
Dari sudut pandang linear, perhatikan matriks:
Sekarang ambil A transpose
Dan pertimbangkan aksi A 'pada B, atau B pada A'.
Masing-masing:
Ini dapat dikembangkan untuk matriks nxn. Dan menerapkan konsep ini dengan cepat dalam kode:
sumber
Kode C # untuk memutar [n, m] array 2D 90 derajat ke kanan
Hasil:
sumber
PHP:
Dari PHP5.6, transposisi array dapat dilakukan dengan
array_map()
panggilan sleak . Dengan kata lain, kolom dikonversi menjadi baris.Kode: ( Demo )
$ dialihkan:
sumber
For i:= 0 to X do For j := 0 to X do graphic[j][i] := graphic2[X-i][j]
X adalah ukuran dari array grafik.
sumber
#transpose adalah metode standar kelas Ruby Array, dengan demikian:
Implementasinya adalah fungsi transposisi n ^ 2 yang ditulis dalam C. Anda dapat melihatnya di sini: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-transpose dengan memilih "klik untuk beralih sumber "di samping" transpos ".
Saya ingat solusi yang lebih baik daripada O (n ^ 2), tetapi hanya untuk matriks yang dikonstruksi secara khusus (seperti matriks jarang)
sumber
Kode C untuk rotasi matriks 90 derajat searah jarum jam DI TEMPAT untuk setiap matriks M * N
sumber
di sini adalah implementasi In Place saya di C
sumber
Berikut adalah upaya saya untuk rotasi matriks 90 deg yang merupakan solusi 2 langkah dalam C. Pertama transpos matriks di tempat dan kemudian bertukar cols.
sumber
@dagorym: Ah, teman. Saya telah menggantung ini sebagai puzzle yang baik "Aku bosan, apa yang bisa saya renungkan". Saya datang dengan kode transposisi di tempat saya, tetapi tiba di sini untuk menemukan Anda cukup identik dengan milikku ... ah, well. Ini dia di Ruby.
sumber
Metode C ++ sederhana, bahwa akan ada overhead memori besar dalam array besar.
sumber