复数的通用最大/最小函数

在 julia 中,人们可以找到(据称)对实数集合的min/minimummax/ 的有效实现maximum。由于这些概念不是为复数唯一定义的,我想知道是否已经在某处实现了这些函数的参数化版本。

我目前正在对感兴趣的数组的元素进行排序,然后取最后一个值,据我所知,这比找到具有最大绝对值(或其他值)的值要昂贵得多。

这主要是为了max在复杂数组上重现函数的 Matlab 行为。

这是我当前的代码

a = rand(ComplexF64,4)
b = sort(a,by  = (x) -> abs(x))
c = b[end]

可能的函数调用看起来像

c = maximum/minimum(a,by=real/imag/abs/phase)

使用提供的解决方案编辑Julia 1.5.3 中的一些性能测试

function maxby0(f,iter)
    b = sort(iter,by  = (x) -> f(x))
    c = b[end]
end

function maxby1(f, iter)
    reduce(iter) do x, y
        f(x) > f(y) ? x : y
    end
end

function maxby2(f, iter; default = zero(eltype(iter)))
    isempty(iter) && return default
    res, rest = Iterators.peel(iter)
    fa = f(res)
    for x in rest
        fx = f(x)
        if fx > fa
            res = x
            fa = fx
        end
    end

    return res
end

compmax(CArray) = CArray[ (abs.(CArray) .== maximum(abs.(CArray))) .& (angle.(CArray) .== maximum( angle.(CArray))) ][1]


Main.isless(u1::ComplexF64, u2::ComplexF64) = abs2(u1) < abs2(u2)

function maxby5(arr)
    arr_max = arr[argmax(map(abs, arr))]
end

a = rand(ComplexF64,10)

using BenchmarkTools
@btime maxby0(abs,$a)
@btime maxby1(abs,$a)
@btime maxby2(abs,$a)
@btime compmax($a)
@btime maximum($a)
@btime maxby5($a)

长度为 10 的向量的输出:

>841.653 ns (1 allocation: 240 bytes)
>214.797 ns (0 allocations: 0 bytes)
>118.961 ns (0 allocations: 0 bytes)
>Execution fails
>20.340 ns (0 allocations: 0 bytes)
>144.444 ns (1 allocation: 160 bytes)

长度为 1000 的向量的输出:

>315.100 ?s (1 allocation: 15.75 KiB)
>25.299 ?s (0 allocations: 0 bytes)
>12.899 ?s (0 allocations: 0 bytes)
>Execution fails
>1.520 ?s (0 allocations: 0 bytes)
>14.199 ?s (1 allocation: 7.94 KiB)

长度为 1000 的向量的输出(所有比较都与abs2):

>35.399 ?s (1 allocation: 15.75 KiB)
>3.075 ?s (0 allocations: 0 bytes)
>1.460 ?s (0 allocations: 0 bytes)
>Execution fails
>1.520 ?s (0 allocations: 0 bytes)
>2.211 ?s (1 allocation: 7.94 KiB)

一些评论:

  • 清晰地(并按预期)排序会减慢操作速度
  • 使用abs2可以节省很多性能(也是预期的)

总结:

  • 由于内置函数将在 1.7 中提供此功能,因此我将避免使用其他Main.isless方法,尽管它被认为是性能最佳的方法,但不修改我的 julia 的核心
  • maxby1maxby2分配无关
  • maxby1感觉更可读

因此,获胜者是安德烈·奥斯金(Andrej Oskin)!

使用更正的compmax实现编辑 n°2新基准

julia> @btime maxby0(abs2,$a)
  36.799 ?s (1 allocation: 15.75 KiB)
julia> @btime maxby1(abs2,$a)
  3.062 ?s (0 allocations: 0 bytes)
julia> @btime maxby2(abs2,$a)
  1.160 ?s (0 allocations: 0 bytes)
julia> @btime compmax($a)
  26.899 ?s (9 allocations: 12.77 KiB)
julia> @btime maximum($a)
  1.520 ?s (0 allocations: 0 bytes)
julia> @btime maxby5(abs2,$a)
2.500 ?s (4 allocations: 8.00 KiB)

回答

在 Julia 1.7 中,您可以使用 argmax

julia> a = rand(ComplexF64,4)
4-element Vector{ComplexF64}:
  0.3443509906876845 + 0.49984979589871426im
  0.1658370274750809 + 0.47815764287341156im
  0.4084798173736195 + 0.9688268736875587im
 0.47476987432458806 + 0.13651720575229853im

julia> argmax(abs2, a)
0.4084798173736195 + 0.9688268736875587im

由于达到1.7需要一些时间,可以使用下面的模拟

maxby(f, iter) = reduce(iter) do x, y
                   f(x) > f(y) ? x : y
                 end
julia> maxby(abs2, a)
0.4084798173736195 + 0.9688268736875587im

UPD:找到这种最大值的更有效的方法是使用类似的东西

function maxby(f, iter; default = zero(eltype(iter)))
    isempty(iter) && return default
    res, rest = Iterators.peel(iter)
    fa = f(res)
    for x in rest
        fx = f(x)
        if fx > fa
            res = x
            fa = fx
        end
    end

    return res
end


回答

根据八度的文档(大概是模仿 matlab 的行为):

 For complex arguments, the magnitude of the elements are used for
 comparison.  If the magnitudes are identical, then the results are
 ordered by phase angle in the range (-pi, pi].  Hence,

      max ([-1 i 1 -i])
          => -1

 because all entries have magnitude 1, but -1 has the largest phase
 angle with value pi.

因此,如果您想准确地模拟 matlab/octave 功能,那么基于此逻辑,我将为复数构建一个“max”函数,如下所示:

function compmax( CArray )
    Absmax   = CArray[   abs.(CArray) .== maximum(  abs.(CArray)) ]
    Totalmax = Absmax[ angle.(Absmax) .== maximum(angle.(Absmax)) ]
    return Totalmax[1]
end

(根据需要添加适当的输入)。

例子:

Nums0 = [ 1, 2, 3 + 4im, 3 - 4im, 5 ];   compmax( Nums0 )
# 1-element Array{Complex{Int64},1}:
#  3 + 4im

Nums1 = [ -1, im, 1, -im ];   compmax( Nums1 )
# 1-element Array{Complex{Int64},1}:
#  -1 + 0im


以上是复数的通用最大/最小函数的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>