【发布时间】:2012-05-30 13:14:36
【问题描述】:
我想计算一个二维数组“tocalc”,其中的元素是根据对其他三个列表 (z,b1,b2) 的测试来计算的。
(*example data*)
z = Range[0, 100, 10];
x = Range[10];
b1 = ConstantArray[0., Length[x]];
tocalc = ConstantArray[0, {Length[x], Length[z]}];
b2 = {0, 20, 30, 40, 50, 40, 30, 20, 10, 0};
一个解决方案是
(*simple but slow solution*)
Do[
Do[
If[z[[k]] <= b2[[i]] && z[[k]] >= b1[[i]],
tocalc[[i, k]] = (b2[[i + 1]] - b2[[i - 1]])],
{k, 1, Length[z]}];,
{i, 2, Length[x] - 1}]
结果
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {30, 30, 30, 0, 0, 0, 0, 0, 0, 0,
0}, {20, 20, 20, 20, 0, 0, 0, 0, 0, 0, 0}, {20, 20, 20, 20, 20, 0,
0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0}, {-20, -20, -20, -20, -20, 0, 0, 0, 0, 0,
0}, {-20, -20, -20, -20, 0, 0, 0, 0, 0, 0, 0}, {-20, -20, -20, 0, 0,
0, 0, 0, 0, 0, 0}, {-20, -20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0}}
问题:如何在 Mathematica 中有效地做到这一点?
如果评估 10000 次,则需要 3.66 秒。而在 Matlab 中,这需要 0.04 秒,所以 Matlab 快了近 100 倍。 我知道使用两个 Do 循环的解决方案对于 Mathematica 来说并不完美,因此我尝试了其他几种解决方案,例如 MapIndexed、Table、函数、条件等。但是这两个 Do 循环并不是真的更快,甚至可能更慢。 这是 MapIndexed 的一个示例:
tocalc = ConstantArray[0, {Length[x], Length[z]}];
MapIndexed[
If[z[[Part[#2, 2]]] <= b2[[Part[#2, 1]]] &&
z[[Part[#2, 2]]] >= b1[[Part[#2, 1]]] && Part[#2, 1] >= 2 &&
Part[#2, 1] <= Length[x] - 1,
tocalc[[Part[#2, 1], Part[#2, 2]]] = (b2[[Part[#2, 1] + 1]] -
b2[[Part[#2, 1] - 1]]), 0.] &, tocalc, {2}];
理想的解决方案应该适用于更大的矩阵和实数以及更复杂的条件。
---编辑:
由于在我的实际问题中看起来一些解决方案甚至更慢,所以这里是它的一个例子:
现实世界的问题
b2 = {0.`, 0.`, 0.`, 990.3440201085594`, 1525.7589030785484`,
1897.6531659202747`, 2191.6073263357594`, 2433.0441988616717`,
2630.6658409463894`, 2799.347578394955`, 2944.656306810331`,
3070.718467691769`, 3179.485627984329`, 3272.3788096129415`,
3346.199103579602`, 3405.384848015466`, 3346.199103579602`,
3272.3788096129415`, 3179.485627984329`, 3070.718467691769`,
2944.656306810331`, 2799.347578394955`, 2630.6658409463894`,
2433.0441988616717`, 2191.6073263357594`, 1897.6531659202747`,
1525.7589030785484`, 990.3440201085594`, 0.`, 0.`, 0.`};
z = {0.`, 250.`, 500.`, 750.`, 1000.`, 1250.`, 1500.`, 1750.`, 2000.`,
2250.`, 2500.`, 2750.`, 3000.`, 3250.`,
3500.`}; (*z(k)*)
imax = 31; (*number of x(i)*)
b1 = ConstantArray[0., imax]; (*lower boundary, can be different form 0*)
deltax = 50000.`;
mmax = 10000.; (*number of calculations*)
A00 = 1.127190283243198`*^-12; (*somefactor*)
n = 3;
一个解决方案:
f2C = Compile[{{b2, _Real, 1}, {z, _Real, 1}, {b1, _Real, 1}},
With[{zeros = {ConstantArray[0., Length[z]]}},
Join[zeros,
Table[If[
b1[[i]] <= z[[k]] <=
b2[[i]], -(A00*(Abs[(b2[[i + 1]] - b2[[i - 1]])/(2.0*
deltax)])^(n -
1.0)*(b2[[i]]^(n + 1.) - (b2[[i]] - z[[k]])^(n +
1.)))*((b2[[i + 1]] - b2[[i - 1]])/(2.0*deltax))
, 0.],
{i, 2, Length[b2] - 1}, {k, Length[z]}
], zeros]]
, CompilationTarget -> "C"];
结果是
Timing[Do[f2C[b2, z, b1];, {mmax}]]
Out[85]= {81.3544, Null}
谢谢!
【问题讨论】:
-
当你说“如果这被评估了 10000 次,那么它将花费超过 30 秒。”,你的意思是把你的前两个代码块包装在
Do[ ... , {10000}] // Timing中吗?这在 6.5 年前的移动 CPU 上运行 7 秒。或者你的意思是100000次?只是想确保我的基准测试是正确的。 -
好的。谢谢你是对的,我实际上使用了 100000 次。我编辑了文本。
-
在 MATLAB 上需要多长时间?就这样我知道什么时候停止尝试优化它:) “矢量化”它将时间从 7 秒推到 0.5 到 1 秒之间(即大约 10 倍加速),而无需编译。但是代码并不漂亮且不直观。对于更大的矩阵,加速会更大。
-
我现在在 Matlab 和 10^4 评估中做了同样的例子。耗时 0.04 秒,在 Mathematica 中为 3.66。所以 Matlab 快了大约 91.5 倍。 (在另一台机器上,但 Mathematica 甚至更新)。
标签: performance wolfram-mathematica