如何解决朱莉娅:计算排序数组中可能包含缺失的唯一元素数量的最快方法
关键是数组已排序并且可能包含缺少的元素。我怀疑length(unique(arr))
可能不是最快的。
我想知道是否存在可以处理这种情况的预构建函数?
解决方法
Julia的implementation of unique(itr)
对于任意集合都非常有效-时间与输入集合的大小大致成线性比例。但是,由于它构造了两个查找字典来帮助识别以前看过的元素,因此它分配的内存量与集合中唯一元素的数量成比例。如果您知道输入集合已经排序,则可以利用它来减少分配并显着加快计数:
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
函数对数组进行排序的情况下,此函数才起作用。您可以在迭代中止时添加检查,并在必要时切换到length(unique(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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。