N öğenin permütasyonunu açıklamak için, ilk öğenin bittiği konum için n olasılığa sahip olduğunuzu görürsünüz, böylece bunu 0 ile n-1 arasında bir sayı ile tanımlayabilirsiniz. Bir sonraki elemanın bittiği konum için n-1 olasılık kaldı, bu yüzden bunu 0 ile n-2 arasında bir sayıyla tanımlayabilirsiniz.
N sayı elde edene kadar vb.
N = 5 için bir örnek olarak, getiriyor permütasyon dikkate abcde
için caebd
.
a
, birinci eleman ikinci konumda bitiyor, bu yüzden onu indeks 1 olarak atıyoruz .
b
3. dizin olan dördüncü konumda sona erer, ancak bu kalan üçüncü konumdur, bu yüzden ona 2 atarız .
c
her zaman 0 olan ilk kalan konumda biter .
d
(sadece kalan iki konumdan) 1 olan son kalan konumda sona erer .
e
0'da dizine alınmış kalan tek konumda sona erer .
Yani dizin dizimiz {1, 2, 0, 1, 0} var .
Artık, örneğin bir ikili sayı içinde 'xyz', z + 2y + 4x anlamına gelir. Ondalık sayı
için z + 10y + 100x olur. Her rakam bir miktar ağırlık ile çarpılır ve sonuçlar toplanır. Ağırlıktaki bariz model elbette ağırlığın w = b ^ k, b sayının tabanı ve k rakamın indeksi olmasıdır. (Her zaman sağdan rakamları sayacağım ve en sağdaki rakam için indeks 0'dan başlayacağım. Aynı şekilde 'ilk' rakamdan bahsederken en sağdaki rakamdan bahsediyorum.)
Nedeni basamak için ağırlıklar bu modeli takip niye k 0'dan basamağı ile temsil edilebilir en yüksek sayı sadece haneli k + 1 kullanmaya ile temsil edilebilir en düşük sayıdan daha tam olarak 1 düşük olması gerektiğidir. İkili değerde 0111, 1000'den küçük olmalıdır. Ondalık olarak 099999, 100000'den küçük olmalıdır.
Değişken tabanlı kodlama
Sonraki sayılar arasındaki aralığın tam olarak 1 olması önemli kuraldır. Bunu gerçekleştirerek, dizin dizimizi değişken tabanlı bir sayı ile temsil edebiliriz . Her hane için taban, o hane için farklı olasılıkların miktarıdır. Ondalık sayı için her basamağın 10 olasılığı vardır, sistemimiz için en sağdaki basamak 1 olasılığa ve en soldaki n olasılığa sahip olacaktır. Ancak en sağdaki rakam (dizimizdeki son sayı) her zaman 0 olduğu için, onu dışarıda bırakıyoruz. Bu, 2'den n'ye kadar bazlarla kaldığımız anlamına gelir. Genel olarak, k'inci basamağın tabanı b [k] = k + 2 olacaktır. K basamağı için izin verilen en yüksek değer h [k] = b [k] - 1 = k + 1'dir.
Basamakların ağırlıkları w [k] ile ilgili kuralımız, i = 0'dan i = k'ye giderken h [i] * w [i] toplamının 1 * w [k + 1] 'e eşit olmasını gerektirir. Yinelenen ifade edilirse, w [k + 1] = w [k] + h [k] * w [k] = w [k] * (h [k] + 1). İlk ağırlık w [0] her zaman 1 olmalıdır. Buradan başlayarak şu değerlere sahibiz:
k h[k] w[k]
0 1 1
1 2 2
2 3 6
3 4 24
... ... ...
n-1 n n!
(W [k-1] = k! Genel ilişkisi, tümevarım ile kolayca kanıtlanır.)
Dizimizi dönüştürerek elde ettiğimiz sayı, k 0'dan n-1'e koşarken, s [k] * w [k] toplamı olacaktır. Burada s [k], dizinin k'inci (en sağdaki, 0'dan başlayarak) öğesidir. Örnek olarak, daha önce belirtildiği gibi en sağdaki öğenin çıkarıldığı {1, 2, 0, 1, 0} öğemizi alın: {1, 2, 0, 1} . Toplamımız 1 * 1 + 0 * 2 + 2 * 6 + 1 * 24 = 37 .
Her dizin için maksimum konumu alırsak, {4, 3, 2, 1, 0} elde edeceğimizi ve bunun 119'a dönüşeceğini unutmayın. Sayı kodlamamızdaki ağırlıklar, atlamamamız için seçildiğinden herhangi bir sayı, 0 ile 119 arasındaki tüm sayılar geçerlidir. Bunlardan tam olarak 120 tane var, ki bu n! Örneğimizde n = 5 için, tam olarak farklı permütasyonların sayısı. Böylece kodlanmış sayılarımızın tüm olası permütasyonları tamamen belirttiğini görebilirsiniz.
Değişken tabanlı
Kod Çözme'den kod çözme, ikili veya ondalık sayıya dönüştürmeye benzer. Ortak algoritma şudur:
int number = 42;
int base = 2;
int[] bits = new int[n];
for (int k = 0; k < bits.Length; k++)
{
bits[k] = number % base;
number = number / base;
}
Değişken tabanlı numaramız için:
int n = 5;
int number = 37;
int[] sequence = new int[n - 1];
int base = 2;
for (int k = 0; k < sequence.Length; k++)
{
sequence[k] = number % base;
number = number / base;
base++; // b[k+1] = b[k] + 1
}
Bu doğru {1, 2, 0, 1} bizim 37 geri çözer ( sequence
olurdu {1, 0, 2, 1}
bu kod örneğinde, ama ne olursa olsun ... sürece endeks olarak uygun). Orijinal dizimizi {1, 2, 0, 1, 0} geri almak için sağ uca 0 eklememiz gerekir (son elemanın yeni konumu için her zaman sadece bir olasılığı olduğunu unutmayın).
Bir dizin dizisi kullanarak bir
listeye izin verme Belirli bir dizin sırasına göre bir listeye izin vermek için aşağıdaki algoritmayı kullanabilirsiniz. Ne yazık ki bu bir O (n²) algoritması.
int n = 5;
int[] sequence = new int[] { 1, 2, 0, 1, 0 };
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
bool[] set = new bool[n];
for (int i = 0; i < n; i++)
{
int s = sequence[i];
int remainingPosition = 0;
int index;
// Find the s'th position in the permuted list that has not been set yet.
for (index = 0; index < n; index++)
{
if (!set[index])
{
if (remainingPosition == s)
break;
remainingPosition++;
}
}
permuted[index] = list[i];
set[index] = true;
}
Permütasyonların ortak temsili
Normalde, bizim yaptığımız kadar beklenmedik bir şekilde bir permütasyonu temsil etmezsiniz, sadece permütasyon uygulandıktan sonra her bir elementin mutlak konumu ile temsil edersiniz. Örneğimiz {1, 2, 0, 1, 0} for abcde
to caebd
normalde {1, 3, 0, 4, 2} ile temsil edilir. 0'dan 4'e (veya genel olarak, 0'dan n-1'e) her bir indeks, bu gösterimde tam olarak bir kez meydana gelir.
Bu formda bir permütasyon uygulamak kolaydır:
int[] permutation = new int[] { 1, 3, 0, 4, 2 };
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
for (int i = 0; i < n; i++)
{
permuted[permutation[i]] = list[i];
}
Tersine çevirmek çok benzer:
for (int i = 0; i < n; i++)
{
list[i] = permuted[permutation[i]];
}
Temsilimizden ortak temsile dönüştürme
Algoritmamızı indeks dizimizi kullanarak bir listeye izin vermek için alırsak ve bunu {0, 1, 2, ..., n-1} kimlik permütasyonuna uygularsak, şunu elde ederiz: ortak biçimde temsil edilen ters permütasyon. ( Örneğimizde {2, 0, 4, 1, 3} ).
Tersine çevrilmemiş ön mutasyonu elde etmek için az önce gösterdiğim permütasyon algoritmasını uyguluyoruz:
int[] identity = new int[] { 0, 1, 2, 3, 4 };
int[] inverted = { 2, 0, 4, 1, 3 };
int[] normal = new int[n];
for (int i = 0; i < n; i++)
{
normal[identity[i]] = list[i];
}
Veya ters permütasyon algoritmasını kullanarak permütasyonu doğrudan uygulayabilirsiniz:
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
int[] inverted = { 2, 0, 4, 1, 3 };
for (int i = 0; i < n; i++)
{
permuted[i] = list[inverted[i]];
}
Ortak formdaki permütasyonlarla uğraşmak için tüm algoritmaların O (n) olduğunu, formumuzda bir permütasyon uygulamak için O (n²) olduğunu unutmayın. Birkaç kez bir permütasyon uygulamanız gerekiyorsa, önce onu ortak temsile dönüştürün.