Julia 朱莉娅:给定一个可迭代的'itr',是否有一种有效的数据结构和算法来获取'itr'的前'n'个值的索引?

Julia 朱莉娅:给定一个可迭代的'itr',是否有一种有效的数据结构和算法来获取'itr'的前'n'个值的索引?,julia,Julia,考虑一下dist=rand(4_000_000),我希望找到顶部n值的索引。对于排名第一的人来说,这很容易 argmax(距离) 但是如果有一个查找顶部的函数n?如果只需要索引而不实际排序列表,请使用partialsortperm: julia> partialsortperm(dist,1:5) 5-element view(::Array{Int64,1}, 1:5) with eltype Int64: 1015972 672133 1519815 2724755 42

考虑一下
dist=rand(4_000_000)
,我希望找到顶部
n
值的索引。对于排名第一的人来说,这很容易

argmax(距离)


但是如果有一个查找顶部的函数
n

如果只需要索引而不实际排序列表,请使用
partialsortperm

julia> partialsortperm(dist,1:5)
5-element view(::Array{Int64,1}, 1:5) with eltype Int64:
 1015972
  672133
 1519815
 2724755
  428060
请注意,如果您在一行中多次这样做,您可以使用
partialsortperm避免重新分配索引向量

如果您确实需要元素使用
partialsort
,最快的方法是使用
partialsort

julia> partialsort!(dist,1:5)
5-element view(::Array{Float64,1}, 1:5) with eltype Float64:
 3.7341092440357215e-8
 8.483718505480908e-8
 2.1386679072143977e-7
 2.2127291532392235e-7
 4.3546665384752714e-7

这里有一个替代方案,只接受一个iterable
itr
,它不要求它是一个
AbstractArray
(为了简单起见,我假设
itr
生成浮点):

因此,正如您所看到的,如果
n
很小,那么即使对于
AbstractArray
s,这种方法也会更快(但主要的一点是,这种方法适用于任何iterable
itr

如果
n
较大,且
itr
已按升序排序,则
partialsorperm
rev=true
将更快(因为在线方法必须在每次迭代中更新解,这是在线方法无法避免的)。但是我假设一个典型的用例是针对小型
n
和稍微混乱的
itr


(如果您必须处理大的<代码> N< /代码>情况,并且更健壮,您可以考虑使用堆而不是只排序的数组,但我没有对它进行基准测试)

请注意,这些函数不接受原始问题中所要求的任何迭代器,而是<代码> > ActuaTrace。下面我发布了一个使用在线算法的替代方案。
function topn(itr, n)
    topvalues = Vector{Tuple{Float64, Int}}() # assuming itr produces floats
    @inbounds for (i,v) in enumerate(itr)
        if i <= n
            push!(topvalues, (v, i))
            i == n && sort!(topvalues)
        else
            if v > first(topvalues[1])
                j = searchsortedlast(topvalues, v, by = first) # assuming itr produces floats
                for k in 1:j-1
                    topvalues[k] = topvalues[k+1]
                end
                topvalues[j] = (v, i)
            end
        end
    end
    return sort!(topvalues, rev=true)
end
julia> using BenchmarkTools

julia> dist = rand(4_000_000);

julia> @btime partialsortperm($dist, 1:5, rev=true)
  69.583 ms (4 allocations: 30.52 MiB)
5-element view(::Array{Int64,1}, 1:5) with eltype Int64:
 3482486
  404839
  511215
 3082421
 2600778

julia> @btime topn($dist, 5)
  4.079 ms (5 allocations: 464 bytes)
5-element Array{Tuple{Float64,Int64},1}:
 (0.9999999876010581, 3482486)
 (0.9999998674122681, 404839)
 (0.9999998410867581, 511215)
 (0.9999995172666474, 3082421)
 (0.9999992939127473, 2600778)

julia> sort!(dist); # make dist ascending

julia> @btime partialsortperm($dist, 1:5, rev=true)
  45.634 ms (4 allocations: 30.52 MiB)
5-element view(::Array{Int64,1}, 1:5) with eltype Int64:
 4000000
 3999999
 3999998
 3999997
 3999996

julia> @btime topn($dist, 5)
  46.660 ms (5 allocations: 464 bytes)
5-element Array{Tuple{Float64,Int64},1}:
 (0.9999999876010581, 4000000)
 (0.9999998674122681, 3999999)
 (0.9999998410867581, 3999998)
 (0.9999995172666474, 3999997)
 (0.9999992939127473, 3999996)

julia> sort!(dist, rev=true); # make dist descending

julia> @btime partialsortperm($dist, 1:5, rev=true)
  41.199 ms (4 allocations: 30.52 MiB)
5-element view(::Array{Int64,1}, 1:5) with eltype Int64:
 1
 2
 3
 4
 5

julia> @btime topn($dist, 5)
  4.075 ms (5 allocations: 464 bytes)
5-element Array{Tuple{Float64,Int64},1}:
 (0.9999999876010581, 1)
 (0.9999998674122681, 2)
 (0.9999998410867581, 3)
 (0.9999995172666474, 4)
 (0.9999992939127473, 5)