8

在 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

我怀疑这可能是不可能的,因为数字错误与乘法复合的方式。此外,该函数需要能够返回非机器浮点数才能有用。

4

3 回答 3

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) 然后这只是需要出现非机器大小的浮动的最后一步。

有关适用于正浮点数和负浮点数的解决方案,请参阅此 SO 答案。

让我们快速检查一下这个解决方案是否适用于产生非机器大小答案的浮点数。与安德鲁的(快得多)比较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}

但是对于 long PackedArrays,例如这里的测试示例,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].

于 2011-03-14T05:05:09.350 回答
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]. 如果您的结果可以达到那么大,那么相同的评论也适用于 Total:

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}
于 2011-03-14T05:30:15.617 回答
3

Simon 的方法很快,但在负值上失败。结合他对我的另一个问题的回答,这是一个处理否定的快速解决方案。谢谢,西蒙。

功能

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}
于 2011-03-15T06:53:20.407 回答