我想计算一个2D数组“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,functions,Conditionals等。但是两个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}
谢谢!
答案 0 :(得分:1)
您可以执行以下操作。您需要弄清楚如何处理边界(其中未定义b2 [[i + 1]]或b2 [[i-1]])。
f[x_, y_] := If[x[[1]] <= y <= x[[2]], x[[4]] - x[[3]], 0]
这里我限制了Outer所处的级别,这样我就不需要改变头部了(正如我在原始响应中所做的那样)。
In[1309]:= Outer[f,
Transpose[{b1, b2, RotateRight[b2], RotateLeft[b2]}], z, 1]
Out[1309]= {{20, 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}, {-10, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0}}
速度检查:
In[1298]:= Timing[
Do[Outer[f,
Apply[list,
Transpose[{b1, b2, RotateRight[b2], RotateLeft[b2]}], {1}],
z], {10^4}]]
Out[1298]= {2.68, Null}
我们可以编译该函数以获得更好的速度。
fC = Compile[{{x, _Integer, 1}, {y, _Integer}},
If[x[[1]] <= y <= x[[2]], x[[4]] - x[[3]], 0]];
In[1306]:= Timing[
Do[Outer[fC, Transpose[{b1, b2, RotateRight[b2], RotateLeft[b2]}], z,
1], {10^4}]]
Out[1306]= {0.8, Null}
---编辑---
变体包括编译整个例程。这是一个这样的。
ff = Compile[{{b1, _Integer, 1}, {b2, _Integer, 1}, {z, _Integer,
1}},
With[{lc =
RotateRight[ListConvolve[{1, 0, -1}, b2, {-1, -1}, 0]]},
Table[
If[b1[[i]] <= z[[k]] <= b2[[i]], lc[[i]], 0], {i,
Length[b2]}, {k, Length[z]}
]]];
In[385]:= Timing[Do[ff[b1, b2, z], {10^4}]]
Out[385]= {0.24, Null}
如果我添加CompilationTarget -> "C"
,那么它会快两倍。
另一种变体,在C代码中,不到0.1秒。
In[441]:=
ff2C = Compile[{{b1, _Integer, 1}, {b2, _Integer, 1}, {z, _Integer,
1}},
With[{zeros = {ConstantArray[0, Length[z]]}},
Join[zeros, Table[
If[b1[[i]] <= z[[k]] <= b2[[i]], b2[[i + 1]] - b2[[i - 1]],
0], {i, 2, Length[b2] - 1}, {k, Length[z]}
], zeros]], CompilationTarget -> "C"];
In[442]:= Timing[Do[ff2C[b1, b2, z], {10^4}]]
Out[442]= {0.04, Null}
In[443]:= ff2C[b1, b2, z]
Out[443]= {{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}}
我猜有些变种可能会更快。
---结束编辑---
---编辑2 ---
当然,如果您有全局变量(即在Compile之外定义),那么还有一些工作要做。我知道两种可能性。在版本8之前,可以使用Compile周围的With []来填充常量,如下所示。
f2C = [[n = n,deltax = deltax,A00 = A00}, 编译[{{b2,_Real,1},{z,_Real,1},{b1,_Real,1}}, 使用[{zeros = {ConstantArray [0。,Length [z]]}}, 加入[零, 表[如果[ b1 [[i]]&lt; = z [[k]]&lt; = 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,长度[b2] - 1},{k,长度[z]}],零]], CompilationTarget - &gt; “C”]];
在版本8中,以下效果相同。
f2Cb =编译[{{b2,_Real,1},{z,_Real,1},{b1,_Real,1}}, 使用[{zeros = {ConstantArray [0。,Length [z]]}}, 加入[零, 表[如果[ b1 [[i]]&lt; = z [[k]]&lt; = 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,长度[b2] - 1},{k,长度[z]}],零]], CompilationTarget - &gt; “C”, CompilationOptions - &gt; {“InlineExternalDefinitions” - &gt;真}];
要么我在大约0.7秒内得到一个更现实的例子的结果,而我的机器将花费超过100秒而没有在编译内定义那些全局变量。
更通用的方法可能是将它们作为参数传递(如果它们可能更改而不是常量)。这会导致运行时间略微变慢。
关于该选项方法,您可以查看Cocumentation Center中的ref / CompilationOptions
---结束编辑2 ---