【问题标题】:why plus operator is vectorized in Julia but not the division operator?为什么加号运算符在 Julia 中被矢量化,而不是除法运算符?
【发布时间】:2020-12-09 10:11:24
【问题描述】:

我对 Julia 很陌生,我对 Julia 中的向量化操作很好奇。

例子:

x = [2 3 4]
y = [3 4 5]

如果我运行上面的代码:

x + y

我明白了:

1×3 Array{Int64,2}:
 5  7  9

但如果我这样做:

x/y

我明白了:

1×1 Array{Float64,2}:
 0.7600000000000002

我无法理解,经过一番研究,我发现 x./y 是进行矢量化操作的正确方法,但我想知道加号是如何工作的。

这是我的问题:

这背后的直觉是什么(使加号矢量化而不是除法运算符)?

当我执行 x/y 时会发生什么?为什么 .76000 会作为答案出现。

我希望这不是重复的,如果是请告诉我。我将删除它。首先十分感谢。请帮忙。

【问题讨论】:

    标签: julia


    【解决方案1】:

    AFAICT + 算子不会自动广播:

    julia> 1 + [2, 3]
    ERROR: MethodError: no method matching +(::Int64, ::Array{Int64,1})
    For element-wise addition, use broadcasting with dot syntax: scalar .+ array
    
    julia> [1 2] + [3, 4]
    ERROR: DimensionMismatch("dimensions must match: a has dims (Base.OneTo(1), Base.OneTo(2)), b has dims (Base.OneTo(2),), mismatch at 1")
    

    你要分别写:

    julia> 1 .+ [2, 3]
    2-element Array{Int64,1}:
     3
     4
    
    julia> [1 2] .+ [3, 4]
    2×2 Array{Int64,2}:
     4  5
     5  6
    

    但重要的是,+ 是对完全相同维度的数组的有效操作(在数学意义上),这就是它起作用的原因:

    julia> [2 3 4] + [3 4 5]
    1×3 Array{Int64,2}:
     5  7  9
    

    请注意,这不是广播,而只是线性代数。

    现在关于x / y,您可以检查它是否定义为:

    help?>  x / y
      /(x, y)
    
      Right division operator: multiplication of x by the inverse of y on the right.
    

    所以你有:

    julia> using LinearAlgebra
    
    julia> x * pinv(y)
    1×1 Array{Float64,2}:
     0.7600000000000005
    

    (实际的实现有点不同——我在这里使用了一种简化的方法)

    【讨论】:

      【解决方案2】:

      + 在任何数组上下文中都是明确的。它按元素操作,没有其他常见的解释。如果数组不是通用大小,则会引发错误。

      *matrix 上下文(二维数组)中是明确的。它指的是矩阵乘法。第一个输入的第二个维度的长度必须与第二个输入的第一个维度的长度匹配,否则会引发错误。

      但是,对于一般数组,* 是不明确的。例如,如果xy 是数字向量(一维数组),那么x*y 是什么意思?也许您想要按元素执行操作,或者您想要内积?还是张量积?出于这个原因,* 如果使用两个向量作为输入调用,将引发错误。请注意,.* 是明确的,因为根据广播的定义,它只意味着元素乘法。

      这与/ 有何关系?请注意/ 是右除法运算符,即x 乘以y 的逆或伪逆(其中yx 的右侧)。所以* 的语义在这里适用,附加约束是y 必须能够被反转(或者,至少,Moore-Penrose 反转)。如果满足这些约束,则/ 是明确的并因此被定义。如果他们不满意,那么/ 将抛出一个错误(至少应该如此):-)

      这个操作绝对不是元素,这就是为什么.//在两个向量的情况下是不同的。

      现在,牢记所有这些,开始您的示例。首先,请注意您实际上并没有定义向量。向量定义为[1,2]。但是你正在做[1 2] 这实际上是一个矩阵。所以操作[1 2] / [3 4] 等价于[1 2] * pinv([3 4])。这里定义了伪逆,因为[1 2] 是1x2,pinv([3 4]) 是2x1,所以结果将是一个1x2 x 2x1 数组,也就是一个1x1 矩阵,这正是你得到的。

      请注意,[1,2] / [3,4] 返回一个 2x2 矩阵。这也是有道理的,因为 [1,2] 隐含 2x1,而 pinv([3,4]) 是 1x2,所以输出将是 2x1 x 1x2 或更确切地说是 2x2 矩阵。

      【讨论】:

      • y 不必倒置。使用 AFAICT 伪逆。不幸的是,/ 的文档字符串有点不精确,例如rand(3) / rand(3) 不会出错,因为两个向量的列数相同(即 1)。
      • @BogumiłKamiński 是的,我刚刚在提交答案后检查了 REPL,并意识到允许伪逆:-) 我正在修复我的答案
      • @PKumar 不用担心。我现在已经解决了所有问题,并解释了在给定输入的伪逆维度的情况下您可以期待什么样的输出。如果您认为我已经回答了问题,请随时单击我的答案旁边的勾号以标记已回答的问题。干杯。
      【解决方案3】:

      注意:这个解释是错误的,请阅读评论。我把它放在这里了,以后不会有人说这样的话了。

      也许语言创造者可以给出更好的答案,但据我所知,最初,大部分操作都是矢量化的。当广播被发明时,大多数矢量化版本的运算符都从 Base 中删除,除非它包括对 Base 的大规模重写(或者如果它以某种方式影响性能,我不知道)。当然,加法是比除法更常见的运算,所以就不用了。

      【讨论】:

      • 不,标准不是重写。我们留下了线性代数所需的向量化运算,即那些对数组有明确标准数学定义的运算。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-08
      • 1970-01-01
      • 2022-08-17
      • 1970-01-01
      • 2020-08-09
      相关资源
      最近更新 更多