Permutace

Kromně obecného povídání o permutacích jsme na cvičení řešili následující úlohu:

Mějme dánu N-prvkovou permutaci. Jaké je její pořadí mezi všemi permutacemi délky N, pokud je uspořádáme lexikograficky?

Na cvičení jsme rozebírali dva algoritmy. První z nich danou úlohu neřešil, druhý ano. Výklad byl poněkud zmatený, proto zde jako satisfakci uvedu vysvětlení principu, na kterém lze postavit algoritmus, který realizuje převod oběma směry.

Převod permutace na kód a zpět

Algoritmus, který bude níže popsán, využívá speciální způsob reprezentace permutace. Abychom tento způsob lépe pochopili, podíváme se nejprve na lexikograficky setříděnou tabulku všech permutací 3-prvkové množiny písmen {A,B,C}.

permutacepořadíkód
ABC0000
ACB1010
BAC2100
BCA3110
CAB4200
CBA5210

Kód v posledním sloupci jednoznačně reprezentuje danou permutaci. Představme si, že permutaci zapisujeme zleva doprava a znak, který zapíšeme, si označíme jako použitý. Potom i-tá číslice v kódu odpovídá lexikografickému pořadí (číslujeme přitom od 0) právě zapisovaného znaku mezi všemi doposud nepoužitými znaky.


Konkrétní příklad: převod BAC na kód
  1. 1) Zapisujeme znak B. Žádný znak ještě není použit. V množině {A,B,C} je B lexikograficky 1. znak (při číslování od 0). První číslice (zleva) odpovídajícího kódu je tedy 1.
  2. 2) Zapisujeme znak A. Už je použito B. V množině {A,C} je A lexikograficky 0. znak. Druhá číslice kódu je 0.
  3. 3) Zapisujeme znak C. Je to poslední nepoužitý znak. Množina nepoužitých znaků je jednoprvková a C je v ní lexikograficky 0-tým (a jediným) znakem. Třetí číslice kódu je proto 0.

Nakonec tedy obdržíme pro permutaci BAC kód 100.


Z popsaného kódu lze permutaci také snadno rekonstruovat. Postupujeme-li opět zleva, stačí nám zase množina nepoužitých znaků. Číslice na i-té pozici v kódu nám přitom říká, kolikátý (myšleno lexikograficky a číslováno opět od 0) znak z množiny nepoužitých znaků máme na i-tou pozici napsat.

Konkrétní příklad: převod 210 na permutaci
  1. 1) Číslo 2 znamená, že máme vzít lexikograficky 2. znak z množiny {A,B,C}. Tím je znak C.
  2. 2) Číslo 1 znamená, že máme vzít lexikograficky 1. znak z množiny {A,B}. To je B.
  3. 3) Číslo 0 znamená, že máme vzít lexikograficky 0-tý znak z množiny {A}. V tuto chvíli nemáme moc na výběr, protože A je zároveň poslední dosud nepoužitý znak.

Kódu 210 tedy odpovídá permutace CBA.


Převod kódu na číslo a zpět.

Máme bezva kód, který reprezentuje permutaci. Jak z něj uděláme číslo?

Nejdříve otočíme číslování: kódy i permutace budeme nyní číslovat od zprava doleva a začínat budeme od 0. Je-li
code: array[0..N-1] of integer
náš kód a fac(x) funkce pro výpočet faktoriálu, získáme z něj číslo (num) následujícím postupem:

num:=0;

for i:=0 to N-1 do
	num:= num + code[i]*fac(i); 

Za povšimnutí stojí fakt, že 0-tou číslici kódu vlastně k ničemu nepotřebujeme. Stejně je vždy rovna 0 a do sumy nikdy ničím nepřispěje. Stejně dobře bychom mohli iterovat od i:=1.

Příklad: výpočet čísla z kódu 200

Jde vlastně o jednoduchou sumu:

0*0!+0*1!+2*2! = 0 + 0 + 2*2 = 4

Kód 200 jsme tedy převedli na číslo 4.


Při převodu z čísla na kód si vystačíme s následujícím kódem. Protože číslujeme všechno zprava a od nuly, nemusíme uvnitř cyklu nijak upravovat indexovou proměnnou i.

num // cislo, ktere prevadime
for i:=N-1 downto 0 do
	begin
		code[i]:= num div fac(i);
		num:= num mod fac(i); 
	end

// pole code ted obsahuje hledany kod

Příklad: výpočet kódu z čísla 3
  1. i=2: code[2]:= 3 div 2! (= 1)
  2. i=1: code[1]:= 1 div 1! (= 1)
  3. i=0: code[0]:= 0 div 0! (= 0) - poslední číslice kódu je vždy 0

Pro číslo 3 tedy získáme kód 110.

Algoritmus

Umíme realizovat následující převody:

  1. permutace <=> kód
  2. kód <=> číslo

Algoritmus získáme, když za sebe napojíme jednotlivé kroky.

Domácí úkol

Tentokrát žádné domácí úkoly.