【问题标题】:Resize array generated by the loop调整循环生成的数组
【发布时间】:2016-02-05 01:28:42
【问题描述】:

让我们有一个数组 A(:,:)

Real, Allocatable:: A(:,:), B(:) 
Integer, Allocatable:: rowin(:) 
Integer:: m,j,k, nl

Allocate(A(max1,max2))

由 m,j,k 上的几个循环定义

 nl = 0

 A(rowin(m)+j,k) = ...

现在我想选择 A(:,:) 的子集,使它们的值为负并将它们存储到数组 B(:) 未知长度中,即

if(A(rowin(m)+j,k).lt.0.d0) then
  nl = nl + 1
  B(nl) = A(rowin(m)+j,k)               
end if

由于 max1 和 max2 是非常大的数字,我不想分配长度为 max1*max2 的 B(:),但正好是 A(:,:) 包含负值的长度。另外,我不想再次在 m、j 和 k 上进行复杂的循环。 谢谢。

【问题讨论】:

  • 您真的需要 Fortran 90 或 95 还是允许 Fortran 2003(答案使用的自动重新分配来自 Fortran 2003,非常有用)。
  • 两者都是可能的(首选 Fortran 90/95),但需要处理循环和索引,因为@roygvib 给出了他的答案。

标签: fortran dynamic-arrays fortran95


【解决方案1】:

这很简单:

b = pack(a,a<0.0)

a 的负元素打包到rank-1 数组b 中。使用最近的编译器b 将在分配期间自动分配到正确的大小。

(最新的 Fortran 标准在 pack 函数的文档中给出了一个非常相似的操作示例。)

【讨论】:

    【解决方案2】:

    如果不希望将B 预先分配到某个固定大小,我们可以通过使用可分配数组的自动重新分配来模拟动态数组。在最简单的情况下,我们可以通过添加一个新值来逐个元素地扩展B。例如,假设我们对mj 有双重循环,选择与给定条件匹配的值,并将这些值添加到B。在这里,B 的大小从 0、1、2、... 自动增长。

    integer, allocatable :: B(:)
    integer :: m, j
    
    allocate( B( 0 ) ) !! start from an empty array
    
    do m = 1, 10000
    do j = 1, 10000
    
        !! Some condition to add an element, e.g.
        if ( m == j .and. mod( m, 1000 ) == 0 ) then
    
            !! Add a new element to B.
            B = [ B, m ]
        endif
    enddo
    enddo
    
    print *, "size( B ) = ", size( B )
    print *, B(:)
    

    结果变成

    size( B ) =  10
    1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
    

    在您的情况下,您可能可以以类似的方式添加所需的 A 值,例如

    allocate( B( 0 ) )
    
    do m = 1, ...
    do j = 1, ...
    do k = 1, ...
        if ( A( rowin(m)+j, k ) < 0.d0 ) then   !! or any condition
            B = [ B, A(rowin(m)+j,k) ]
        end if
    enddo
    enddo
    enddo
    

    这种方法的一个缺点是效率非常低,因为每次添加新元素时都会重新分配B。虽然这对于小的B 来说没有问题,但对于大的B 来说可能会成为一个严重的瓶颈。在这种情况下,可以几何地增加B 的大小而不是增加1。例如,以下代码根据需要将B 的大小加倍,以减少重新分配的次数。

    integer, allocatable :: B(:)
    integer :: m, j, nl, p
    
    allocate( B( 0 ) ) !! start from an empty array
    nl = 0             !! number of elements found
    
    do m = 1, 10000
    do j = 1, 10000
    
        if ( m == j .and. mod( m, 1000 ) == 0 ) then
    
            nl = nl + 1
    
            !! Expand B if the size becomes insufficient.
            if ( size(B) < nl ) B = [ B, ( 0, p=1,nl ) ]
    
            !! Add a new value.
            B( nl ) = m 
        endif
    enddo
    enddo
    
    B = B( 1 : nl )  !! adjust B to the optimal size
    

    还有一些注意事项:

    • 如果经常使用类似的方法,可以方便地编写像resize() 这样的实用程序例程,它将可分配数组作为参数并更改其大小。如果效率真的很重要,最好在resize() 中显式使用allocate()move_alloc() 来消除一个临时数组。
    • 上述代码需要相对较新的编译器,支持自动重新分配左侧的可分配数组。
    • 如果您使用 Intel fortran,则需要添加 -assume realloc_lhs 选项。如果B 的大小可以变得相当大,您还需要-heap-arrays 选项。对于 gfortran 或 Oracle fortran,不需要特殊选项。
    • 我们应该在左侧附加冒号;即,B(:) = [ B, m ] 是 NG
    • 有关动态数组的更多详细信息(例如,增长率),请参阅Wiki 页面。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-21
      • 2012-02-24
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 2012-04-04
      • 1970-01-01
      相关资源
      最近更新 更多