我正在运行一个Table功能,需要花费太多时间才能完成。
我想知道是否有办法检索到目前为止计算的结果。
答案 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
相同的迭代器语法(并且还动态本地化迭代器变量),同时是顺序(常量内存)构造。为了收集中间结果,使用了Table
和Reap
。由于Sow
可以是任何代码段,特别是也可以使用expr
,因此只需要Sow
Reap
这些Sown
个值的自定义标记通过我们的函数,但不是它执行的代码。由于Module
自然地生成具有唯一名称的(临时)符号,因此我只使用Module
生成的没有值的变量作为标记。这是一种通常有用的技术。
为了能够在用户以交互方式或在代码中发布Abort[]
的情况下收集结果,我们将Do
循环包装在CheckAbort
中。在Abort[]
(此处为{}
)上执行的代码在此方法中基本上是任意的,因为结果集合无论如何都由Sow
和Reap
完成,尽管可能是在更复杂的版本中有用,它将结果保存到用户提供的某个变量中,然后重新发出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}]]