Wolfram mathematica 数学产量

Wolfram mathematica 数学产量,wolfram-mathematica,Wolfram Mathematica,你能像Mathematica中Python的yield语句那样创建生成器吗?有关概念,请参见例如 更新 这里有一个例子,我的意思是,只使用O(n)space(Sedgewick的算法书中的算法)迭代所有置换: 那么就这样称呼它: gen[Print,3],打印所有6个长度为3的排列。您可能想让问题更一般,但您链接到的页面上给出的迭代排列的示例恰好内置于Mathematica中: Scan[Print, Permutations[{1, 2, 3}]] 那里的打印可以替换为任何函数。正如我前面所

你能像Mathematica中Python的
yield
语句那样创建生成器吗?有关概念,请参见例如

更新 这里有一个例子,我的意思是,只使用
O(n)
space(Sedgewick的算法书中的算法)迭代所有置换:

那么就这样称呼它:


gen[Print,3]
,打印所有6个长度为3的排列。

您可能想让问题更一般,但您链接到的页面上给出的迭代排列的示例恰好内置于Mathematica中:

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

那里的
打印
可以替换为任何函数。

正如我前面所述,使用
编译
将获得更快的代码。使用来自的算法,以下代码按字典顺序生成下一个分区:

PermutationIterator[f_, n_Integer?Positive, nextFunc_] := 
 Module[{this = Range[n]},
  While[this =!= {-1}, f[this]; this = nextFunc[n, this]];]
In[83]:= gen[dummy, 9] // Timing

Out[83]= {26.067, Null}

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

Out[84]= {1.03, Null}
以下代码假定我们运行版本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
    ];
然后

这在性能上明显优于原始的
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代码实现还差得远,但它比纯顶级代码的速度要快得多。

FWIW,Python使用迭代器/生成器的方式是相当不寻常的。只要对状态(闭包、类)有某种抽象,就可以用任何语言实现它们。啊,很好。也许可以把它作为你自己问题的答案(这样做被认为是非常合乎犹太教义的)。还是还有一个问题没有回答?好吧,你需要显式地传递你的函数,而pythonyield可以帮你解决这个问题,并使它适合框架。所以,它不是很完美。但是足够好了,事实上,我现在使用它。从书中引用的代码很好,但是用顶级Mathematica编写,它不是最有效的,对于更大的n来说运行缓慢。您可能希望在Compile中编写nextPermutation,然后循环排列。另外一个好处是,您可以在v8中将它编译成C,以提高大约10倍的性能。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]你可以使用我在这篇文章中公开的技巧来获得C速度:。如果您为编译后的函数创建一个生成器,如下所示:
PermutationIteratorGen[f_u,nextFunc]:=Compile[{n,{u Integer},Module[{this=Range[n]},而[this=!={-1},f[this];this=nextFunc[n,this]];RuntimeOptions->“Speed”,compilementtarget->“C”,compileationoptions->{“InlineCompiledFunctions”->True,“InlineExternalDefinitions”->True}]
,contd..继续..然后,假设您的伪函数可以
编译
,您会得到另一个数量级的加速:
fn=PermutationIteratorGen[#和,cfNextPartition[“C”];fn[9]//计时
。上述技巧有效地允许封闭编译函数的变量存在于编译代码中,并由调用方编译函数修改,因为最后我们进行内联,只得到一个大型编译函数,但对我们来说,它看起来更模块化。但是使用
Sow
或其他不可编译函数迭代将大大降低性能,因此C和MVM之间几乎没有区别。@Leonid是的,这是一个很好的观点,迭代器也可以自定义编写以执行一些预定的操作,从而完全放弃传递函数。我不是说C-speed的意思是,它远不是每秒生成1.3亿个置换在fxtbook中引用。
fn[11]
在我的机器上花费9.86秒,总计每秒400万次排列。查看
CompilePrint[fn]
很有启发性,并且会指出发生这种情况的原因。@Sasha谢谢,我确实应该看一下
CompilePrint
。我在其他情况下也会这样做,但在这里,我不知怎的相信不会调用主计算器,也没有检查,正如您在那里评论的那样,我提到的代码也是如此。@Sasha Strange:我真的很奇怪查看
CompilePrint[fn]
,没有找到对主计算器的调用。但是,有多个对
CopyTensor
的调用,但是我假设
CopyTensor
是链接到生成的C代码的Wolfram运行时库函数,所以它应该足够快。因此,我仍然不知道是什么原因导致了如此巨大的速度差异在你从书中引用的时间和
fn
评估给出的时间之间。我只能猜测,由于涉及大量小数组内存分配和释放,这可能是主要因素。
In[85]:= PermutationIterator[dummy, 9, 
  cfNextPartition["MVM"]] // Timing

Out[85]= {1.154, Null}