Saya memiliki n elemen. Sebagai contoh, katakanlah, 7 elemen, 1234567. Saya tahu ada 7! = 5040 kemungkinan permutasi dari 7 elemen ini.
Saya ingin algoritme cepat yang terdiri dari dua fungsi:
f (angka) memetakan angka antara 0 dan 5039 ke permutasi unik, dan
f '(permutasi) memetakan permutasi kembali ke nomor asalnya.
Saya tidak peduli tentang korespondensi antara bilangan dan permutasi, asalkan setiap permutasi memiliki nomor uniknya sendiri.
Jadi, misalnya, saya mungkin memiliki fungsi di mana
f(0) = '1234567'
f'('1234567') = 0
Algoritme tercepat yang terlintas dalam pikiran adalah menghitung semua permutasi dan membuat tabel pencarian di kedua arah, sehingga, setelah tabel dibuat, f (0) akan menjadi O (1) dan f ('1234567') menjadi a mencari string. Namun, ini adalah memori haus, terutama ketika n menjadi besar.
Adakah yang bisa mengusulkan algoritma lain yang akan bekerja dengan cepat dan tanpa kerugian memori?
Jawaban:
Untuk mendeskripsikan permutasi n elemen, Anda lihat bahwa untuk posisi akhir elemen pertama, Anda memiliki n kemungkinan, jadi Anda dapat mendeskripsikannya dengan angka antara 0 dan n-1. Untuk posisi akhir elemen berikutnya, Anda memiliki n-1 kemungkinan yang tersisa, sehingga Anda dapat mendeskripsikannya dengan angka antara 0 dan n-2.
Dan lain-lain sampai Anda memiliki n nomor.
Sebagai contoh untuk n = 5, perhatikan permutasi yang membawa
abcde
kecaebd
.a
, elemen pertama, berakhir di posisi kedua, jadi kami menetapkannya indeks 1 .b
berakhir di posisi keempat, yang akan menjadi indeks 3, tetapi itu adalah posisi ketiga yang tersisa, jadi kami menetapkannya 2 .c
berakhir di posisi tersisa pertama, yang selalu 0 .d
berakhir di posisi terakhir yang tersisa, yang (dari hanya dua posisi tersisa) adalah 1 .e
berakhir di satu-satunya posisi yang tersisa, diindeks pada 0 .Jadi kami memiliki urutan indeks {1, 2, 0, 1, 0} .
Sekarang Anda tahu bahwa misalnya dalam bilangan biner, 'xyz' berarti z + 2y + 4x. Untuk bilangan desimal,
z + 10y + 100x. Setiap digit dikalikan dengan beberapa bobot, dan hasilnya dijumlahkan. Pola yang jelas dalam bobot adalah tentu saja bahwa bobotnya adalah w = b ^ k, dengan b basis bilangan dan k indeks digit. (Saya akan selalu menghitung digit dari kanan dan mulai dari indeks 0 untuk digit paling kanan. Begitu pula ketika saya berbicara tentang digit 'pertama' yang saya maksudkan paling kanan.)
The Alasan mengapa bobot untuk digit mengikuti pola ini adalah bahwa jumlah tertinggi yang dapat diwakili oleh angka dari 0 sampai k harus tepat 1 lebih rendah dari jumlah terendah yang dapat diwakili dengan hanya menggunakan digit k + 1. Dalam biner, 0111 harus lebih rendah dari 1000. Dalam desimal, 099999 harus lebih rendah dari 100000.
Pengkodean ke basis-variabel
Jarak antara angka-angka berikutnya menjadi tepat 1 adalah aturan penting. Menyadari hal ini, kita dapat merepresentasikan urutan indeks kita dengan nomor basis variabel . Basis untuk setiap digit adalah jumlah kemungkinan berbeda untuk digit tersebut. Untuk desimal, setiap digit memiliki 10 kemungkinan, untuk sistem kami digit paling kanan memiliki 1 kemungkinan dan digit paling kiri memiliki n kemungkinan. Tetapi karena digit paling kanan (angka terakhir dalam urutan kita) selalu 0, kita biarkan saja. Itu berarti kita memiliki basis 2 hingga n. Secara umum, digit ke-k akan memiliki basis b [k] = k + 2. Nilai tertinggi yang diperbolehkan untuk digit k adalah h [k] = b [k] - 1 = k + 1.
Aturan kita tentang bobot w [k] digit mensyaratkan bahwa jumlah dari h [i] * w [i], di mana i berpindah dari i = 0 ke i = k, sama dengan 1 * w [k + 1]. Dinyatakan berulang kali, w [k + 1] = w [k] + h [k] * w [k] = w [k] * (h [k] + 1). Bobot pertama w [0] harus selalu 1. Mulai dari sana, kami memiliki nilai berikut:
(Hubungan umum w [k-1] = k! Dengan mudah dibuktikan dengan induksi.)
Angka yang kita peroleh dari konversi urutan kita akan menjadi jumlah dari s [k] * w [k], dengan k berjalan dari 0 ke n-1. Di sini s [k] adalah elemen k'th (paling kanan, dimulai dari 0) dari barisan tersebut. Sebagai contoh, ambil {1, 2, 0, 1, 0} kami, dengan elemen paling kanan dilepas seperti yang disebutkan sebelumnya: {1, 2, 0, 1} . Jumlah kita adalah 1 * 1 + 0 * 2 + 2 * 6 + 1 * 24 = 37 .
Perhatikan bahwa jika kita mengambil posisi maksimum untuk setiap indeks, kita akan memiliki {4, 3, 2, 1, 0}, dan itu akan dikonversi menjadi 119. Karena bobot dalam penyandian angka kami dipilih sehingga kami tidak melewatkan nomor apa pun, semua angka 0 hingga 119 valid. Tepatnya ada 120 di antaranya, yaitu n! untuk n = 5 dalam contoh kita, tepatnya jumlah permutasi yang berbeda. Jadi Anda dapat melihat nomor yang dikodekan kami benar-benar menentukan semua kemungkinan permutasi.
Decoding dari variable-base
Decoding mirip dengan mengkonversi ke biner atau desimal. Algoritma yang umum adalah ini:
Untuk bilangan basis variabel kami:
Ini dengan benar menerjemahkan 37 kembali ke {1, 2, 0, 1} (
sequence
akan ada{1, 0, 2, 1}
dalam contoh kode ini, tapi terserah ... selama Anda mengindeks dengan tepat). Kita hanya perlu menambahkan 0 di ujung kanan (ingat elemen terakhir selalu hanya memiliki satu kemungkinan untuk posisi barunya) untuk mendapatkan kembali urutan awal kita {1, 2, 0, 1, 0}.Mengizinkan daftar menggunakan urutan indeks
Anda dapat menggunakan algoritma di bawah ini untuk mengubah daftar menurut urutan indeks tertentu. Sayangnya, ini adalah algoritma O (n²).
Representasi umum permutasi
Biasanya Anda tidak akan merepresentasikan permutasi secara tidak intuitif seperti yang kita lakukan, tetapi hanya dengan posisi absolut setiap elemen setelah permutasi diterapkan. Contoh kita {1, 2, 0, 1, 0} untuk
abcde
tocaebd
biasanya diwakili oleh {1, 3, 0, 4, 2}. Setiap indeks dari 0 hingga 4 (atau secara umum, 0 hingga n-1) muncul tepat sekali dalam representasi ini.Menerapkan permutasi dalam formulir ini mudah:
Membaliknya sangat mirip:
Mengonversi dari representasi kita ke representasi umum
Perhatikan bahwa jika kita menggunakan algoritme untuk mengubah daftar menggunakan urutan indeks kita, dan menerapkannya ke permutasi identitas {0, 1, 2, ..., n-1}, kita mendapatkan permutasi terbalik , direpresentasikan dalam bentuk umum. ( {2, 0, 4, 1, 3} dalam contoh kita).
Untuk mendapatkan premutasi non-terbalik, kami menerapkan algoritma permutasi yang baru saja saya tunjukkan:
Atau Anda bisa menerapkan permutasi secara langsung, dengan menggunakan algoritma permutasi terbalik:
Perhatikan bahwa semua algoritme untuk menangani permutasi dalam bentuk umum adalah O (n), sedangkan menerapkan permutasi dalam bentuk kita adalah O (n²). Jika Anda perlu menerapkan permutasi beberapa kali, konversikan dulu ke representasi umum.
sumber
1234
, f (4) = {0, 2, 0, 0} = 1342. Dan f '(1423) = {0, 1 1, 0} = 3. Algoritme ini benar-benar menginspirasi. Saya ingin tahu itu adalah karya asli dari OP. saya telah mempelajari dan menganalisisnya untuk sementara waktu. Dan saya percaya itu benar :){1, 2, 0, 1, 0}
->{1, 3, 0, 4, 2}
? Dan sebaliknya? Apa itu mungkin? (dengan tidak mengonversi antara{1, 2, 0, 1, 0}
<-->{C, A, E, B, D}
, yang membutuhkan O (n ^ 2).) Jika "gaya kami" dan "gaya umum" tidak dapat diubah, sebenarnya keduanya adalah dua hal yang berbeda, bukan? Terima kasih xSaya menemukan algoritma O (n), berikut penjelasan singkatnya http://antoinecomeau.blogspot.ca/2014/07/mapping-between-permutations-and.html
sumber
Kompleksitas dapat diturunkan ke n * log (n), lihat bagian 10.1.1 ("Kode Lehmer (tabel inversi)", hal.232ff) dari fxtbook: http://www.jjj.de/fxt/ #fxtbook lompat ke bagian 10.1.1.1 ("Komputasi dengan larik besar" hal.235) untuk metode cepat. Kode (GPLed, C ++) ada di halaman web yang sama.
sumber
Masalah terpecahkan. Namun, saya tidak yakin Anda masih membutuhkan solusinya setelah bertahun-tahun ini. LOL, saya baru bergabung dengan situs ini, jadi ... Periksa Kelas Permutasi Java saya. Anda dapat mendasarkan pada indeks untuk mendapatkan permutasi simbol, atau memberikan permutasi simbol lalu mendapatkan indeks.
Ini Kelas Premutasi saya
dan inilah Kelas Utama saya untuk menunjukkan cara menggunakan kelas.
Selamat bersenang-senang. :)
sumber
Setiap elemen dapat berada di salah satu dari tujuh posisi. Untuk mendeskripsikan posisi satu elemen, Anda membutuhkan tiga bit. Itu berarti Anda dapat menyimpan posisi semua elemen dalam nilai 32bit. Itu jauh dari efisien, karena representasi ini bahkan akan memungkinkan semua elemen berada pada posisi yang sama, tetapi saya percaya bit-masking seharusnya cukup cepat.
Namun, dengan lebih dari 8 posisi, Anda memerlukan sesuatu yang lebih bagus.
sumber
Ini kebetulan menjadi fungsi bawaan di J :
sumber
Anda dapat mengenkode permutasi menggunakan algoritme rekursif. Jika permutasi-N (beberapa urutan angka {0, .., N-1}) berbentuk {x, ...} maka enkodekan sebagai x + N * pengkodean (N-1) -permutasi diwakili oleh "..." pada angka {0, N-1} - {x}. Kedengarannya seperti suap, berikut beberapa kodenya:
Algoritma ini adalah O (n ^ 2). Poin bonus jika ada yang memiliki algoritma O (n).
sumber
Sungguh pertanyaan yang menarik!
Jika semua elemen Anda adalah angka, Anda mungkin ingin mempertimbangkan untuk mengubahnya dari string menjadi angka sebenarnya. Kemudian Anda akan dapat mengurutkan semua permutasi dengan menyusunnya, dan menempatkannya dalam sebuah array. Setelah itu, Anda akan terbuka untuk salah satu dari berbagai algoritma pencarian di luar sana.
sumber
Saya terburu-buru dalam jawaban saya sebelumnya (dihapus), saya memiliki jawaban yang sebenarnya. Ini disediakan oleh konsep serupa, faktoradic , dan terkait dengan permutasi (jawaban saya terkait dengan kombinasi, saya minta maaf atas kebingungan itu). Saya benci hanya memposting tautan wikipedia, tetapi saya menulis artikel yang saya lakukan beberapa waktu yang lalu tidak dapat dipahami karena beberapa alasan. Jadi, saya dapat mengembangkannya nanti jika diminta.
sumber
Ada sebuah buku yang menulis tentang ini. Maaf, tapi saya tidak ingat namanya (kemungkinan besar Anda akan menemukannya dari wikipedia). tapi bagaimanapun saya menulis implementasi python dari sistem pencacahan itu: http://kks.cabal.fi/Kombinaattori Beberapa di antaranya dalam bahasa Finlandia, tetapi cukup salin kode dan variabel nama ...
sumber
Saya memiliki pertanyaan yang tepat ini dan berpikir saya akan memberikan solusi Python saya. Ini O (n ^ 2).
Ini sangat mudah; setelah membuat representasi faktorad dari nomor tersebut, saya hanya memilih dan menghapus karakter dari string. Menghapus dari string adalah mengapa ini adalah solusi O (n ^ 2).
Solusi Antoine lebih baik untuk performa.
sumber
Sebuah pertanyaan terkait adalah menghitung permutasi terbalik, permutasi yang akan mengembalikan vektor yang diijinkan ke urutan semula ketika hanya array permutasi yang diketahui. Berikut adalah kode O (n) (dalam PHP):
Perangkat Lunak David Spector Springtime
sumber