[解決済み] 並べ換えの遅延生成
質問
Clojureで遅延リストを作成するような方法で、セットの並べ換えを生成するアルゴリズムを探しています。つまり、並べ換えのリストに対して反復処理を行い、各並べ換えは私が要求するまで計算されず、すべての並べ換えが一度にメモリに格納される必要はありません。
代わりに、私はある集合が与えられると、その集合の "next"順列を返すようなアルゴリズムを探しています。それは、それ自身の出力で繰り返し関数を呼び出すと、ある順序で元の集合のすべての順列を循環するような方法です(順序は重要ではありません)。
そのようなアルゴリズムはあるのでしょうか? 私が見た順列生成アルゴリズムのほとんどは、一度に(通常は再帰的に)それらを生成する傾向があり、これは非常に大きな集合にスケールしません。 Clojure(または他の関数型言語)での実装は有用でしょうが、私は擬似コードからそれを見つけ出すことができます。
どのように解決するのですか?
はい、そこで
です。
というアルゴリズムがあり、それも非常に単純です。C++の標準テンプレートライブラリ(STL)には
next_permutation
.
このアルゴリズムでは、実際に の次に の順列、つまり辞書的に次の順列を見つけます。例えば、32541 のようなシーケンスが与えられたとします。次の並べ換えは何でしょうか?
よく考えてみると、それは "34125" であることがわかると思います。そして、あなたの考えは、おそらく次のようなものだったでしょう。で、"32541"です。
- は、"32" を固定したまま、"541" の部分で後の順列を見つける方法はありません。なぜならその順列はすでに 5、4、および 1 の最後のもので、それは降順にソートされています。
- したがって、"2" をより大きなものに--実際には、"541" の部分でそれより大きい最小の数、すなわち 4 に--変更する必要があります。
- さて、順列が "34" として始まると決めたら、残りの数字は増加する順序になるはずなので、答えは "34125" となります。
アルゴリズムは、まさにその推論のラインを実装することです。
- 降順に並べられた最も長い "tail" を見つけなさい。("541" の部分です)。
- 末尾の直前の数("2"の部分)を末尾のそれよりも大きい最小の数(4)に変えなさい。
- tail を昇順にソートします。
(1.)は、前の要素が現在の要素より小さくない限り、最後から始めて逆行することで効率的に行えます。(2.)は、"4" と '2" を入れ替えるだけで、"34521" とすることができます。一度これを行えば、(3.)のソートアルゴリズムを使用せずに済みます。なぜなら、テールは昔も今も(これについて考えてみてください)降順でソートされているので、それを逆にすればよいだけだからです。
C++のコードはまさにこれを行います(ソースを見てください。
/usr/include/c++/4.0.0/bits/stl_algo.h
のソースを見るか、あるいは
この記事
を参照); それをあなたの言語に翻訳するのは簡単なはずです。[C++のイテレータに馴染みがなければ、 "BidirectionalIterator" を "pointer" と読めばよいでしょう。このコードでは
false
を返します。これは次の順列がない場合、つまり、すでに降順になっている場合です]。
template <class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first,
BidirectionalIterator last) {
if (first == last) return false;
BidirectionalIterator i = first;
++i;
if (i == last) return false;
i = last;
--i;
for(;;) {
BidirectionalIterator ii = i--;
if (*i <*ii) {
BidirectionalIterator j = last;
while (!(*i <*--j));
iter_swap(i, j);
reverse(ii, last);
return true;
}
if (i == first) {
reverse(first, last);
return false;
}
}
}
順列ごとに O(n) の時間がかかるように見えるかもしれませんが、もっと注意深く考えると、すべての順列の合計で O(n!) の時間がかかるので、順列ごとに O(1) -- 定数時間 -- だけであることが証明できます。
良いことに、このアルゴリズムは要素が繰り返されるシーケンスでも動作します。たとえば "232254421" では、末尾を "54421" として見つけ、 "2" と "4" を交換し ("232454221") 残りを逆にして "232412245" とし、それが次の順列となる、ということです。
関連
-
[解決済み】Dijkstraのアルゴリズムが負の重みのエッジに対して機能しないのはなぜですか?
-
[解決済み] アルゴリズムの教科書では、ソートされた配列について「増加」ではなく「非減少」を使っているのはなぜですか?
-
[解決済み] ラジアンを度数に変換する方法は?
-
[解決済み] 再帰性 T(n) = T(n^(1/2)) + 1
-
[解決済み] リストのすべての並べ換えを生成するには?
-
[解決済み】与えられた文字列のすべての並べ換えを生成する
-
[解決済み] リストの並べ換えをすべて生成するアルゴリズム?
-
[解決済み] 並べ換え→数→並べ換えの高速マッピングアルゴリズム
-
[解決済み] エラトステネスの篩アルゴリズムの時間複雑性
-
[解決済み] 学校の時間割を作成するアルゴリズム
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] アルゴリズム設計マニュアル』の解答はどこにあるのですか?[終了しました]
-
[解決済み] どのようにすれば、ほとんどすべてのアルゴリズムを修正して、最良の場合の実行時間を持つようにできるか?
-
[解決済み] DPLLアルゴリズムはどのように動作しますか?[クローズド]
-
[解決済み] 2つのNFAの交点の求め方
-
[解決済み] k-meansの時間計算量はどの程度ですか?
-
[解決済み] ヒープ構築のトップダウン・アプローチはボトムアップよりも成長度合いがO(n)よりもO(log n)低いにもかかわらず、なぜ効率が悪いのでしょうか?
-
[解決済み] 無限リストでのfoldlとfoldrの動作
-
[解決済み] クイックソートとマージソートの比較 [重複]。
-
[解決済み] 旧経度+nmから新経度、新緯度を算出する。
-
[解決済み] ビット単位で、モジュラス演算子の代わりに使用