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}
.
permutace | pořadí | kód |
ABC | 0 | 000 |
ACB | 1 | 010 |
BAC | 2 | 100 |
BCA | 3 | 110 |
CAB | 4 | 200 |
CBA | 5 | 210 |
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) Zapisujeme znak
B
. Žádný znak ještě není použit. V množině{A,B,C}
jeB
lexikograficky 1. znak (při číslování od 0). První číslice (zleva) odpovídajícího kódu je tedy 1. - 2) Zapisujeme znak
A
. Už je použitoB
. V množině{A,C}
jeA
lexikograficky 0. znak. Druhá číslice kódu je 0. - 3) Zapisujeme znak
C
. Je to poslední nepoužitý znak. Množina nepoužitých znaků je jednoprvková aC
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) Číslo 2 znamená, že máme vzít lexikograficky 2. znak z množiny
{A,B,C}
. Tím je znakC
. - 2) Číslo 1 znamená, že máme vzít lexikograficky 1. znak z množiny
{A,B}
. To jeB
. - 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žeA
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
- i=2:
code[2]:= 3 div 2! (= 1)
- i=1:
code[1]:= 1 div 1! (= 1)
- 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:
- permutace <=> kód
- kód <=> číslo
Algoritmus získáme, když za sebe napojíme jednotlivé kroky.
Domácí úkol
Tentokrát žádné domácí úkoly.