我经常写:{Min@#, Max@#} &
然而这似乎效率低下,因为表达式必须扫描两次,一次找到最小值,一次找到最大值。有更快的方法吗?表达式通常是张量或数组。
答案 0 :(得分:5)
这有点打败它。
minMax = Compile[{{list, _Integer, 1}},
Module[{currentMin, currentMax},
currentMin = currentMax = First[list];
Do[
Which[
x < currentMin, currentMin = x,
x > currentMax, currentMax = x],
{x, list}];
{currentMin, currentMax}],
CompilationTarget -> "C",
RuntimeOptions -> "Speed"];
v = RandomInteger[{0, 1000000000}, {10000000}];
minMax[v] // Timing
我认为它比Leonid的版本快一点,因为Do
比For
快一点,即使在编译代码中也是如此。
但最终,这是使用高级函数式编程语言时性能影响的一个例子。
对Leonid的回应:
我不认为该算法可以解释所有的时差。 很多,我认为,无论如何都会应用这两种测试。但是,Do
和For
之间的差异是可衡量的。
cf1 = Compile[{{list, _Real, 1}},
Module[{sum},
sum = 0.0;
Do[sum = sum + list[[i]]^2,
{i, Length[list]}];
sum]];
cf2 = Compile[{{list, _Real, 1}},
Module[{sum, i},
sum = 0.0;
For[i = 1, i <= Length[list],
i = i + 1, sum = sum + list[[i]]^2];
sum]];
v = RandomReal[{0, 1}, {10000000}];
First /@{Timing[cf1[v]], Timing[cf2[v]]}
{0.685562, 0.898232}
答案 1 :(得分:3)
在Mathematica编程实践中,我认为这是最快的。我认为在mma中尝试使其更快的唯一方法是将Compile
与C编译目标一起使用,如下所示:
getMinMax =
Compile[{{lst, _Real, 1}},
Module[{i = 1, min = 0., max = 0.},
For[i = 1, i <= Length[lst], i++,
If[min > lst[[i]], min = lst[[i]]];
If[max < lst[[i]], max = lst[[i]]];];
{min, max}], CompilationTarget -> "C", RuntimeOptions -> "Speed"]
然而,即使这似乎比你的代码慢一点:
In[61]:= tst = RandomReal[{-10^7,10^7},10^6];
In[62]:= Do[getMinMax[tst],{100}]//Timing
Out[62]= {0.547,Null}
In[63]:= Do[{Min@#,Max@#}&[tst],{100}]//Timing
Out[63]= {0.484,Null}
你可能完全用C编写函数,然后编译并加载为dll - 你可以通过这种方式消除一些开销,但我怀疑你会赢得超过几个百分点 - 不值得IMO的努力。
修改强>
有趣的是,您可以使用手动公共子表达式消除显着提高编译解决方案的速度(lst[[i]]
此处):
getMinMax =
Compile[{{lst, _Real, 1}},
Module[{i = 1, min = 0., max = 0., temp},
While[i <= Length[lst],
temp = lst[[i++]];
If[min > temp, min = temp];
If[max < temp, max = temp];];
{min, max}], CompilationTarget -> "C", RuntimeOptions -> "Speed"]
比{Min@#,Max@#}
快一点。
答案 2 :(得分:3)
对于数组,您可以执行最简单的功能并使用
Fold[{Min[##],Max[##]}&, First@#, Rest@#]& @ data
不幸的是,它不是速度恶魔。即使对于短列表,5个元素,Leonid's answers和Mark's answer都至少快7倍,在第7版中未编译。对于长列表,25000个元素,这种情况变得更糟,Mark的速度提高了19.6倍,即使在这个长度上,这个解决方案也只需要0.05秒就可以运行。
但是,我不会指望{Min[#], Max[#]}&
作为选项。未编译它比Mark的短名单快1.7倍,长列表快15倍(分别比Fold
解决方案快8倍和近300倍)。
不幸的是,我无法为{Min[#], Max[#]}&
,Leonid或Mark的答案的编译版本获得好的数字,而是我收到了许多难以理解的错误消息。实际上,{Min[#], Max[#]}&
执行时间增加了。尽管如此,Fold
解决方案得到了显着改善,并且花费了Leonid的答案“未编译时间”的两倍。
编辑:对于好奇,这里是未编译函数的一些时序测量 -
每个函数用于100个水平轴上指定长度的列表,平均时间(秒)是垂直轴。按照时间的升序,曲线是{Min[#], Max[#]}&
,马克的答案,列昂尼德的第二个答案,列昂尼德的第一个答案,以及上面的Fold
方法。
答案 3 :(得分:2)
对于所有正在做时间的人,我想警告你,执行的顺序非常重要。例如,看看以下两个略有不同的时序测试:
(1)
res =
Table[
a = RandomReal[{0, 100}, 10^8];
{
Min[a] // AbsoluteTiming // First, Max[a] // AbsoluteTiming // First,
Max[a] // AbsoluteTiming // First, Min[a] // AbsoluteTiming // First
}
, {100}
]
这里的奇怪人物是最后一个Min
(2)
res =
Table[
a = RandomReal[{0, 100}, 10^8];
{
Max[a] // AbsoluteTiming // First, Min[a] // AbsoluteTiming // First,
Min[a] // AbsoluteTiming // First, Max[a] // AbsoluteTiming // First
}
, {100}
]
此处,找到第一个Max
的最高时间,第二个max
的第二个最高时间,两个Min
的时间大致相同且最低。实际上,我希望Max
和Min
大约需要同一时间,但事实并非如此。前者似乎比后者花费多50%的时间。杆位也似乎有50%的差点。
现在与Mark和Leonid给出的算法进行比较:
res =
Table[
a = RandomReal[{0, 100}, 10^8];
{
{Max[a], Min[a]} // AbsoluteTiming // First,
{Min@#, Max@#} &@a // AbsoluteTiming // First,
getMinMax[a] // AbsoluteTiming // First,
minMax[a] // AbsoluteTiming // First,
{Min[a], Max[a]} // AbsoluteTiming // First
}
, {100}
]
这里我们发现{Max [a],Min [a]}(包括杆位障碍)的.3 s,.1级是Mark的方法;其他人都差不多。