调整循环生成的数组大小

时间:2016-02-05 01:28:42

标签: fortran dynamic-arrays fortran95

让我们有一个数组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复杂的循环。 谢谢。

2 个答案:

答案 0 :(得分:3)

这非常简单:

b = pack(a,a<0.0)

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

(最新的Fortran标准提供了与pack函数文档中的示例非常相似的操作。)

答案 1 :(得分:1)

如果不希望将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()这样的实用程序例程,它将可分配数组作为参数并更改其大小。如果效率确实很重要,最好在allocate()中明确使用move_alloc()resize()来消除一个临时数组。
  • 上面的代码需要相对较新的编译器,它支持在左侧自动重新分配可分配数组。
  • 如果您使用英特尔Fortran,则需要添加-assume realloc_lhs选项。如果B的大小变得相当大,您还需要-heap-arrays选项。对于gfortran或Oracle fortran,不需要特殊选项。
  • 我们应该在左侧附加冒号;即,B(:) = [ B, m ]是NG
  • 有关动态数据的更多详细信息(例如,增长率),请参阅Wiki页。