[英]Julia pairwise broadcast
我想比較 Julia 中字符串列表中的每一對字符串。 一種方法是
equal_strs = [(x == y) for x in str_list, y in str_list]
但是,如果我按如下方式使用廣播:
equal_strs = broadcast(==, str_list, str_list)
它返回一個向量而不是二維數組。 有沒有辦法使用broadcast
output 二維數組?
廣播通過擴展(“廣播”)長度不同的維度來工作,其方式是使用 NxKx1 廣播的(例如)大小為 Nx1xM 的數組給出 NxKxM 數組。
這意味着如果你廣播一個長度為 N 個向量的操作,你將得到一個長度為 N 個向量。
所以你需要一個字符串數組是一個長度為 N 的向量,另一個是 1xM 矩陣:
julia> using Random
julia> str1 = [randstring('A':'C', 3) for _ in 1:5]
5-element Vector{String}:
"ACC"
"CBC"
"AAC"
"CAB"
"BAB"
1.8.0> str2 = [randstring('A':'C', 3) for _ in 1:4]
4-element Vector{String}:
"ABB"
"BAB"
"CAA"
"BBC"
1.8.0> str1 .== permutedims(str2)
5×4 BitMatrix:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 1 0 0
permutedims
會將長度為 N 的向量更改為 1xN 矩陣。
順便說一句,您很少會在代碼中使用broadcast
( broadcast(==, a, b)
),而是使用點語法, a.== b
,這更慣用。
您應該為廣播機器轉置一個向量,以通過擴展輸入的維度來構建矩陣以達成一致。
julia> str_list = ["hello", "car", "me", "good", "people", "good"];
julia> equal_strs = broadcast(==, str_list, permutedims(str_list))
6×6 BitMatrix:
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 1
0 0 0 0 1 0
0 0 0 1 0 1
此外,以下類似。
equal_strs = str_list .== permutedims(str_list)
equal_strs = isequal.(str_list, permutedims(str_list))
將假設“列表”是指向量,因為 Julia 中沒有類似 python 的列表。 如果您的意思是一個元組,我建議將其轉換為 Vector ,因為廣播最好與 Arrays (Vector 是其子類型)一起使用。
str_list = ["one", "two", "three", "one", "two"]
現在你只需做
broadcast(==, str_list, permutedims(str_list))
或更簡潔的點運算符
str_list .== permutedims(str_list)
引擎蓋下會發生什么:
在 Julia 中廣播按元素工作,所以如果你有 2 個向量,它不會做任何事情,因為尺寸匹配。
但是如果你有一個向量和一個矩陣(向量是一個一維數組,矩陣是一個二維數組) ,形狀為(N,1)
和(1,N)
Julia 將廣播1
維給你一個形狀矩陣(N,N)
這就是你想要的。
現在通常用數字你會做'
而不是permutedims
num_list .== num_list'
至於為什么它不適用於字符串,請參閱此答案。
正如其他答案所建議的那樣, lst.== permutedims(lst)
是一種非常好的查找結果的方法。 但它需要 O(n^2) 比較,如果列表很長,使用 O(n*log(n)) 比較算法可能會更好。 以下是帶有一點基准的算法的實現:
function equal_str(lst)
sp = sortperm(lst)
isp = invperm(sp)
same = [i==1 ? false : lst[sp[i]]==lst[sp[i-1]] for i=1:length(lst)]
ac = accumulate((k,v)-> ifelse(v==false, k+1, k), same; init=0)
return [ ac[isp[i]]==ac[isp[j]] for i=1:length(lst),j=1:length(lst) ]
end
基准給出:
julia> using Random
julia> using BenchmarkTools
julia> lst = [randstring('A':'C',3) for i=1:40];
julia> show(lst)
["CBA", "CAB", "BCA", "AAC", "AAA", "ABC", "BBA", "CAB", "CBC", "CCA",
"BCC", "BCB", "CAB", "BCB", "ACC", "CBC", "CCC", "CCB", "BCB", "BCB",
"ABA", "AAC", "CCC", "ABC", "BAC", "CAB", "BAB", "BCB", "CCA", "CAC",
"AAA", "BBC", "ABC", "BCB", "CBA", "CAA", "CAB", "CAC", "CBC", "CBC"]
julia> @btime $lst .== permutedims($lst) ;
9.025 μs (5 allocations: 4.58 KiB)
julia> @btime equal_str($lst) ;
6.112 μs (8 allocations: 3.08 KiB)
lst
越大,差異越大。 正如 OP 所建議的那樣,這僅適用於將列表與其自身進行比較。 為了比較兩個列表,應該在 O(n*log(n)) 時間內使用不同的算法。
最后,即使這個算法通過排序也有點太難了,但是 O(n^2) 時間/空間復雜度是產生結果的內在因素。
更新:更線性的 O(n) 時間計算(仍然 O(n^2) 來制作矩陣):
function equal_str_2(lst)
d = Dict{String,Int}()
d2 = Dict{Int, Vector{Int}}()
for p in pairs(lst)
if haskey(d,p[2])
push!(d2[d[p[2]]],p[1])
else
d[p[2]] = p[1]
d2[p[1]] = [p[1]]
end
end
res = zeros(Bool, (length(lst), length(lst)))
for p in values(d2)
for q in Iterators.product(p,p)
res[q[1],q[2]] = true
res[q[2], q[1]] = true
end
end
return res
end
並使用更大的lst
進行基准測試:
julia> lst = [randstring('A':'C',3) for i=1:140];
julia> @btime $lst .== permutedims($lst) ;
99.094 μs (5 allocations: 6.89 KiB)
julia> @btime equal_str($lst) ;
51.981 μs (9 allocations: 23.12 KiB)
julia> @btime equal_str_2($lst) ;
21.539 μs (72 allocations: 27.47 KiB)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.