2009-07-22 15 views
11

Si può fare qualcosa come la dichiarazione di rendimento di Python in Mathematica, al fine di creare generatori? Vedi per es. here per il concetto.Rendimento in Mathematica

Aggiornamento Ecco un esempio di ciò che intendo, a iterare su tutte le permutazioni, utilizzando solo O (n) Spazio: (Algoritmo come nel libro algoritmi di Sedgewick):

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]; 
    ] 

Poi chiamare lo come:

gen[Print,3], la stampa di tutti i 6 permutazioni di lunghezza 3.

+0

FWIW, il modo in cui Python fa iteratori/generatori è piuttosto insolito. Finché hai una sorta di astrazione sullo stato (chiusure, classi), puoi implementarle in qualsiasi lingua. – jrockway

+0

Ah, bello. Forse aggiungilo come risposta alla tua stessa domanda (è considerato piuttosto kosher farlo). O c'è ancora una domanda senza risposta qui? – dreeves

+0

Bene, è necessario passare in modo esplicito le proprie funzioni, mentre il Python fornisce una sorta di calcolo per te e lo inserisce nel framework. Quindi, non è proprio perfetto. Ma abbastanza buono, anzi, lo uso ora. – nes1983

risposta

5

Come ho detto in precedenza, utilizzando Compile verrà assegnato un codice più veloce. Utilizzando un algoritmo dal fxtbook, il seguente codice genera una prossima partizione ordinamento lessicografico:

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

Il codice seguente presuppone che gestiscono la versione 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 
    ]; 

Poi

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

Out[75]= True 

Questo è chiaramente migliore nelle prestazioni rispetto alla funzione originale 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} 

mezzo della macchina virtuale di Mathematica non è molto più lento:

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

Out[85]= {1.154, Null} 

Naturalmente questo non è neanche lontanamente implementazione del codice C, ma fornisce un notevole velocità-up su codice puro di livello superiore.

+0

È possibile ottenere velocità C utilizzando il trucco che stavo esponendo in questo post: http://stackoverflow.com/questions/5246330/delete-repeating- list-elementi-conservazione-order-of-apparizione/5251034 # 5251034. Se si crea un generatore per una funzione compilata 'PermutationIterator', in questo modo:' PermutationIteratorGen [f_, nextFunc_]: = Compila [{{n, _Interger}}, Modulo [{this = Range [n]}, While [this =! = {-1}, f [this]; this = nextFunc [n, this]];], RuntimeOptions -> "Speed", CompilationTarget -> "C", CompilationOptions -> {"InlineCompiledFunctions" -> True, "InlineExternalDefinitions" -> True}] ', continua .. –

+0

Continua .. Quindi, assumendo che la tua funzione * dummy * possa essere 'Compiled', ottieni un altro ordine di accelerazione della magnitudine:' fn = PermutationIteratorGen [# &, cfNextPartition ["C"]]; [9] // Calendario ». Il trucco sopra in modo efficace consente alle variabili della funzione Compiled allegata di vivere nel codice compilato e di essere modificate dalla funzione compilata dal chiamante, perché alla fine si esegue l'inlining e si ottiene una sola grande funzione Compiled, ma per noi sembra più modulare . Ma l'utilizzo di 'Sow' o di altre funzioni non controllabili peggiorerà notevolmente le prestazioni, quindi non vi è praticamente alcuna differenza tra C e MVM. –

+0

@Leonid Sì, questo è un buon punto e l'iteratore può anche essere personalizzato per eseguire alcune operazioni predeterminate, quindi rinunciare completamente alla funzione. Quello che intendevo per C-speed è che è lontano da 130 milioni di permutazioni generate al secondo delle quote fatte in fxtbook. 'fn [11]' impiega 9,86 secondi sulla mia macchina per un totale di 4 milioni di permutazioni al secondo. Uno sguardo a 'CompilePrint [fn]' è istruttivo e indicherà perché è successo. – Sasha

2

probabilmente dire la questione di essere più generale, ma l'esempio di iterare su permutatio ns come indicato sulla pagina si collega a sembra essere integrata in Mathematica:

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

Il Print non ci può essere sostituito con qualsiasi funzione.

+2

Bene, ciò che intendo per un generatore è più simile al seguente, che funziona nella memoria O (n), dove è necessario O (n!). gen [f_, n_]: = Modulo [{id = -1, val = Tabella [Null, {n}], visita}, visita [k_]: = Modulo [{t}, id ++; Se [k!= 0, val [[k]] = id]; Se [id == n, f [val]]; Do [Se [val [[t]] == Null, visita [t]], {t, 1, n}]; id--; val [[k]] = Null;]; visita [0]; Si esegue in questo modo: gen [Stampa, 3] – nes1983