2009-07-22 9 views
11

ジェネレータを作成するためにMathematicaでPythonのyield文のようなことをすることはできますか?例えば、概念のためにhere。 (Sedgewickのアルゴリズム帳のようなアルゴリズム):Yield in Mathematica

更新 はここだけO(n)のスペースを使用して、すべての順列を反復処理するために、私が何を意味するかの例ですその後

gen[f_, n_] := Module[{id = -1, val = Table[Null, {n}], visit}, 
    visit[k_] := Module[{t}, 
    id++; If[k != 0, val[[k]] = id]; 
    If[id == n, f[val]]; 
    Do[If[val[[t]] == Null, visit[t]], {t, 1, n}]; 
    id--; val[[k]] = Null;]; 
    visit[0]; 
    ] 

それそれを呼び出します以下のような:

gen[Print,3]、あなたはおそらく、より一般的なことを質問を意味するが、permutatioを反復処理の一例長さ3

+0

FWIW、Pythonのイテレータ/ジェネレータのやり方は、まれです。あなたが状態(クロージャ、クラス)を超えて何らかの抽象化を持っている限り、それらを任意の言語で実装することができます。 – jrockway

+0

あ、いいね。たぶんあなた自身の質問への答えとしてそれを追加してください(それを行うにはかなり正直な考えです)。それともまだここに答えられていない質問がありますか? – dreeves

+0

さて、あなたは関数を明示的に渡す必要がありますが、Pythonの収穫物はあなたのためにそれを理解し、フレームワークに適合させます。だから、それは完全ではありません。しかし、十分に良い、確かに、私は今それを使用します。 – nes1983

答えて

5

前述したように、Compileを使用すると、より高速なコードが与えられます。 fxtbookからアルゴリズムを使用して、次のコードは、辞書式順序で次のパーティションを生成します。

PermutationIterator[f_, n_Integer?Positive, nextFunc_] := 
Module[{this = Range[n]}, 
    While[this =!= {-1}, f[this]; this = nextFunc[n, this]];] 

次のコードは、我々はバージョン8を実行する前提としています

ClearAll[cfNextPartition]; 
cfNextPartition[target : "MVM" | "C"] := 
    cfNextPartition[target] = 
    Compile[{{n, _Integer}, {this, _Integer, 1}}, 
    Module[{i = n, j = n, ni, next = this, r, s}, 
    While[Part[next, --i] > Part[next, i + 1], 
     If[i == 1, i = 0; Break[]]]; 
    If[i == 0, {-1}, ni = Part[next, i]; 
     While[ni > Part[next, j], --j]; 
     next[[i]] = Part[next, j]; next[[j]] = ni; 
     r = n; s = i + 1; 
     While[r > s, ni = Part[next, r]; next[[r]] = Part[next, s]; 
     next[[s]] = ni; --r; ++s]; 
     next 
     ]], RuntimeOptions -> "Speed", CompilationTarget -> target 
    ]; 

その後

In[75]:= Reap[PermutationIterator[Sow, 4, cfNextPartition["C"]]][[2, 
    1]] === Permutations[Range[4]] 

Out[75]= True 
を は

これはさ明らかに元のgen機能よりもパフォーマンスが優れています。

In[83]:= gen[dummy, 9] // Timing 

Out[83]= {26.067, Null} 

In[84]:= PermutationIterator[dummy, 9, cfNextPartition["C"]] // Timing 

Out[84]= {1.03, Null} 

Mathematicaの仮想マシンを使用すると、はるかに遅いではありません。もちろん

In[85]:= PermutationIterator[dummy, 9, 
    cfNextPartition["MVM"]] // Timing 

Out[85]= {1.154, Null} 

これはCコードの実装の近くにどこにもない、まだ純粋なトップレベルのコードに比べて大幅スピードアップを提供します。

+0

あなたは、私がこの記事では、露光されたトリックを使用してCの速度を得ることができます。http://stackoverflow.com/questions/5246330/delete-repeating-リスト要素 - 保持出現順序/ 5251034#5251034。 'PermutationIteratorGen [f_、nextFunc_]:= [{{n、_Integer}}、 モジュール[{this = Range [n]}、 をコンパイルするときに、コンパイルされた関数' PermutationIterator'のジェネレータを作成すると、 [this =!= {-1}、f [this];コンパイルオプション - > {"InlineCompiledFunctions" - > True、 "InlineExternalDefinitions" - > True}] '、"コンパイルオプション - > {"InlineCompiledFunctions" - > True、 、 contd .. –

+0

続いて、あなたの* dummy *関数が 'Compiled'であると仮定すると、もう一つの倍のスピードアップが得られます:' fn = PermutationIteratorGen [#&、cfNextPartition ["C"]]; fn [9] //タイミング。上記のトリックは効果的にコンパイルされたコンパイルされた関数の変数をコンパイルされたコードに入れて呼び出し側のコンパイルされた関数で変更できるようにします。なぜなら、最終的にはインライン化を行いコンパイルされた関数を1つだけ取得するからです。 。しかし、 'Sow'などのコンパイル不能な関数を使用すると、パフォーマンスが大幅に低下するため、CとMVMの違いはほとんどありません。 –

+0

@Leonidはい、これは良い点です。また、イテレータは、あらかじめ決められた操作を実行するためにカスタム記述することもできます。私がCスピードではないことが意味することは、fxtbookで作成された1秒当たりの見積もりが130百万個の順列からずっと離れていることです。 'fn [11]'は私のマシンで9.86秒かかり、毎秒4百万の順列になります。 'CompilePrint [fn]'を参考にして、なぜそれが起こったのかを示します。 – Sasha

2

のすべての6つの順列を印刷あなたがリンクページに与えられたとして、NSにMathematicaにして構築することが行われます

Scan[Print, Permutations[{1, 2, 3}]] 

Printは任意の関数であり置き換えることができます。

+2

さて、ジェネレータが意味することは、O(n!)が必要なO(n)メモリで動作する次のようなものです。 gen [f_、n_]:=モジュール[{id = -1、val =テーブル[Null、{n}]、訪問} [k_]:=モジュール[{t}、 id ++; [k!= 0、val [[k]] = id]; [id == n、f [val]]; [val [[t]] == Nullの場合、[t]]、{t、1、n}]にアクセスしてください。 id-; val [[k]] = Null;]; 訪問[0]; ] あなたはこのようにそれを実行します。 GEN [印刷]、[3] – nes1983