Julia 复数的一般最大/最小函数

Julia 复数的一般最大/最小函数,julia,Julia,在julia中,人们可以(假设)在实数集合上找到min/最小值和max/最大值的高效实现。 由于这些概念不是为复数唯一定义的,我想知道这些函数的参数化版本是否已经在某处实现了 我目前正在对感兴趣数组的元素进行排序,然后取最后一个值,据我所知,这比查找具有最大绝对值的值(或其他值)要昂贵得多 这主要是为了在复杂阵列上重现max函数的Matlab行为 这是我目前的代码 a = rand(ComplexF64,4) b = sort(a,by = (x) -> abs(x)) c = b[en

在julia中,人们可以(假设)在实数集合上找到
min
/
最小值
max
/
最大值
的高效实现。
由于这些概念不是为复数唯一定义的,我想知道这些函数的参数化版本是否已经在某处实现了

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

这主要是为了在复杂阵列上重现
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)
长度为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
进行所有比较):

一些评论:

  • 排序明显(如预期)会减慢操作速度
  • 使用
    abs2
    可以节省大量性能(预期也是如此)
总结如下:

  • 由于内置函数将在1.7中提供此功能,因此我将避免使用附加的
    Main.isless
    方法,尽管它被认为是性能最好的方法,但不修改我的内核
  • maxby1
    maxby2
    不分配任何内容
  • maxby1
    的可读性更强
因此,获胜者是安德烈·奥斯金

使用校正后的
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
复数的“大小”由其模数的大小决定。你可以用腹肌。或者像上面说的那样得到1.7


如果这是我计算的代码,我会通过以下方式让我的生活简单得多:

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

julia> maximum(rand(ComplexF64, 10))
0.9876138798492835 + 0.9267321874614858im
julia>Main.isless(u1::ComplexF64,u2::ComplexF64)=abs2(u1)最大值(兰特(复杂度64,10))
0.98761387984892835+0.9267321874614858im

这为
Main
中的现有方法添加了一个新的实现。因此,对于库代码来说,这不是一个优雅的想法,但它会让您以最少的努力到达您需要的地方。

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

因此,如果您想精确模拟matlab/倍频程功能,那么基于此逻辑,我将为复数构造一个“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

虽然复数的“大小”(即其范数)是唯一定义的,但对复数进行排序并不重要。您也可以使用广播而不是地图。出于性能方面的原因,abs2比abs好,因为平方根的计算成本很高arr[argmax(abs2.(arr))]`@竹诚实地问,为什么不呢?在我看来,按量级/大小/abs对常规旧数字进行排序的自然方法也适用于复杂的情况。这不是真的有数学上的原因吗?你是在暗示“按字典顺序”对复数进行排序更合适吗?@blorgon I也倾向于使用复数的大小。然而,这里的讨论让我非常信服。而且,由于实现数量级的函数可能同样困难,为什么不让用户根据需要进行排序呢?如果在您的用例中,按
abs
对实数进行排序是有意义的,那么按norm对复数进行排序就可以了。不过,您可能对排序()的稳定性感兴趣。您的意思是,
argmax
当前没有将函数作为参数处理的方法?另外,将
dims
选项传递给该
maxby
的正确方法是什么?是的,1.5(我认为是1.6)中没有函数作为参数。我认为,dims最简单的方法是迭代Cartesianindice。对不起,我自己从来没有这样做过。虽然你的实现在你的示例上工作得很好,但它似乎对随机选择的复数没有系统性的工作…@你是对的,我在逻辑上有一个错误(将最大角度应用于完整数组,而不是已经达到最大大小的子集)。现在修好了。虽然很明显,根据公认的答案,如果您不关心matlab兼容行为,那么遵循此逻辑并不特别重要。:)比方说,我宁愿有可能有一个默认行为,但能够在需要时改变它。在matlab中,在这种比较中实际上没有提到相位,尽管matlab可能也有这样的行为。@是的,在他们的文档中倍频程更清楚。Matlab“暗示”这是它在提供的示例中所做的,但并没有太明确地表明这是他们的规范……只是补充了一下,与其他规范相比不是很有效,但比我自己的规范要好:)
julia> arr_max = arr[argmax(map(abs, arr))]
0.9695470502095325 + 0.9978696209350371im
julia> arr_min = arr[argmin(map(abs, arr))]
0.12749588414783353 + 0.09918182087026373im
julia> Main.isless(u1::ComplexF64, u2::ComplexF64) = abs2(u1) < abs2(u2)

julia> maximum(rand(ComplexF64, 10))
0.9876138798492835 + 0.9267321874614858im
 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.
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