在Mathematica中使用数组和表函数.哪个是最好的

Phi*_*hil 23 arrays wolfram-mathematica

我在mathematica中主要是一个Table函数用户.但是我注意到在几个使用Array而不是Table来表示相同结果的例子中,它运行速度明显更快,尤其是随着表的维度变大.

所以我的问题是:当执行速度是主要问题时,何时最适合使用表?

是什么解释了这种差异

我的猜测是因为Arrays假设列表中的项目之间存在功能关系,它会更有效地存储它们,因此使用更少的内存,从而便于存储和后续处理?

它是怎么回事?

Sas*_*sha 12

Array没有性能优势Table.它们之间存在差异,这使得一个优先于另一个.


编辑有几个人注意到Table多维阵列的速度较慢.所有这些都使用变量来保存表大小.Table具有HoldAll属性并且仅自动评估最外层的交互绑定.因为内部迭代器仍未被评估,所以表的元素无法编译.With在自动编译中使用显式数字或结果:

In[2]:= With[{b = 10^4, c = 10^4},
 {Timing@(#[[1, 1]] &[ar = Array[(# + #2) &, {b, c}]]) , 
  Timing@(#[[1, 1]] &[ta = Table[(i + j), {i, b}, {j, c}]])}
 ]

Out[2]= {{4.93, 2}, {4.742, 2}}

In[3]:= Attributes[Table]

Out[3]= {HoldAll, Protected}
Run Code Online (Sandbox Code Playgroud)


Array让你丝毫不亚于构建函数值的数组Table.他们采取不同的论点.Array采取功能:

In[34]:= Array[Function[{i, j}, a[i, j]], {3, 3}]

Out[34]= {{a[1, 1], a[1, 2], a[1, 3]}, {a[2, 1], a[2, 2], 
  a[2, 3]}, {a[3, 1], a[3, 2], a[3, 3]}}
Run Code Online (Sandbox Code Playgroud)

而表采用明确的形式:

In[35]:= Table[a[i, j], {i, 3}, {j, 3}]

Out[35]= {{a[1, 1], a[1, 2], a[1, 3]}, {a[2, 1], a[2, 2], 
  a[2, 3]}, {a[3, 1], a[3, 2], a[3, 3]}}
Run Code Online (Sandbox Code Playgroud)

Array只能遍历常规数组,同时Table可以对列表进行任意迭代:

In[36]:= Table[a[i, j], {i, {2, 3, 5, 7, 11}}, {j, {13, 17, 19}}]

Out[36]= {{a[2, 13], a[2, 17], a[2, 19]}, {a[3, 13], a[3, 17], 
  a[3, 19]}, {a[5, 13], a[5, 17], a[5, 19]}, {a[7, 13], a[7, 17], 
  a[7, 19]}, {a[11, 13], a[11, 17], a[11, 19]}}
Run Code Online (Sandbox Code Playgroud)

有时Array可以更简洁.比较乘法表:

In[37]:= Array[Times, {5, 5}]

Out[37]= {{1, 2, 3, 4, 5}, {2, 4, 6, 8, 10}, {3, 6, 9, 12, 15}, {4, 8,
   12, 16, 20}, {5, 10, 15, 20, 25}}
Run Code Online (Sandbox Code Playgroud)

In[38]:= Table[i j, {i, 5}, {j, 5}]

Out[38]= {{1, 2, 3, 4, 5}, {2, 4, 6, 8, 10}, {3, 6, 9, 12, 15}, {4, 8,
   12, 16, 20}, {5, 10, 15, 20, 25}}
Run Code Online (Sandbox Code Playgroud)

Array 允许一个人用任何头部构建表达式,而不仅仅是列表:

In[39]:= Array[a, {3, 3}, {1, 1}, h]

Out[39]= h[h[a[1, 1], a[1, 2], a[1, 3]], h[a[2, 1], a[2, 2], a[2, 3]],
  h[a[3, 1], a[3, 2], a[3, 3]]]
Run Code Online (Sandbox Code Playgroud)

默认情况下,h选择头部会List导致创建常规数组.表没有这种灵活性.

  • 有些情况下`Array`明显更快,特别是对于多维数组.例如,考虑`b = 1000; c = 1000; 时间@(#[[1,1]]&[ar =数组[(#+#2)&,{b,c}]])时间@(#[[1,1]]&[ta =表[ (i + j),{i,b},{j,c}]])`.所以速度等价的陈述似乎只适用于线性数组.也许,对于多维数组`Table`不能完全利用自动编译(也许不能确保数组确实是矩形的,因而无法打包) - 我没有其他解释为上述一个数量级的性能差异例. (4认同)
  • @Phil我也扩大了答案.当您在较少量的评估中获得结果时,性能会更好.请参见http://stackoverflow.com/questions/4721171/performance-tuning-in-mathematica (2认同)
  • @Leonid在你的例子中,`Array`生成的数组是打包数组,而`Table`则不是:``Developer`PackedArrayQ/@ {ar,ta}``给`{True,False}`. (2认同)
  • @Alexey这就是重点.`Array`经常可以打包,因为已知的结果数组是矩形的.对于`Table`,这并不总是正确的,因为迭代器可能依赖于其他迭代器.另见我对@Sjoerd关于`ConstantArray`的问题的评论. (2认同)

tom*_*omd 9

迈克尔·特洛特在编程(第707 - 710)地址之间的差异问题ArrayTable并认为作为Table具有属性HoldAll就计算它的每一个调用的参数,而Array"尽可能"才刚刚开始计算它的参数.这可能会导致行为和速度的差异.

Attributes[Table]
Run Code Online (Sandbox Code Playgroud)

{HoldAll,Protected}

Attributes[Array]
Run Code Online (Sandbox Code Playgroud)

{}保护

Michael Trott使用以下示例来说明速度和行为的差异.我从他的书(光盘)中逐字逐句地接受它们.我希望我这样做不违反任何规则.

Remove[a, i, j];
a = 0;
Table[a = a + 1; ToExpression[StringJoin["a" <> ToString[a]]][i, j],
       {i, 3}, {j, 3}]
Run Code Online (Sandbox Code Playgroud)

{{a1 [1,1],a2 [1,2],a3 [1,3]},{a4 [2,1],a5 [2,2],a6 [2,3]},{a7 [ 3,1],a8 [3,2],a9 [3,3]}}

a = 0;
Array[a = a + 1; 
 ToExpression[StringJoin["a" <> ToString[a]]], {3, 3}] 
Run Code Online (Sandbox Code Playgroud)

{{a1 [1,1],a1 [1,2],a1 [1,3]},{a1 [2,1],a1 [2,2],a1 [2,3]},{a1 [ 3,1],a1 [3,2],a1 [3,3]}}

(注意行为上的差异)

为了说明预计算第一个参数的效果,他使用了以下示例(再次逐字,第709页).

o[a = 0;
  Table[a = a + 1; 
   ToExpression[StringJoin["a" <> ToString[a]]][i, j],
         {i, 3}, {j, 3}], {2000}] // Timing
Do[a = 0;
  Array[a = a + 1; ToExpression[ StringJoin["a" <> ToString[a]]], 
                                            {3, 3}], {2000}] // Timing
Run Code Online (Sandbox Code Playgroud)

{0.700173,Null}

{0.102587,Null}

(我在Mac上使用mma7.我的编程副本使用v5.1.可能有更新)

当然,这不是编程中讨论ArrayTable讨论的唯一区别.

我看到其他答案,我有兴趣了解其他人对这一点的看法.


Mr.*_*ard 6

一个原因Array可能更快,因为它经常更好地编译其第一个参数.

在Mathematica 7中:

In[1]:= SystemOptions[CompileOptions -> ArrayCompileLength]

Out[1]= {"CompileOptions" -> {"ArrayCompileLength" -> 250}}
Run Code Online (Sandbox Code Playgroud)

In[2]:= SystemOptions[CompileOptions -> TableCompileLength]

Out[2]= {"CompileOptions" -> {"TableCompileLength" -> 250}}
Run Code Online (Sandbox Code Playgroud)

所以可以推断出Array并且Table应该在同一点编译.

但是,让我们试一试.我将利用Timo的timeAvg功能:

n = 15;
Array[Mod[#^2, 5]*(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]*(1 + j), {i, n}, {j, n}] // timeAvg

(* Out = 0.00034496 *)

(* Out = 0.00030016 *)

n = 16;
Array[Mod[#^2, 5]*(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]*(1 + j), {i, n}, {j, n}] // timeAvg

(* Out = 0.000060032 *)

(* Out = 0.0005008   *)
Run Code Online (Sandbox Code Playgroud)

我们看到的是Array能够编译Mod[#^2, 5]*(1 + #2) &Table无法编译Mod[i^2, 5]*(1 + j),因此Array在达到CompileLength时变得更快.许多功能都不那么有利.如果你只是在函数中将乘法更改为除法,这会导致有理而不是整数结果,那么这种自动编译不会发生,并且Table更快:

n = 15;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, n}, {j, n}] // timeAvg

(* Out = 0.000576   *)

(* Out = 0.00042496 *)

n = 16;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, n}, {j, n}] // timeAvg

(* Out = 0.0005744  *)

(* Out = 0.0004352  *)
Run Code Online (Sandbox Code Playgroud)

但是,如果我们也可以编译呢?如果我们使用浮点数,通过开始1.,我们得到Real输出,可以编译:

n = 15;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}, 1.] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, 1., n}, {j, 1., n}] // timeAvg

(* Out = 0.0006256  *)

(* Out = 0.00047488 *)

n = 16;
Array[Mod[#^2, 5]/(1 + #2) &, {n, n}, 1.] // timeAvg
Table[Mod[i^2, 5]/(1 + j), {i, 1., n}, {j, 1., n}] // timeAvg

(* Out = 0.00010528 *)

(* Out = 0.00053472 *)
Run Code Online (Sandbox Code Playgroud)

再次,Array在更大尺寸的阵列上更快.

  • +1用于扩展自动编译分析.至于为什么`Table`对于多维数组来说比较慢,我认为不是因为`Table`无法编译,而是因为它无法确定结果数组是否为矩形(因为你可以创建非矩形嵌套)列表与`表'),因此不能打包 - 正如我在@ Sasha的答案的第一个评论中提到的那样.如果你不打包,使用Compile几乎没有什么优势,假设你的函数对单个数字的计算要求不高.但这只是猜测,我不确定. (3认同)