簡體   English   中英

Julia 成對廣播

[英]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 矩陣。

順便說一句,您很少會在代碼中使用broadcastbroadcast(==, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM