非再帰的順列生成アルゴリズム

以下に提案する非再帰アルゴリズムは、リプスキーの本[1]で説明されているものとは多少異なり、インターネットのロシア語セグメントで私が発見しました。 面白いものになることを願っています。



問題の簡単な説明。 次元Nのセットがあります。すべてのNを取得する必要があります。 可能な順列。

さらに、簡単にするために、整数(1..N)をセットとして使用します。 数字の代わりに、任意のオブジェクトを使用できます。 アルゴリズムのセットの要素を比較する操作はありません。

中間データを保存するには、次の形式のデータ構造を作成します。

type dtree ukaz as integer '      spisok() as integer '    end type
      
      





そして、初期値でそれを埋めます

  Dim masiv(N-) As dtree '   = N-1 For ii = 1 To N - 1 masiv(ii).ukaz = 1 ReDim masiv(ii).spisok(N + 1 - ii) '    For kk = 1 To (N + 1 - ii) masiv(ii).spisok(kk) = kk + ii - 1 Next Next
      
      





masiv配列の要素番号はレベルと呼ばれます。

最初のレベルのリストに、セットのすべての要素を入力します。 最初のレベルでは、リストの次元はNであり、リスト自体はアルゴリズムの実行中に変更されません。 最初の入力時に、配列内のすべてのポインターがリストの最初の要素に設定されます。

次の各レベルで、そのリストは前のレベルのリストに基づいて形成されますが、ポインターでマークされた1つの要素はありません。 最後から2番目のレベル(N-2)では、リストには3つの項目が含まれています。 最後のレベル(N-1)では、リストには2つのアイテムが含まれています。 下位レベルのリストは、前のレベルのポインターで示される要素を持たない前のレベルのリストとして形成されます。

最初の充填の結果、最初の2つの順列が取得されましたこれは、ポインターが指すリスト項目から上位レベル(1 ...(N-2))で形成された一般的な配列です。

 For ii = 1 To N-2 massiv(ii).spisok(ukaz) Next
      
      





最後のレベルのリストから、異なる順序の2つの要素ペア(2つのテール1 2および2 1)

 + massiv(N-1).spisok(1) + massiv(N-1).spisok(2) + massiv(N-1).spisok(2) + massiv(N-1).spisok(1)
      
      





常に最後から2番目のレベル(N-2)から、さらにすべての順列も形成されます。

後続の順列を取得する手順は、最後から2番目のレベル(N-2)にあり、2つの順列を形成しているため、選択した要素のポインターを1増やします。

可能であれば、最後のレベルでリストを変更して繰り返します。

最後から2番目のレベルでポインターを増やすことができない場合(可能なオプションはすべて列挙されている)、ポインターを増やす(右に移動する)ことができるレベルまで上昇します。 アルゴリズムの終了条件-最初のレベルのポインターがNを超えています。

ポインターを右に移動した後、その下のリストを変更して最後から2番目のレベル(N-2)に移動し、リストを更新して、選択したアイテムのポインターを1に設定します。

アルゴリズムの動作は、以下の図でより明確に明確に示されています(集合N = 5の次元)。 図の番号は、説明のレベルに対応しています。 図に加えて、アルゴリズムを理解するために何も必要とされない可能性さえあります。



もちろん、アルゴリズムを実装するとき、通常の2次元配列を使用できます。特に、小さなNの場合、メモリのゲインは何も与えず、大きなNの場合、アルゴリズムが機能するのを待つことができません。

VBAにアルゴリズムを実装する1つの方法を以下に示します。 これを実行するには、マクロを含むExcelブックを作成し、VB開発者タブでモジュールを作成し、テキストをモジュールにコピーします。 generate()を実行すると、すべての順列がSheet1に表示されます。



Excel用VBA


 Option Explicit Type dtree tek_elem_ukaz As Integer spisok() As Integer End Type Dim masiv() As dtree Dim start_print As Integer Dim N As Integer Sub generate() Dim ii As Integer, kk As Integer, jj As Integer Dim uroven As Integer 1.Cells.Clear N = 5 start_print = 1 ReDim masiv(N - 1) '   For ii = 1 To N - 1 masiv(ii).tek_elem_ukaz = 1 ReDim masiv(ii).spisok(N + 1 - ii) For kk = 1 To (N + 1 - ii) masiv(ii).spisok(kk) = kk + ii - 1 Next Next uroven = N - 2 Do '  Call print_rezult(uroven) '       If masiv(uroven).tek_elem_ukaz <= (N - uroven) Then '    '    masiv(uroven).tek_elem_ukaz = masiv(uroven).tek_elem_ukaz + 1 '    Call zap_niz(uroven) Else '      ,     Do While uroven > 1 And masiv(uroven).tek_elem_ukaz > (N - uroven) uroven = uroven - 1 Loop If uroven = 1 And masiv(1).tek_elem_ukaz = N Then MsgBox "stop calc" Exit Sub '   End If '         masiv(uroven).tek_elem_ukaz = masiv(uroven).tek_elem_ukaz + 1 Call zap_niz(uroven) '    Do While uroven < N - 2 uroven = uroven + 1 masiv(uroven + 1).tek_elem_ukaz = 1 '    For kk = 2 To N - uroven + 1 masiv(uroven + 1).spisok(kk - 1) = masiv(uroven).spisok(kk) Next Loop End If Loop End Sub Sub print_rezult(ukaz As Integer) Dim ii As Integer For ii = 1 To ukaz With masiv(ii) 1.Cells(start_print, ii) = .spisok(.tek_elem_ukaz) 1.Cells(start_print + 1, ii) = .spisok(.tek_elem_ukaz) End With Next With masiv(ukaz + 1) 1.Cells(start_print, ukaz + 1) = .spisok(1) 1.Cells(start_print, ukaz + 2) = .spisok(2) start_print = start_print + 1 1.Cells(start_print, ukaz + 1) = .spisok(2) 1.Cells(start_print, ukaz + 2) = .spisok(1) start_print = start_print + 1 End With End Sub Sub zap_niz(ukaz As Integer) '    Dim ii As Integer, wsp1 As Integer '    wsp1 = masiv(ukaz).tek_elem_ukaz masiv(ukaz + 1).tek_elem_ukaz = 1 '    For ii = 1 To wsp1 - 1 masiv(ukaz + 1).spisok(ii) = masiv(ukaz).spisok(ii) Next For ii = wsp1 + 1 To N - ukaz + 1 masiv(ukaz + 1).spisok(ii - 1) = masiv(ukaz).spisok(ii) Next End Sub
      
      







参照:

[1] V.リプスキー。 プログラマーのための組み合わせ論。 -モスクワ、ミール出版社、1988年。



All Articles