在Julia语言中,我们可以检查一个数组是否包含某个值,方法如下:
> 6 in [4,6,5]
true
然而,当尝试按特定顺序检查子数组时,这将返回false:
> [4,6] in [4,6,5]
false
如何正确使用语法来验证是否存在特定的子数组?
我认为值得一提的是,在Julia 1.0中你有一个名为issubset
的函数。
> issubset([4,6], [4,6,5])
true
您也可以使用 latex 的符号\subseteq
来方便地调用它。
> [4,6] ⊆ [4,6,5]
true
这看起来对我来说很优化:> using Random
> x, y = randperm(10^3)[1:10^2], randperm(10^3);
> @btime issubset(x, y);
16.153 μs (12 allocations: 45.96 KiB)
写出一个性能良好的函数需要一些代码,但这比上面的 issubvec
版本要快得多:
function subset2(x,y)
lenx = length(x)
first = x[1]
if lenx == 1
return findnext(y, first, 1) != 0
end
leny = length(y)
lim = length(y) - length(x) + 1
cur = 1
while (cur = findnext(y, first, cur)) != 0
cur > lim && break
beg = cur
@inbounds for i = 2:lenx
y[beg += 1] != x[i] && (beg = 0 ; break)
end
beg != 0 && return true
cur += 1
end
false
end
0.005273 seconds (65.70 k allocations: 4.073 MB)
0.000086 seconds (4 allocations: 160 bytes)
@time
的结果(对于 issubvec
)看起来可能包含了编译 - 对于如此简单的调用来说,它是一个过于异常的值。你能重新检查一下(在计时之前进行一次编译运行)吗? - Dan Getzissubvec
似乎具有O(n)的分配,其中n是y的长度。 - Scott Jonessubset2
更加优化(如果想要推进,还有更多的优化选项),但这可能会是另一个讨论的议题。 - Dan Getzissubvec
函数说明了标准库中数组函数可以做些什么。 - Scott Jones针对第三个条件,即向量[4,6]
作为子向量出现在4,6,5
中,建议使用以下函数:
issubvec(v,big) =
any([v == slice(big,i:(i+length(v)-1)) for i=1:(length(big)-length(v)+1)])
els
向量中的每个元素给出一个布尔值,指示其是否出现在set
向量中,建议采用以下方法:function vecin(els,set)
res = zeros(Bool,size(els))
res[findin(els,set)]=true
res
end
使用OP中的向量,结果如下:
julia> vecin([4,6],[4,6,5])
2-element Array{Bool,1}:
true
true
julia> issubvec([4,6],[4,6,5])
true
@time
来查看性能是否因过度分配而受到影响是一个好主意。 - Scott Jonesissubvec
显然没有经过优化,@ScottJones,但它的逻辑非常清晰 - 这是我的意图。你编写的函数更好(甚至存在更优化的搜索子字符串/子向量算法)。我认为这样的通用子向量函数可能适合于 Base(与字符串函数类似的名称)。 - Dan Getzissubvec
的逻辑而苦苦挣扎,结合了数组推导和使用slice
和any
函数。这并不是批评,我喜欢看到Julia的数组函数可以做出强大的事情,但是从C/C++/Java等语言转换过来,我必须扭曲我的大脑才能理解它。此外,我发现像那样简短而甜美的代码通常无法扩展,并且我是一个性能专家。 - Scott Jonesin
进行向量化处理:julia> in([4,6,5]).([4, 6])
2-element BitArray{1}:
true
true
并使用all
链来获取您的答案:
julia> all(in([4,6,5]).([4, 6]))
true
目前Julia库中没有标准的函数来确定一个特定的序列是否出现为另一个序列的子序列。可能是因为这实际上是一个相当棘手的问题(也称为字符串搜索算法),如何解决取决于您是否会重复搜索、是否要进行多个匹配、有多个模式、想要模糊匹配等。
这里提供的其他答案都是合理的,但有些过时了,而Julia已经得到了改进,我想提供一个稍微更加惯用的解决方案。
function issubarray(needle, haystack)
getView(vec, i, len) = view(vec, i:i+len-1)
ithview(i) = getView(haystack, i, length(needle))
return any(i -> ithview(i) == needle, 1:length(haystack)-length(needle)+1)
end
这是闪电般的快速,并且几乎不需要内存 - Julia的view
轻巧高效。而且,与Julia一贯的做法一样,解决方案通常只需定义更多的函数。
subset2(x,y)
好,也不够快,但它会返回索引。function findsequence(arr::Array{Int64}, seq::Array{Int64})
indices = Int64[]
i = 1
n = length(seq)
if n == 1
while true
occurrence = findnext(arr, seq[1], i)
if occurrence == 0
break
else
push!(indices, occurrence)
i = occurrence +1
end
end
else
while true
occurrence = Base._searchindex(arr, seq, i)
if occurrence == 0
break
else
push!(indices, occurrence)
i = occurrence +1
end
end
end
return indices
end
julia> @time findsequence(rand(1:9, 1000), [2,3])
0.000036 seconds (29 allocations: 8.766 KB)
16-element Array{Int64,1}:
80
118
138
158
234
243
409
470
539
589
619
629
645
666
762
856
这里是一个更现代化的实现,使用findall
function issubsequence(A, B)
B1inA = findall(isequal(B[1]), A) # indices of the first element of b occuring in a
matchFirstIndex = [] # Saves the first index in A of the occurances
for i in B1inA
if length(A[i:end]) < length(B) continue end
if A[i:i + length(B) - 1] == B
push!(matchFirstIndex, i)
end
end
return matchFirstIndex
end
我得到了与 @daycaster 相似的运行时
@time issubsequence(rand(1:9, 1000), [2,3])
0.000038 seconds (111 allocations: 20.844 KiB)
7-element Vector{Any}:
57
427
616
644
771
855
982
4
和第一个结果组成的元组。 - Dan Getzsubsets
,您可以编写[4,6] in subsets([4,5,6])
。 - GnimucA
的每个元素(不考虑A
作为一个整体序列)是否包含在另一个数组B
中的人来说,setdiff(A, B) |> isempty
就足以完成这项工作。 - Gnimuc