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

Ced*_* H. 8 wolfram-mathematica

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

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

Mic*_*lat 12

Select 做你需要的,并且会保持一致,尊重列表的预先存在的顺序:

Select[list, # > threshold &, 1]
Run Code Online (Sandbox Code Playgroud)

例如:

In[1]:= Select[{3, 5, 4, 1}, # > 3 &, 1]

Out[1]= {5}
Run Code Online (Sandbox Code Playgroud)

您可以在第二个参数中提供所需的任何阈值或标准函数.

第三个参数指定只匹配一个(即第一个)元素.

希望有所帮助!


acl*_*acl 8

Joe正确地在他的回答中指出,人们会期望二进制搜索技术比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
  ]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

(为了演示目的,我任意将阈值设为2).

但是,BinarySearchCombinatorica中的函数是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]
        ]
    ]
Run Code Online (Sandbox Code Playgroud)

或迭代地:

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
]
Run Code Online (Sandbox Code Playgroud)

递归方法更清晰但我会坚持迭代方法.

为了测试它的速度,

ClearAll[binSearchTiming]
binSearchTiming[length_, iterations_] := Module[
    {lst},
    lst = Sort[RandomInteger[{0, 100}, length]];
    (Do[binSearchIterative[lst, 2], {i, 1, iterations}] // Timing // 
     First)/iterations
  ]
Run Code Online (Sandbox Code Playgroud)

哪个产生

在此输入图像描述

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

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

总之,那么,不要Select用于长列表.

这就是我的答案.有关于手动或通过Combinatorica包进行二进制搜索的一些注释.

我比较了(编译)短常规的速度做二进制搜索VS的BinarySearch距离Combinatorica.请注意,这不做些什么的问题问什么(同样没有BinarySearchCombinatorica); 我上面给出的代码.

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

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"
];
Run Code Online (Sandbox Code Playgroud)

我们现在可以比较这和BinarySearchCombinatorica.注意,)名单必须进行排序b)本不会返回第一个匹配的元素,但一个匹配的元素.

lst = Sort[RandomInteger[{0, 100}, 1000000]];
Run Code Online (Sandbox Code Playgroud)

让我们比较两个二进制搜索例程.重复50000次:

Needs["Combinatorica`"]
Do[binarySearch[2, lst], {i, 50000}] // Timing
Do[BinarySearch[lst, 2], {i, 50000}] // Timing
(*
{0.073437, Null}
{4.8354, Null}
*)
Run Code Online (Sandbox Code Playgroud)

所以手写的更快.现在实际上二进制搜索只是访问列表中的6-7个点来获取这些参数(例如类似{500000, 250000, 125000, 62500, 31250, 15625, 23437}的东西),显然差异只是开销; BinarySearch例如,或许是更一般的,或者没有编译.