调整循环生成的数组大小
Resize array generated by the loop
让我们有一个数组 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) = ...
现在我想 select 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 进行复杂的循环。
谢谢。
这很简单:
b = pack(a,a<0.0)
会将 a
的负元素打包到 rank-1 数组 b
中。使用最近的编译器 b
将在分配期间自动分配到正确的大小。
(最新的 Fortran 标准在 pack
函数的文档中给出了非常相似的操作作为示例。)
如果不希望将 B
预分配到某个固定大小,我们可以通过使用可分配数组的自动重新分配来模拟动态数组。在最简单的情况下,我们可以通过添加新值来逐个元素地扩展B
。例如,假设我们对匹配给定条件的 m
和 j
、select 值进行双重循环,并将这些值添加到 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 页。
让我们有一个数组 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) = ...
现在我想 select 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 进行复杂的循环。 谢谢。
这很简单:
b = pack(a,a<0.0)
会将 a
的负元素打包到 rank-1 数组 b
中。使用最近的编译器 b
将在分配期间自动分配到正确的大小。
(最新的 Fortran 标准在 pack
函数的文档中给出了非常相似的操作作为示例。)
如果不希望将 B
预分配到某个固定大小,我们可以通过使用可分配数组的自动重新分配来模拟动态数组。在最简单的情况下,我们可以通过添加新值来逐个元素地扩展B
。例如,假设我们对匹配给定条件的 m
和 j
、select 值进行双重循环,并将这些值添加到 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 页。