在Mathematica中,包含所有机器大小整数或浮点数的向量(或矩形数组)可以存储在打包数组中。这些对象占用的内存较少,而且有些操作速度要快得多。
RandomReal
会尽可能生成一个打包数组。可以使用Developer
函数FromPackedArray
考虑这些时间
lst = RandomReal[1, 5000000];
Total[lst] // Timing
Plus @@ lst // Timing
lst = Developer`FromPackedArray[lst];
Total[lst] // Timing
Plus @@ lst // Timing
Out[1]= {0.016, 2.50056*10^6}
Out[2]= {0.859, 2.50056*10^6}
Out[3]= {0.625, 2.50056*10^6}
Out[4]= {0.64, 2.50056*10^6}
因此,在打包数组的情况下,Total
比Plus @@
快许多倍,但对于非打包数组大约相同。请注意,Plus @@
在打包数组上实际上要慢一些。
现在考虑
lst = RandomReal[100, 5000000];
Times @@ lst // Timing
lst = Developer`FromPackedArray[lst];
Times @@ lst // Timing
Out[1]= {0.875, 5.8324791357*10^7828854}
Out[1]= {0.625, 5.8324791357*10^7828854}
最后,我的问题是:Mathematica中有一个快速方法用于打包数组的列表产品,类似于Total
吗?
我怀疑这可能是不可能的,因为数值误差与乘法复合的方式。此外,该函数需要能够返回非机器浮动才有用。
答案 0 :(得分:9)
我也想知道是否存在与Total
相乘的乘法。
真的不是那么糟糕的解决方案
In[1]:= lst=RandomReal[2,5000000];
Times@@lst//Timing
Exp[Total[Log[lst]]]//Timing
Out[2]= {2.54,4.370467929041*10^-666614}
Out[3]= {0.47,4.370467940*10^-666614}
只要数字是正数而且不是太大或太小,
那么舍入误差也不算太差。
关于在评估期间可能发生的事情的猜测是:(1)如果数字是正浮点数,则Log
操作可以快速应用于打包数组。 (2)然后可以使用Total
的压缩数组方法快速添加数字。 (3)然后,这只是需要出现非机器尺寸浮子的最后一步。
请参阅this SO answer了解适用于正浮动和负浮动的解决方案。
让我们快速检查一下此解决方案是否适用于产生非机器大小答案的浮点数。与安德鲁(更快)compiledListProduct
比较:
In[10]:= compiledListProduct =
Compile[{{l, _Real, 1}},
Module[{tot = 1.}, Do[tot *= x, {x, l}]; tot],
CompilationTarget -> "C"]
In[11]:= lst=RandomReal[{0.05,.10},15000000];
Times@@lst//Timing
Exp[Total[Log[lst]]]//Timing
compiledListProduct[lst]//Timing
Out[12]= {7.49,2.49105025389*10^-16998863}
Out[13]= {0.5,2.4910349*10^-16998863}
Out[14]= {0.07,0.}
如果选择较大的(>1
)实数,则compiledListProduct
会产生警告
CompiledFunction::cfne: Numerical error encountered; proceeding with uncompiled evaluation.
并且需要一些时间才能给出结果......
一个古玩是Sum
和Product
都可以使用任意列表。 Sum
工作正常
In[4]:= lst=RandomReal[2,5000000];
Sum[i,{i,lst}]//Timing
Total[lst]//Timing
Out[5]= {0.58,5.00039*10^6}
Out[6]= {0.02,5.00039*10^6}
但对于长PackedArray
s,例如此处的测试示例,Product
失败,因为自动编译的代码(版本8.0)不能正确捕获下溢/溢出:
In[7]:= lst=RandomReal[2,5000000];
Product[i,{i,lst}]//Timing
Times@@lst//Timing
Out[8]= {0.,Compile`AutoVar12!}
Out[9]= {2.52,1.781498881673*10^-666005}
有用的WRI技术支持提供的解决方法是使用SetSystemOptions["CompileOptions" -> {"ProductCompileLength" -> Infinity}]
关闭产品编译。另一种选择是使用lst=Developer`FromPackedArray[lst]
。
答案 1 :(得分:4)
首先,为避免混淆,请查看一个示例,其结果可以表示为硬件机器精度数字,必须全部小于
In[1]:= $MaxMachineNumber
Out[1]= 1.79769*10^308
你的Total示例已经有了这个漂亮(和快速)的属性。以下是使用机器编号的Times时间示例的变体:
In[2]:= lst = RandomReal[{0.99, 1.01}, 5000000];
Times @@ lst // Timing
Out[3]= {1.435, 1.38851*10^-38}
现在我们可以使用Compile创建一个编译函数来有效地执行此操作:
In[4]:= listproduct =
Compile[{{l, _Real, 1}},
Module[{tot = 1.}, Do[tot *= x, {x, l}]; tot]]
Out[4]= CompiledFunction[{l},Module[{tot=1.},Do[tot*=x,{x,l}];tot],-CompiledCode-]
速度更快:
In[5]:= listproduct[lst] // Timing
Out[5]= {0.141, 1.38851*10^-38}
假设你有一个C编译器和Mathematica 8,你也可以自动编译到C代码。创建临时DLL并在运行时链接回Mathematica。
In[6]:= compiledlistproduct =
Compile[{{l, _Real, 1}},
Module[{tot = 1.}, Do[tot *= x, {x, l}]; tot],
CompilationTarget -> "C"]
Out[6]= CompiledFunction[{l},Module[{tot=1.},Do[tot*=x,{x,l}];tot],-CompiledCode-]
这使得性能与内置Mathematica函数的性能差别不大:
In[7]:= compiledlistproduct[lst] // Timing
Out[7]= {0.015, 1.38851*10^-38}
请注意,如果您的产品确实超出$ MaxMachineNumber(或$MinMachineNumber),那么您最好坚持使用Apply[Times, list]
。同样的评论适用于道达尔,如果您的结果可以变得那么大:
In[11]:= lst = RandomReal[10^305, 5000000];
Plus @@ lst // Timing
Out[12]= {1.435, 2.499873364498981*10^311}
In[13]:= lst = RandomReal[10^305, 5000000];
Total[lst] // Timing
Out[14]= {1.576, 2.500061580905602*10^311}
答案 2 :(得分:3)
Simon的方法很快,但在负值上失败了。将它与his answer to my other question结合起来,这是一个处理否定的快速解决方案。谢谢,西蒙。
f = (-1)^(-1 /. Rule @@@ Tally@Sign@# /. -1 -> 0) * Exp@Total@Log@Abs@# &;
lst = RandomReal[{-50, 50}, 5000000];
Times @@ lst // Timing
f@lst // Timing
lst = Developer`FromPackedArray[lst];
Times @@ lst // Timing
f@lst // Timing
{0.844, -4.42943661963*10^6323240}
{0.062, -4.4294366*10^6323240}
{0.64, -4.42943661963*10^6323240}
{0.203, -4.4294366*10^6323240}