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

Compilation 在Mathematica中,如何为任意数量的参数编译函数Outer[]?,compilation,wolfram-mathematica,Compilation,Wolfram Mathematica,如果我想从两个列表list1和list2中查找所有可能的和,我将使用Outer[]函数和Plus作为组合运算符: [1]=list1={a,b}中的;列表2={c,d};外部[加上,列表1,列表2] Out[1]={{a+c,a+d},{b+c,b+d} 如果我想处理任意数量的列表,比如列表 [2]=listOfLists={list1,list2} 那么,我知道如何找到所有可能的和的唯一方法就是使用Apply[]函数(它有缩写@)和Join: In[3]=argumentsToPass=Joi

如果我想从两个列表
list1
list2
中查找所有可能的和,我将使用
Outer[]
函数和
Plus
作为组合运算符:

[1]=list1={a,b}中的
;列表2={c,d};外部[加上,列表1,列表2]

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

如果我想处理任意数量的列表,比如列表

[2]=listOfLists={list1,list2}

那么,我知道如何找到所有可能的和的唯一方法就是使用
Apply[]
函数(它有缩写
@
)和
Join

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

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

[4]=外部@@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:“函数参数Outer不支持编译Outer@@Join[{Plus},listOfLists]]。支持的函数参数只有Times、Plus或List。求值将使用未编译的函数。”

问题是,我正在使用一个受支持的函数,即
Plus
。问题似乎完全在于
Apply[]
函数。因为如果我给它一个固定数量的列表加在一起,它工作得很好

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

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

但我一使用
Apply
,它就坏了

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

Out[8]=Compile::cpapot:“函数参数Outer不支持编译Outer@@Join[{Plus},{bob,joe}]。支持的函数参数只有Times、Plus或List。求值将使用未编译的函数。”

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

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


非常感谢。

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

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}}}
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

编辑:

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

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
变量进行持久化,将记忆化的定义本地化,从而巧妙地解决这一开销:

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}}

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

如果您的输入是整数列表,我怀疑编译此函数的价值,至少在Mathematica 7中是如此

例如:

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}}}
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已经相当快了

如果您有编译速度以外的原因,您可能会在元组中找到一些用途,而不是在外部,但是您仍然受到需要张量输入的已编译函数的限制

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会终止内核,“没有更多可用内存。”

但是我不需要重新编译所有可能的列表吗?我想要一个单独的编译函数,根据输入,它可以处理不同数量的列表。后一个函数(ComputeSumsemoized)正是这样做的。当它第一次看到一些以前没有看到的列表(比如3个)时,它会编译。但在所有其他情况下,如果您提供3个列表,它将不会重新编译-它将使用记忆定义(已编译函数)。但是请注意,对于Outer,虽然您可以从编译中获得一些速度(特别是如果您编译为C),但这可能不会太引人注目。事实上,这并不是一个处理任何列表组合的单一编译函数。相反,这是编译函数的集合(哈希表),根据需要自动创建。对于单个编译函数,您确实存在
Apply
Outer
的问题,我不知道如何解决这个问题。但是,在实践中,它有那么大的区别吗?每次提供新数量的列表时,由于编译,您将面临较小的性能影响,但对于给定数量的列表,这种情况只会发生一次。明白了。天哪,这似乎是一个简单问题的复杂解决方案,但很可能Mathematica没有提供更好的方法。非常感谢您的大力帮助!问题其实并不那么简单。在一种过程语言中,它需要嵌套循环,根据输入参数的数量有许多循环-我不知道如何在C或Java中立即做到这一点,因为正是这个公式(不允许递归或其他技巧)。大约您需要多少个列表,