Mathematica中两个奇怪的效率问题

Jes*_*del 5 optimization performance wolfram-mathematica compilation

第一个问题

我有时间计算以下语句需要多长时间(其中V [x]是一个耗时的函数调用):

Alice     = Table[V[i],{i,1,300},{1000}];
Bob       = Table[Table[V[i],{i,1,300}],{1000}]^tr;
Chris_pre = Table[V[i],{i,1,300}];
Chris     = Table[Chris_pre,{1000}]^tr;
Run Code Online (Sandbox Code Playgroud)

爱丽丝,鲍勃和克里斯是相同的基质,计算3种略有不同的方式.我发现Chris的计算速度比Alice和Bob快1000倍.

Alice计算速度慢1000倍并不奇怪,因为天真地,函数V必须比计算Chris时多被调用1000次.但令人非常惊讶的是,Bob的速度非常慢,因为除了Chris存储中间步骤Chris_pre之外,他的计算方式 Chris完全相同.

鲍勃为什么评价这么慢?


第二个问题

假设我想在表单的Mathematica中编译一个函数

f(x)=x+y
Run Code Online (Sandbox Code Playgroud)

其中"y"是在编译时固定的常量(但我不希望在代码中直接替换它的数字,因为我希望能够轻松地更改它).如果y的实际值是y = 7.3,我定义

f1=Compile[{x},x+y]
f2=Compile[{x},x+7.3]
Run Code Online (Sandbox Code Playgroud)

然后f1比f2慢50%.当编译f1时,如何使Mathematica将"y"替换为"7.3",以便f1以f2的速度运行?


编辑:

我找到了第二个问题的一个丑陋的解决方法:

f1=ReleaseHold[Hold[Compile[{x},x+z]]/.{z->y}]
Run Code Online (Sandbox Code Playgroud)

肯定有更好的办法...

Cas*_*bel 5

您可能应该将这些问题作为单独的问题发布,但不用担心!

问题一

Alice的问题当然是你的期望.Bob的问题是Table每次外部迭代都会对内部进行一次求值Table.Trace清晰可见:

Trace[Table[Table[i, {i, 1, 3}], {3}]]

{
Table[Table[i,{i,1,2}],{2}],
{Table[i,{i,1,2}],{i,1},{i,2},{1,2}},{Table[i,{i,1,2}],{i,1},{i,2},{1,2}},
{{1,2},{1,2}}
}
Run Code Online (Sandbox Code Playgroud)

添加了换行符以强调,是的,Trace on Table的输出有点奇怪,但你可以看到它.很明显,Mathematica可以更好地优化它,因为知道外部表没有迭代器,但无论出于何种原因,它都没有考虑到这一点.只有克里斯做你想要的,尽管你可以修改鲍勃:

Transpose[Table[Evaluate[Table[V[i],{i,1,300}]],{1000}]]
Run Code Online (Sandbox Code Playgroud)

这看起来实际上比Chris大约两倍左右,因为它不必存储中间结果.

问题二

使用Evaluate有一个更简单的解决方案,但我希望它不适用于所有可能的编译函数(即真正应该被保存的函数):

f1 = Compile[{x}, Evaluate[x + y]];
Run Code Online (Sandbox Code Playgroud)

你也可以使用With:

With[{y=7.3},
    f1 = Compile[{x}, x + y];
]
Run Code Online (Sandbox Code Playgroud)

或者,如果y在其他地方定义,请使用临时:

y = 7.3;
With[{z = y},
    f1 = Compile[{x}, x + z];
]
Run Code Online (Sandbox Code Playgroud)

我不是Mathematica的范围和评估机制的专家,所以可以很容易地有一个更好的方法,但希望其中一个为你做到!