Mathematica表功能

时间:2011-06-24 16:08:42

标签: wolfram-mathematica

我正在运行一个Table功能,需要花费太多时间才能完成。

我想知道是否有办法检索到目前为止计算的结果。

3 个答案:

答案 0 :(得分:19)

建议的解决方案

这是Table的{​​{1}}版本 - 能够并且将保留到目前为止收集的中间结果。它是发布here的解决方案的修改版本。

Abort

它应该能够采用与ClearAll[abortableTable]; SetAttributes[abortableTable, HoldAll]; abortableTable[expr_, iter__List] := Module[{indices, indexedRes, sowTag}, SetDelayed @@ Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@ Hold[iter]], Hold], indices]; indexedRes = If[# === {}, #, First@#] &@Last@Reap[ CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], {}], sowTag]; AbortProtect[ Map[First, SplitBy[indexedRes, Table[ With[{i = i}, Function[Slot[1][[2, i]]]], {i, Length[Hold[iter]] - 1}]], {-3}]]]; 相同的迭代器规范。

如何运作

以下是它的工作原理。第一个语句(Table)“解析”迭代器,假设它们都是SetDelayed @@...形式,并将迭代器变量列表分配给变量{iteratorSymbol_,bounds__}。需要使用indices构造以防止对迭代器变量进行可能的评估。有很多方法可以做到这一点,我只使用其中一种方法。以下是它的工作原理:

Hold

然后使用In[44]:= {i, j, k} = {1, 2, 3}; Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@ Hold[{i, 1, 10}, {j, 1, 5}, {k, 1, 3}]], Hold], indices] Out[45]= Hold[indices, {i, j, k}] 会自然地生成SetDelayed @@ the-above形式的延迟定义。我将值分配给索引indices:={i,j,k},以证明在使用此构造时不会对它们进行不必要的评估。

下一个语句生成一个收集结果列表,其中每个结果都分组在一个列表中,其中包含用于生成它的索引列表。由于i,j,k变量由延迟定义定义,因此对于新的索引组合,它将每次重新评估。这里使用的另一个关键特性是indices循环接受与Do相同的迭代器语法(并且还动态本地化迭代器变量),同时是顺序(常量内存)构造。为了收集中间结果,使用了TableReap。由于Sow可以是任何代码段,特别是也可以使用expr,因此只需要Sow Reap这些Sown个值的自定义标记通过我们的函数,但不是它执行的代码。由于Module自然地生成具有唯一名称的(临时)符号,因此我只使用Module生成的没有值的变量作为标记。这是一种通常有用的技术。

为了能够在用户以交互方式或在代码中发布Abort[]的情况下收集结果,我们将Do循环包装在CheckAbort中。在Abort[](此处为{})上执行的代码在此方法中基本上是任意的,因为结果集合无论如何都由SowReap完成,尽管可能是在更复杂的版本中有用,它将结果保存到用户提供的某个变量中,然后重新发出Abort[](当前未实现的功能)。

结果,我们进入变量indexedRes一个表单

的平面列表
{{expr1, {ind11,ind21,...indn1}},...,{exprk, {ind1k,ind2k,...indnk}}

其中结果与相应的索引组合分组。我们需要这些索引组合来从平面列表重建多维结果列表。这样做的方法是根据i - 索引的值重复拆分列表。函数SplitBy具有此功能,但我们需要提供用于拆分步骤的函数列表。由于子列表i中的{expr,{ind1,...,indn}} - 迭代器索引的索引为2,i,因此在i - 步骤进行拆分的函数为#[[2, i]]&,我们需要动态构建这些函数的列表,以将其提供给SplitBy。这是一个例子:

In[46]:= Table[With[{i = i}, Function[Slot[1][[2, i]]]], {i, 5}]

Out[46]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[2, 5]] &}

With[{i=i},body]构造用于在纯函数中注入i的特定值。将i的值注入Function的替代方法确实存在,例如:

In[75]:= 
Function[Slot[1][[2, i]]] /. Map[List, Thread[HoldPattern[i] -> Range[5]]]

Out[75]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[2, 5]] &}

In[80]:= Block[{Part}, Function /@ Thread[Slot[1][[2, Range[5]]]]]

