查找Mathematica列表的第一个元素大于阈值

时间:2010-09-08 21:10:56

标签: wolfram-mathematica

我想知道如何获得大于给定阈值的(已经订购的)列表的第一个元素。

我不太了解Mathematica中的列表操作功能,也许有人可以给我一个有效的技巧。

6 个答案:

答案 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
  ]

enter image description here

(为了演示目的,我任意将阈值设为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
  ]

产生

enter image description here

所以,更快,更好的缩放行为。

实际上没有必要编译它,但我还是做了。

最后,不要将Select用于长列表。

我的回答结束了。有关于手动或通过Combinatorica包进行二进制搜索的一些注释。

我将(编译的)短例程的速度与二分法搜索的速度与BinarySearch的{​​{1}}进行了比较。请注意,这不会出现问题所要求的问题(CombinatoricaBinarySearch也不会这样做;我上面给出的代码。

二进制搜索可以迭代实现为

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)

答案 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 eicrit[ei],则

TrueSelectFirst[list, # > threshold &]

对于您的具体情况,您可以使用:

#!/bin/bash

if [ $# -lt 3 ]; then
  echo "Some arguments are missing"
fi

# passing "*" as argument 
echo "$2"