我想知道如何获得大于给定阈值的(已经订购的)列表的第一个元素。
我不太了解Mathematica中的列表操作功能,也许有人可以给我一个有效的技巧。
答案 0 :(得分:12)
Select
做了你需要的,并且会保持一致,尊重列表的预先存在的顺序:
Select[list, # > threshold &, 1]
例如:
In[1]:= Select[{3, 5, 4, 1}, # > 3 &, 1]
Out[1]= {5}
您可以在第二个参数中提供所需的任何阈值或标准函数。
第三个参数指定只匹配一个(即第一个)元素。
希望有所帮助!
答案 1 :(得分:8)
Joe在他的answer中正确地指出,人们会期望二进制搜索技术比Select
更快,即使列表已经排序,它似乎只是进行线性搜索:
ClearAll[selectTiming]
selectTiming[length_, iterations_] := Module[
{lst},
lst = Sort[RandomInteger[{0, 100}, length]];
(Do[Select[lst, # == 2 &, 1], {i, 1, iterations}] // Timing //
First)/iterations
]
(为了演示目的,我任意将阈值设为2)。
然而,Combinatorica中的BinarySearch
函数是a)不合适(它返回的元素与请求的元素匹配,但不是第一个(最左边),这就是问题所在。
为了获得大于阈值的最左边元素,给定一个有序列表,我们可以递归地进行:
binSearch[lst_,threshold_]:= binSearchRec[lst,threshold,1,Length@lst]
(*
return position of leftmost element greater than threshold
breaks if the first element is greater than threshold
lst must be sorted
*)
binSearchRec[lst_,threshold_,min_,max_] :=
Module[{i=Floor[(min+max)/2],element},
element=lst[[i]];
Which[
min==max,max,
element <= threshold,binSearchRec[lst,threshold,i+1,max],
(element > threshold) && ( lst[[i-1]] <= threshold ), i,
True, binSearchRec[lst,threshold,min,i-1]
]
]
或迭代地:
binSearchIterative[lst_,threshold_]:=Module[
{min=1,max=Length@lst,i,element},
While[
min<=max,
i=Floor[(min+max)/2];
element=lst[[i]];
Which[
min==max, Break[],
element<=threshold, min=i+1,
(element>threshold) && (lst[[i-1]] <= threshold), Break[],
True, max=i-1
]
];
i
]
递归方法更清晰但我会坚持迭代方法。
测试它的速度,
ClearAll[binSearchTiming]
binSearchTiming[length_, iterations_] := Module[
{lst},
lst = Sort[RandomInteger[{0, 100}, length]];
(Do[binSearchIterative[lst, 2], {i, 1, iterations}] // Timing //
First)/iterations
]
产生
所以,更快,更好的缩放行为。
实际上没有必要编译它,但我还是做了。
最后,不要将Select
用于长列表。
我的回答结束了。有关于手动或通过Combinatorica包进行二进制搜索的一些注释。
我将(编译的)短例程的速度与二分法搜索的速度与BinarySearch
的{{1}}进行了比较。请注意,这不会出现问题所要求的问题(Combinatorica
中BinarySearch
也不会这样做;我上面给出的代码。
二进制搜索可以迭代实现为
Combinatorica
我们现在可以将此与binarySearch = Compile[{{arg, _Integer}, {list, _Integer, 1}},
Module[ {min = 1, max = Length@list,
i, x},
While[
min <= max,
i = Floor[(min + max)/2];
x = list[[i]];
Which[
x == arg, min = max = i; Break[],
x < arg, min = i + 1,
True, max = i - 1
]
];
If[ 0 == max,
0,
max
]
],
CompilationTarget -> "C",
RuntimeOptions -> "Speed"
];
与BinarySearch
进行比较。请注意a)列表必须排序b)这不会返回第一个匹配元素,而是一个匹配元素。
Combinatorica
让我们比较两个二进制搜索例程。重复50000次:
lst = Sort[RandomInteger[{0, 100}, 1000000]];
所以手写的更快。现在实际上二进制搜索只是访问列表中的6-7个点来获取这些参数(例如Needs["Combinatorica`"]
Do[binarySearch[2, lst], {i, 50000}] // Timing
Do[BinarySearch[lst, 2], {i, 50000}] // Timing
(*
{0.073437, Null}
{4.8354, Null}
*)
),显然差异只是开销;例如,或许{500000, 250000, 125000, 62500, 31250, 15625, 23437}
更通用,或者不编译。
答案 2 :(得分:5)
您可能也希望查看TakeWhile []和LengthWhile []。
http://reference.wolfram.com/mathematica/ref/TakeWhile.html http://reference.wolfram.com/mathematica/ref/LengthWhile.html
答案 3 :(得分:3)
使用Select
可以解决问题,但如果你关心效率,这是一个糟糕的解决方案。 Select
遍历列表中的所有元素,因此将花费时间与列表的长度呈线性关系。
由于您说列表是有序的,因此使用BinarySearch
要好得多,{{3}}将在列表大小的对数时间内工作。表达式(编辑:我做了一个小调整,因为我写的上一个表达式没有正确处理列表中的重复元素。另一个编辑:这仍然不起作用当阈值本身作为重复元素出现在列表中时,请参阅注释):
Floor[BinarySearch[list,threshold]+1]
将为您提供所需元素的索引。如果所有元素都小于阈值,您将得到列表的长度加一
附:不要忘记在使用Needs["Combinatorica'"]
之前致电BinarySearch
。
答案 4 :(得分:3)
list /. {___, y_ /; y > 3, ___} :> {y}
例如
{3, 5, 4, 1} /. {___, y_ /; y > 3, ___} :> {y}
{5}
答案 5 :(得分:1)
仅供将来参考,从 v10 开始,您可以使用SelectFirst
它有一些额外的细节,例如返回Missing[]
或默认值。
来自文档:
如果
SelectFirst[{e1,e2,…}, crit]
会为ei
crit[ei]
提供True
,如果找不到,则为Missing["NotFound"]
。SelectFirst[{e1,e2,…}, crit, default]
default
ei
为crit[ei]
,则
True
会SelectFirst[list, # > threshold &]
。
对于您的具体情况,您可以使用:
#!/bin/bash
if [ $# -lt 3 ]; then
echo "Some arguments are missing"
fi
# passing "*" as argument
echo "$2"