在Mathematica中,如何为任意数量的参数编译函数Outer []?

时间:2011-02-11 19:58:34

标签: compilation wolfram-mathematica

如果我想从两个列表list1list2中找到所有可能的总和,我使用Outer[]函数并将Plus作为组合运算符:< / p>

In[1]= list1 = {a, b}; list2 = {c, d}; Outer[Plus, list1, list2]

Out[1]= {{a + c, a + d}, {b + c, b + d}}

如果我希望能够处理任意数量的列表,请说明列表,

In[2]= listOfLists={list1, list2};

那么我知道如何找到所有可能总和的唯一方法是使用Apply[]函数(其中包含短手@@)以及Join

In[3]= argumentsToPass=Join[{Plus},listOfLists]

Out[3]= {Plus, {a, b}, {c, d}}

In[4]= Outer @@ argumentsToPass

Out[4]= {{a + c, a + d}, {b + c, b + d}}

或只是

In[5]= Outer @@ Join[{Plus},listOfLists]

Out[5]= {{a + c, a + d}, {b + c, b + d}}

当我尝试编译时出现问题:

In[6]= Compile[ ..... Outer @@ Join[{Plus},listOfLists] .... ]

Compile::cpapot: "Compilation of Outer@@Join[{Plus},listOfLists]] is not supported for the function argument Outer. The only function arguments supported are Times, Plus, or List. Evaluation will use the uncompiled function. "

问题是,我使用支持的功能,即Plus。问题似乎只与Apply[]函数有关。因为如果我给外部加一个固定数量的列表,它可以正常工作

In[7]= Compile[{{bob, _Integer, 1}, {joe, _Integer, 1}}, Outer[Plus, bob, joe]]

Out[7]= CompiledFunction[{bob, joe}, Outer[Plus, bob, joe],-CompiledCode-]

但是一旦我使用Apply,它就会中断

In[8]= Compile[{{bob, _Integer, 1}, {joe, _Integer, 1}}, Outer @@ Join[{Plus}, {bob, joe}]]

Out[8]= Compile::cpapot: "Compilation of Outer@@Join[{Plus},{bob,joe}] is not supported for the function argument Outer. The only function arguments supported are Times, Plus, or List. Evaluation will use the uncompiled function."

所以我的问题是:有没有办法规避这个错误,或者是一种计算从编译函数中任意数量的列表中提取的所有可能元素总和的方法?

(另外,我不确定“编译”是否是合适的标签。请告知。)

非常感谢。

2 个答案:

答案 0 :(得分:11)

使用With以编程方式创建编译函数的一种方法:

Clear[makeCompiled];
makeCompiled[lnum_Integer] :=
 With[{listNames = Table[Unique["list"], {lnum}]},
   With[{compileArgs = {#, _Integer, 1} & /@ listNames},
      Compile @@ Join[Hold[compileArgs],
        Replace[Hold[Outer[Plus, listNames]], 
          Hold[Outer[Plus, {x__}]] :> Hold[Outer[Plus, x]], {0}]]]];

它可以做得更漂亮,但它确实有效。例如:

In[22]:= p2 = makeCompiled[2]
Out[22]= CompiledFunction[{list13,list14},Outer[Plus,list13,list14],-CompiledCode-]

In[23]:= p2[{1,2,3},{4,5}]
Out[23]= {{5,6},{6,7},{7,8}}

In[24]:= p3 = makeCompiled[3]
Out[24]= CompiledFunction[{list15,list16,list17},Outer[Plus,list15,list16,list17],-CompiledCode-]

In[25]:= p3[{1,2},{3,4},{5,6}]
Out[25]= {{{9,10},{10,11}},{{10,11},{11,12}}}

HTH

编辑:

您可以将已编译的函数隐藏在另一个函数后面,以便在运行时创建它并且您实际上看不到它:

In[33]:= 
Clear[computeSums]
computeSums[lists : {__?NumberQ} ..] := makeCompiled[Length[{lists}]][lists];

In[35]:= computeSums[{1, 2, 3}, {4, 5}]

Out[35]= {{5, 6}, {6, 7}, {7, 8}}

在这种情况下,您将面临编译的开销,因为您每次都会重新创建一个已编译的函数。使用Module变量进行持久化,您可以通过memoization相当优雅地控制此开销,以本地化您的memoized定义:

In[44]:= 
Clear[computeSumsMemoized];
Module[{compiled},
  compiled[n_] := compiled[n] = makeCompiled[n];
  computeSumsMemoized[lists : {__?NumberQ} ..] := compiled[Length[{lists}]][lists]];

In[46]:= computeSumsMemoized[{1, 2, 3}, {4, 5}]

Out[46]= {{5, 6}, {6, 7}, {7, 8}}

答案 1 :(得分:3)

这是我的第一篇文章。我希望我做对了。

如果你的输入是整数列表,我对编译这个函数的价值持怀疑态度,至少在Mathematica 7中是这样。

例如:

f = Compile[{{a, _Integer, 1}, {b, _Integer, 1}, {c, _Integer, 1}, {d, _Integer, 1}, {e, _Integer, 1}}, 
        Outer[Plus, a, b, c, d, e]
    ];

a = RandomInteger[{1, 99}, #] & /@ {12, 32, 19, 17, 43};

Do[f @@ a, {50}] // Timing

Do[Outer[Plus, ##] & @@ a, {50}] // Timing

两个时间对我来说并没有太大的不同,但当然这只是一个样本。关键在于,与编译版相比,Outer已经相当快。

如果您有除编译速度以外的其他原因,您可能会在Tuples中找到一些用途而不是Outer,但您仍然需要编译函数的约束,需要张量输入。

f2 = Compile[{{array, _Integer, 2}}, 
      Plus @@@ Tuples@array
    ];

f2[{{1, 3, 7}, {13, 25, 41}}]

如果您的输入很大,则可能需要采用不同的方法。给定一个整数列表,这个函数将返回可能的总和以及获得每个总和的方法的数量:

f3 = CoefficientRules@Product[Sum[x^i, {i, p}], {p, #}] &;

f3[{{1, 3, 7}, {13, 25, 41}}]

在许多情况下,这应该证明更具记忆效率。

a2 = RandomInteger[{1, 999}, #] & /@ {50, 74, 55, 55, 90, 57, 47, 79, 87, 36};

f3[a2]; // Timing

MaxMemoryUsed[]

这需要3秒钟和最小内存,但尝试将Outer应用到a2会终止内核“没有更多内存可用。”