复数的通用最大/最小函数
在 julia 中,人们可以找到(据称)对实数集合的min/minimum和max/ 的有效实现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 的核心 - 在
maxby1与maxby2分配无关 - 该
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