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