辞書順で N 番目の順列を求める
概要
次の問題を解くことを考える.
この問題は,次の3段階で解くことができる.
- 長さ $S$ の順列のうち,辞書順で $N$ 番目のものを求める
- たとえば,長さ $5$ の順列のうち,辞書順で $50$ 番目のものは $(3, 1, 4, 2, 5)$ である
- 1. で求めた順列の逆置換(=単調増加列にするための置換)を求める
- たとえば,順列 $(3, 1, 4, 2, 5)$ に対して置換 $(2, 4, 1, 3, 5)$ を適用すると,順列 $(1, 2, 3, 4, 5)$ になる
- これは容易に求められる
- 2. で求めた置換を順列とみなし,その順列が辞書順で何番目か求める
- たとえば,順列 $(2, 4, 1, 3, 5)$ は,長さ $5$ の順列のうち,辞書順で $36$ 番目である
- 既出:K - 辞書順で何番目?
ここで難関になるのは,「1. 長さ $S$ の順列のうち,辞書順で $N$ 番目のものを求める」の部分である.
辞書順で N 番目の順列を求める
コンテスト本番中は,下記ページを参考にしてプログラムを書いた.
曰く,次のような手順で,長さ $S$ の順列のうち,辞書順で $N$ 番目の順列を得ることができる.
- $N$ を $1$ で割り,その商をさらに $2$ で割り,その商をさらに $3$ で割り,…と繰り返し,商を $S$ で割るまで繰り返す.このとき,各除算で出た余りを $r_1, r_2, \dots, r_S$ とする.
- 数列 $A = (1, 2, \dots, S)$ および空数列 $B$ を用意し,$k$ を $r_S$ から $r_1$ まで順に辿りながら,次の操作をする.
- 数列 $A$ から, 0-indexed で $k$ 番目の要素を取り除き,$B$ の末尾に追加する.
- 2. の操作が終了したときの数列 $B$ が,長さ $S$ の順列のうち,辞書順で $N$ 番目のものになっている.
もう少しちゃんと書くと,こうである.
- for $d=1$ to $S$:
- $r_i \leftarrow N \bmod d$
- $N \leftarrow N\:\mathrm{div}\:d$
- $A = (1, 2, \dots, S)$
- $B = ()$
- for $i=S$ to $1$:
- $k \leftarrow r_i$
- $B$.append($A_k$)
- $A$.remove($A_k$)
- return $B$
なんのこっちゃという話である.
順列を求める例
睨んでもわからないので,例を追っていこう.
長さ $5$ の順列のうち,辞書順で $50$ 番目のものを求めてみよう.
除算を繰り返すところは,こうなる.
- $50 \div \textcolor{blue}{1} = 50 \cdots \textcolor{red}{0}$ → $r_1$
- $50 \div \textcolor{blue}{2} = 25 \cdots \textcolor{red}{0}$ → $r_2$
- $25 \div \textcolor{blue}{3} = 8 \cdots \textcolor{red}{1}$ → $r_3$
- $8 \div \textcolor{blue}{4} = 2 \cdots \textcolor{red}{0}$ → $r_4$
- $2 \div \textcolor{blue}{5} = 0 \cdots \textcolor{red}{2}$ → $r_5$
したがって,$(r_5, r_4, r_3, r_2, r_1) = (2, 0, 1, 0, 0)$ である.
ここで,数列 $(1, 2, 3, 4, 5)$ を用意して,この中から要素を順番に選び出していく.
- $(1, 2, 3, 4, 5)$ のうち,0-indexed で $2$ 番目は $\textcolor{red}{3}$
- $(1, 2, 4, 5)$ のうち,0-indexed で $0$ 番目は $\textcolor{red}{1}$
- $(2, 4, 5)$ のうち,0-indexed で $1$ 番目は $\textcolor{red}{4}$
- $(2, 5)$ のうち,0-indexed で $0$ 番目は $\textcolor{red}{2}$
- $(5)$ のうち,0-indexed で $0$ 番目は $\textcolor{red}{5}$
よって求める順列は $(3, 1, 4, 2, 5)$ である.冒頭の例で載せた答えと一致する.
なぜ上手くいくのか
上述の方法がなぜ上手くいくのか,考えてみる.上の $(3, 1, 4, 2, 5)$ の例をまた使ってみる.
長さ $5$ の順列は,全部で $5! = 120$ 通りである.これらは,次のように分類できる.
- $(1, ?,?,?,?)$ 型 … $4! = 24$ 通り
- $(2, ?,?,?,?)$ 型 … $4! = 24$ 通り
- $(3, ?,?,?,?)$ 型 … $4! = 24$ 通り
- $(4, ?,?,?,?)$ 型 … $4! = 24$ 通り
- $(5, ?,?,?,?)$ 型 … $4! = 24$ 通り
ここで,$(3, ?,?,?,?)$ 型についてさらに分類してみると次のようになる.
- $(3, 1, ?,?,?)$ 型 … $3! = 6$ 通り
- $(3, 2, ?,?,?)$ 型 … $3! = 6$ 通り
- $(3, 4, ?,?,?)$ 型 … $3! = 6$ 通り
- $(3, 5, ?,?,?)$ 型 … $3! = 6$ 通り
ここで,$(3, 1, ?,?,?)$ 型についてさらに分類してみると次のようになる.
- $(3, 1, 2, ?,?)$ 型 … $2! = 2$ 通り
- $(3, 1, 4, ?,?)$ 型 … $2! = 2$ 通り
- $(3, 1, 5, ?,?)$ 型 … $2! = 2$ 通り
ここで,$(3, 1, 4, ?,?)$ 型についてさらに分類してみると次のようになる.
- $(3, 1, 4, 2, ?)$ 型 … $1! = 1$ 通り
- $(3, 1, 4, 5, ?)$ 型 … $1! = 1$ 通り
ところで,除算を繰り返していた部分では,こんな計算をしていたのだった.
- $50 \div \textcolor{blue}{1} = 50 \cdots \textcolor{red}{0}$
- $50 \div \textcolor{blue}{2} = 25 \cdots \textcolor{red}{0}$
- $25 \div \textcolor{blue}{3} = 8 \cdots \textcolor{red}{1}$
- $8 \div \textcolor{blue}{4} = 2 \cdots \textcolor{red}{0}$
- $2 \div \textcolor{blue}{5} = 0 \cdots \textcolor{red}{2}$
上記の式は,以下の形で表現できる.
- $50 = \textcolor{blue}{1} \times 50 + \textcolor{red}{0}$
- $50 = \textcolor{blue}{2} \times 25 + \textcolor{red}{0}$
- $25 = \textcolor{blue}{3} \times 8 + \textcolor{red}{1}$
- $8 = \textcolor{blue}{4} \times 2 + \textcolor{red}{0}$
- $2 = \textcolor{blue}{5} \times 0 + \textcolor{red}{2}$
上の式たちは,ユークリッドの互除法のときによくあるやり方と同じように,下の式を上の式に埋め込むような形で,下の方から順に,次のよう整理できる.
- $8 = \textcolor{blue}{4} \times \left(\textcolor{blue}{5} \times 0 + \textcolor{red}{2}\right) + \textcolor{red}{0}$
- $25 = \textcolor{blue}{3} \times \left(\textcolor{blue}{4} \times \left(\textcolor{blue}{5} \times 0 + \textcolor{red}{2}\right) + \textcolor{red}{0}\right) + \textcolor{red}{1}$
- $50 = \textcolor{blue}{2} \times \left(\textcolor{blue}{3} \times \left(\textcolor{blue}{4} \times \left(\textcolor{blue}{5} \times 0 + \textcolor{red}{2}\right) + \textcolor{red}{0}\right) + \textcolor{red}{1}\right) + \textcolor{red}{0}$
- $50 = \textcolor{blue}{1} \times \left(\textcolor{blue}{2} \times \left(\textcolor{blue}{3} \times \left(\textcolor{blue}{4} \times \left(\textcolor{blue}{5} \times 0 + \textcolor{red}{2}\right) + \textcolor{red}{0}\right) + \textcolor{red}{1}\right) + \textcolor{red}{0}\right) + \textcolor{red}{0}$
整理して最後に出てきた式は,展開して整理できる.
- $50 = \textcolor{blue}{1} \times \left(\textcolor{blue}{2} \times \left(\textcolor{blue}{3} \times \left(\textcolor{blue}{4} \times \left(\textcolor{blue}{5} \times 0 + \textcolor{red}{2}\right) + \textcolor{red}{0}\right) + \textcolor{red}{1}\right) + \textcolor{red}{0}\right) + \textcolor{red}{0}$
- $50 = \textcolor{blue}{1} \times \left(\textcolor{blue}{2} \times \left(\textcolor{blue}{3} \times \left(\textcolor{blue}{4} \times \textcolor{blue}{5} \times 0 + \textcolor{blue}{4} \times \textcolor{red}{2} + \textcolor{red}{0}\right) + \textcolor{red}{1}\right) + \textcolor{red}{0}\right) + \textcolor{red}{0}$
- $50 = \textcolor{blue}{1} \times \left(\textcolor{blue}{2} \times \left(\textcolor{blue}{3} \times \textcolor{blue}{4} \times \textcolor{blue}{5} \times 0 + \textcolor{blue}{3} \times \textcolor{blue}{4} \times \textcolor{red}{2} + \textcolor{blue}{3} \times \textcolor{red}{0} + \textcolor{red}{1}\right) + \textcolor{red}{0}\right) + \textcolor{red}{0}$
- $50 = \textcolor{blue}{1} \times \left(\textcolor{blue}{2} \times \textcolor{blue}{3} \times \textcolor{blue}{4} \times \textcolor{blue}{5} \times 0 + \textcolor{blue}{2} \times \textcolor{blue}{3} \times \textcolor{blue}{4} \times \textcolor{red}{2} + \textcolor{blue}{2} \times \textcolor{blue}{3} \times \textcolor{red}{0} + \textcolor{blue}{2} \times \textcolor{red}{1} + \textcolor{red}{0}\right) + \textcolor{red}{0}$
- $50 = \textcolor{blue}{1} \times \textcolor{blue}{2} \times \textcolor{blue}{3} \times \textcolor{blue}{4} \times \textcolor{blue}{5} \times 0 + \textcolor{blue}{1} \times \textcolor{blue}{2} \times \textcolor{blue}{3} \times \textcolor{blue}{4} \times \textcolor{red}{2} + \textcolor{blue}{1} \times \textcolor{blue}{2} \times \textcolor{blue}{3} \times \textcolor{red}{0} + \textcolor{blue}{1} \times \textcolor{blue}{2} \times \textcolor{red}{1} + \textcolor{blue}{1} \times \textcolor{red}{0} + \textcolor{red}{0}$
- $50 = \textcolor{blue}{5!} \times 0 + \textcolor{blue}{4!} \times \textcolor{red}{2} + \textcolor{blue}{3!} \times \textcolor{red}{0} + \textcolor{blue}{2!} \times \textcolor{red}{1} + \textcolor{blue}{1!} \times \textcolor{red}{0} + \textcolor{red}{0}$
さて,意味ありげな式が出てきたので,この意味を考えていく.$50 = \textcolor{blue}{5!} \times 0 + \textcolor{blue}{4!} \times \textcolor{red}{2} + \textcolor{blue}{3!} \times \textcolor{red}{0} + \textcolor{blue}{2!} \times \textcolor{red}{1} + \textcolor{blue}{1!} \times \textcolor{red}{0} + \textcolor{red}{0}$ というのはつまり,
- $50$ という数字は,
- $\textcolor{blue}{5!}$ が $0$ 個(あたりまえ)
- $\textcolor{blue}{4!}$ が $\textcolor{red}{2}$ 個
- $\textcolor{blue}{3!}$ が $\textcolor{red}{0}$ 個
- $\textcolor{blue}{2!}$ が $\textcolor{red}{1}$ 個
- $\textcolor{blue}{1!}$ が $\textcolor{red}{0}$ 個
- 最後に $\textcolor{red}{0}$ が余る(ひとまず無視)
- と分解することができる
ということを意味している.これは,
- $\textcolor{blue}{5!}$ が $0$ 個
- 長さ $5$ の順列は全部で $5!$ 通りしかないので,当然 $0$ になる
- あえていうなら,$(?, ?,?,?,?)$ 型 であることを主張している
- $\textcolor{blue}{4!}$ が $\textcolor{red}{2}$ 個
- $(1, ?,?,?,?)$ 型, $(2, ?,?,?,?)$ 型 は通り過ぎるので,$(3, ?,?,?,?)$ 型 であることがわかる
- $\textcolor{blue}{3!}$ が $\textcolor{red}{0}$ 個
- $(3, ?,?,?,?)$ 型 の中で,$\textcolor{blue}{3!}$ を1つも作れないということは,$(3, 1, ?,?,?)$ 型 である
- $\textcolor{blue}{2!}$ が $\textcolor{red}{1}$ 個
- $(3, 1, ?,?,?)$ 型 のうち,$(3, 1, 2, ?,?)$ 型 は通り過ぎるので,$(3, 1, 4, ?,?)$ 型 であることがわかる
- $\textcolor{blue}{1!}$ が $\textcolor{red}{0}$ 個
- $(3, 1, 4, ?,?)$ 型 の中で,$\textcolor{blue}{1!}$ を1つも作れないということは,$(3, 1, 4, 2, ?)$ 型である
- 最後に $\textcolor{red}{0}$ が余る
- $(3, 1, 4, 2, ?)$ 型 のうち,最初のもの,すなわち $(3, 1, 4, 2, 5)$ 型 が条件を満たす
という意味である.以上により,上述のアルゴリズムが正当化されるのである.
ここまで辿れば当然わかることではあるが,アルゴリズム前半の「除算を繰り返す部分」は,実は $S!$ で割る → $(S-1)!$ で割る → $(S-2)!$ で割る → … → $2!$ で割る → $1!$ で割る,という操作を効率的に行っているに過ぎない.
以上