Out[80]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[ 2, 5]] &}

In[86]:= Replace[Table[{2, i}, {i, 5}], {inds__} :> (#[[inds]] &), 1]

Out[86]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[ 2, 5]] &}

但可能更加模糊(可能除了最后一个)。

生成的嵌套列表具有适当的结构,子列表{expr,{ind1,...,indn}}处于级别-3(从底部开始的第三级)。通过使用Map[First,lst,{-3}],我们删除了索引组合,因为嵌套列表已经被重建并且不再需要它们。剩下的是我们的结果 - 结果表达式的嵌套列表,其结构对应于Table生成的类似嵌套列表的结构。最后一个语句包含在AbortProtect中 - 以防万一,以确保在可能的Abort[]触发之前返回结果。

使用示例

以下是评估命令后不久我按下Alt+.Abort[])的示例:

In[133]:= abortableTable[N[(1+1/i)^i],{i,20000}]//Short
Out[133]//Short= {2.,2.25,2.37037,2.44141,<<6496>>,2.71807,2.71807,2.71807}

几乎与Table

一样快
In[132]:= abortableTable[N[(1+1/i)^i,20],{i,10000}]//Short//Timing
Out[132]= {1.515,{2.0000000000000000000,2.2500000000000000000,<<9997>>,2.7181459268252248640}}

In[131]:= Table[N[(1+1/i)^i,20],{i,10000}]//Short//Timing
Out[131]= {1.5,{2.0000000000000000000,2.2500000000000000000,<<9997>>,2.7181459268252248640}}

Table执行时不会自动编译:

In[134]:= Table[N[(1+1/i)^i],{i,10000}]//Short//Timing
Out[134]= {0.,{2.,2.25,2.37037,2.44141,<<9993>>,2.71815,2.71815,2.71815}}

可以编写自动编译代码并将其添加到上述解决方案中,我只是没有这样做,因为要做正确的工作会很多。

修改

我重写了这个函数,使一些部分更简洁,更容易理解。也, 在大型列表中,它比第一个版本快25%左右。

ClearAll[abortableTableAlt];
SetAttributes[abortableTableAlt, HoldAll];
abortableTableAlt[expr_, iter : {_Symbol, __} ..] :=
  Module[{indices, indexedRes, sowTag, depth =  Length[Hold[iter]] - 1},
   Hold[iter] /. {sym_Symbol, __} :> sym /. Hold[syms__] :> (indices := {syms});
   indexedRes =  Replace[#, {x_} :> x] &@ Last@Reap[
      CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], Null],sowTag];
   AbortProtect[
      SplitBy[indexedRes, Array[Function[x, #[[2, x]] &], {depth}]][[##,1]] & @@ 
      Table[All, {depth + 1}]
   ]];

答案 1 :(得分:6)

不幸的是没有。如果你想做lst=Table[f[i],{i,1,10000}]这样的事情,那么如果中止你仍然有结果,你可以做

Clear[lst2];
lst2 = {};
(Do[lst2 = {lst2, f[i]}, {i, 1, 10000}];
lst2=Flatten[lst2];) // Timing

对于未定义的f,在我的机器上占用0.173066s,而lst = Table[f[i], {i, 1, 100000}]占用大约0.06s(即Table,它的速度是不可中断的3倍)。

请注意,显而易见的“可中断”解决方案lst = {}; Do[AppendTo[lst, f[i]], {i, 1, 100000}]需要大约40秒,所以不要这样做:使用链接列表并在最后展平,就像在我的第一个示例中一样(但是,如果{ {1}}返回一个列表,然后需要更加小心。)

答案 2 :(得分:3)

另一种解决方案是将中间计算的结果导出到正在运行的日志文件,如this answer by WReach中所述(请参阅“文件支持的内存中方法”部分)。有了这个,您将获得更新的中间计算结果,并且始终能够调查到目前为止计算的内容。

P.S。我认为在this最近的 Mathematica 提示中建议使用Monitor在这种情况下也很有用:

Monitor[Table[Integrate[1/(x^n + 1), x], {n, 20}], 
 ProgressIndicator[n, {1, 20}]]