Rustでk-permutations

最近はRustの勉強がてらAtcoderやAOJの簡単な問題を解いているが、その際に順列を生成する関数が欲しくなったので実装してみた (Atcoderでは itertools クレートを使えるので不要だが)。

実装全体はこちら: Generate k-permutations in Rust

動き方としては、

let s = "ABCD";
s.chars().permutations(2).for_each(|cs| {
    println!("{:?}", cs);
});
// Output:
// ['A', 'B']
// ['A', 'C']
// ['A', 'D']
// ['B', 'A']
// ['B', 'C']
// ['B', 'D']
// ['C', 'A']
// ['C', 'B']
// ['C', 'D']
// ['D', 'A']
// ['D', 'B']
// ['D', 'C']

のようにIteratorから permutations(k) を呼ぶことで、Iteratorからk個取り出して作る順列を (indexオーダーでの) 辞書順で順番に返すようになっている。

実装は、

struct PermutationIterator<T> {
    v: Vec<(usize, T)>, // next()内でv[..k]が次の順列となるように要素をスワップされるベクトル。(index, 要素)の2つ組
    k: usize,           // nPkのk
    finished: bool,     // 順列生成が終了したかどうかのフラグ
}

のような構造体を作り、以下のようにこの構造体に next() を実装してIteratorとしての振る舞いを定義している。

impl<T: Copy> Iterator for PermutationIterator<T> {
    type Item = Vec<T>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.finished {
            None
        } else {
            let res = self.v.iter().map(|e| e.1).take(self.k).collect::<Vec<T>>();
            self.next_k_permutation();
            Some(res)
        }
    }
}

next() を呼ぶたびにIteratorから返却されるベクトルを

let res = self.v.iter().map(|e| e.1).take(self.k).collect::<Vec<T>>();

で作ってから self.next_k_permutation()v[..k] が次の辞書順になるように変換する。 繰り返し順列を生成し、 v[..k] の辞書順が生成される順列内で最大になったときに finished = true にしてIteratorが終了するようにしている。

v[..k] を辞書順で生成する next_k_permutation()Stack Overflowの回答 を参考にして書いた。

impl<T> PermutationIterator<T> {
    fn flip(&mut self, mut lo: usize, mut hi: usize) {
        while lo + 1 < hi {
            self.v.swap(lo, hi - 1);
            lo += 1;
            hi -= 1;
        }
    }

    fn next_k_permutation(&mut self) {
        let mut tail_max = self.v[self.n() - 1].0;
        let mut tail_start = self.k;
        while tail_start > 0 && self.v[tail_start - 1].0 >= tail_max {
            tail_start -= 1;
            tail_max = self.v[tail_start].0;
        }
        if tail_start == 0 {
            self.finished = true;
        } else {
            let mut swap_in: usize;
            let pivot = self.v[tail_start - 1].0;
            if pivot >= self.v[self.n() - 1].0 {
                swap_in = tail_start;
                while swap_in + 1 < self.k && self.v[swap_in + 1].0 > pivot {
                    swap_in += 1;
                }
            } else {
                swap_in = self.n() - 1;
                while swap_in > self.k && self.v[swap_in - 1].0 > pivot {
                    swap_in -= 1;
                }
            }
            self.v.swap(tail_start - 1, swap_in);
            self.flip(self.k, self.n());
            self.flip(tail_start, self.n());
        }
    }

    fn n(&self) -> usize {
        self.v.len()
    }
}

self.vv[..k]v[k..] に分けて考え、 v[k..] が常に昇順に整列されるように要素を交換しながら v[..k] で辞書順で次のVecを表現している。動作が分かり辛いがコードを追いながら紙にv がどう変わっていくかを書いていけば分かると思う。

たとえば (0..5).permutations(2).for_each(...) としたときの PermutationIteratorv の変化を順に書くとこんな感じである↓

↓順列    ↓常に昇順になっている
v[..2] | v[2..]
0 1    | 2 3 4
0 2    | 1 3 4
0 3    | 1 2 4
0 4    | 1 2 3
1 0    | 2 3 4
1 2    | 0 3 4
1 3    | 0 2 4
1 4    | 0 2 3
2 0    | 1 3 4
2 1    | 0 3 4
2 3    | 0 1 4
2 4    | 0 1 3
3 0    | 1 2 4
3 1    | 0 2 4
3 2    | 0 1 4
3 4    | 0 1 2
4 0    | 1 2 3
4 1    | 0 2 3
4 2    | 0 1 3
4 3    | 0 1 2

あとはIteratorから .permutations() を呼べるようにすれば完成だが、これは

trait Permutations<T> {
    fn permutations(self, size: usize) -> PermutationIterator<T>;
}

impl<T, I: IntoIterator<Item = T>> Permutations<T> for I {
    fn permutations(self, k: usize) -> PermutationIterator<T> {
        PermutationIterator::new(self.into_iter(), k)
    }
}

のように permutations() のメソッドシグネチャを持つトレイトを定義し、Iteratorを実装するジェネリクスに対してそのトレイトを実装するコードを書けば実現できる。

大雑把に仕組みをまとめると、Iteratorから .permutations() を呼ぶと PermutationIterator というトレイトが返るようになっており、このトレイトはIteratorとしての振る舞いを持っていて next() を呼ぶたびに内部で next_k_permutation() が呼ばれて辞書順に順列を返す、というものである。

Iteratorから permutations のメソッドを生やす形にしたのと、IteratorのItemに対する制限がCopyトレイトの実装のみなので割と使い勝手が良さそうかな、と思っている。

参考

実装に関してこちらも参考にさせていただいた。