代码之家  ›  专栏  ›  技术社区  ›  Intbes

使用点符号和视图在Julia中递归填充数组

  •  1
  • Intbes  · 技术社区  · 2 年前

    我使用的是Julia 1.9.0,我试图使用视图和点表示法填充一个数组,其中每个条目都取决于前面的条目。比方说,我想要数组中斐波那契级数的前10个条目,所以我初始化前两个条目,并希望在没有for循环的情况下计算其余条目。

    我的方法是:

    v = zeros(Int64, 10)
    v[1:2] .= 1
    
    @views v[3:10] .= v[1:8] .+ v[2:9]
    v
    

    输出

    10-element Vector{Int64}:
     1
     1
     2
     1
     0
     0
     0
     0
     0
     0
    

    但这不起作用,因为显然 v 在计算下一个值之前不会更新。从阅读文档中,我找不出原因,因为据我所知,没有复制数组,也没有创建临时数组。这应该相当于:

    v = zeros(Int64, 10)
    v[1:2] .= 1
    
    @views a, b, c = v[1:8], v[2:9], v[3:10]
    for i in 1:8
      c[i] = a[i] + b[i]
    end
    v
    

    输出

    10-element Vector{Int64}:
      1
      1
      2
      3
      5
      8
     13
     21
     34
     55
    

    这正是我所期望的结果。为什么第一个版本不起作用?如果没有for循环,有可能解决这个问题吗?

    0 回复  |  直到 2 年前
        1
  •  3
  •   Bogumił Kamiński    2 年前

    广播进行别名检测。因此,实际上您的操作会复制数据,因为您有别名。

    要让自己相信这就是发生的事情,请检查以下代码:

    julia> v = zeros(Int64, 10^7);
    
    julia> v[1:2] .= 1;
    
    julia> @allocated v[3:end] .= @views v[1:end-2] .+ v[2:end-1]
    

    此检查使用 Base.mightalias 作用

    去偏是为了保护RHS受到LHS突变的影响。因此,在基本层面上,答案是使用广播无法实现递归。

    但是,您可以创建一个始终返回的自定义类型 false 在里面 Base.mightalias 混叠检查在这种情况下,您所要求的会起作用。

    以下是一个示例:

    julia> using SentinelArrays
    
    julia> v = zeros(Int64, 10^7);
    
    julia> v[1:2] .= 1;
    
    julia> @allocated v[3:end] .= ChainedVector([@view v[1:end-2]]) .+ ChainedVector([@view v[2:end-1]])
    800
    
    julia> v
    10000000-element Vector{Int64}:
                        1
                        1
                        2
                        3
                        5
                        8
                       13
                       21
                       34
                       55
                       89
                      144
                      233
                      377
                      610
                      987
                        ⋮