Julia:计算排序数组中可能包含缺失元素的唯一元素数的最快方法
关键是数组已排序,可能包含缺少的元素。我怀疑Julia:计算排序数组中可能包含缺失元素的唯一元素数的最快方法,julia,Julia,关键是数组已排序,可能包含缺少的元素。我怀疑长度(unique(arr))可能不是最快的 我想知道是否有一个预构建的函数可以处理这种情况?Julia的函数对于任意集合非常有效——时间与输入集合的大小近似成线性关系。但是,由于它构造了两个查找字典来帮助识别以前见过的元素,因此它分配的内存量会随着集合中唯一元素的数量而增加。如果您知道输入集合已排序,则可以利用这一点来减少分配并显著加快计数: function nunique(a) last = first(a) n = 1
长度(unique(arr))
可能不是最快的
我想知道是否有一个预构建的函数可以处理这种情况?Julia的函数对于任意集合非常有效——时间与输入集合的大小近似成线性关系。但是,由于它构造了两个查找字典来帮助识别以前见过的元素,因此它分配的内存量会随着集合中唯一元素的数量而增加。如果您知道输入集合已排序,则可以利用这一点来减少分配并显著加快计数:
function nunique(a)
last = first(a)
n = 1
for x in a
if isless(last, x)
n += 1
last = x
end
end
n
end
r = Array{Union{Missing,Int64}}(rand(1:10000, 100000)) # 100_000 elements, 10_000 unique
r[rand(1:length(r), 100)] .= missing # 100 missing elements
sort!(r)
@time length(unique(r))
# 0.002156 seconds (37 allocations: 503.781 KiB)
# 10001
@time nunique(r)
# 0.000464 seconds (1 allocation: 16 bytes)
# 10001
就我所知,没有为排序输入数组的特殊情况进行优化的内置函数
此函数仍将像输入集合的大小一样在时间上进行缩放,但它只进行一次(!)分配,因此它可以减少创建查找字典所涉及的所有开销
当然,只有当数组已经按照isless
函数进行排序时,此函数才起作用。您可以在迭代中止时添加检查,并在必要时切换到长度(唯一(itr))
版本:
function nunique2(a)
last = first(a)
n = 1
for x in a
if isless(last, x)
n += 1
last = x
elseif !isequal(last, x)
return length(unique(a))
end
end
n
end
@time nunique2(r)
# 0.000256 seconds (1 allocation: 16 bytes)
# 10001
using Random
shuffle!(r)
@time nunique2(r)
# 0.002801 seconds (37 allocations: 503.781 KiB)
# 10001
与所有微基准一样,YMMV。我有一个并行解决方案,但遗憾的是,它在6个线程上只快了30%
function nunique(v)
@assert length(v) > 0
cnt = 1
lasta::eltype(v) = first(v)
@inbounds for newa1 in v
if !isequal(newa1, lasta)
cnt += 1
lasta = newa1
end
end
cnt
end
""" Parallelized version """
function pnunique(v)
nt = Threads.nthreads()
lo::Vector{Int} = collect(0:div(length(v), nt):length(v)-1)
hi::Vector{Int} = lo[2:end]
hi[end] = length(v)
lo = lo .+ 1
nu = Vector{Int}(undef, nt)
Threads.@threads for i in 1:nt
@inbounds nu[i] = nunique(@view v[lo[i]:hi[i]])
end
res = sum(nu)
for j in 1:nt-1
@inbounds res -= v[hi[j]] == v[lo[j+1]]
end
res
end