【问题标题】:Allocate dynamic array with interdependent dimensions分配具有相互依赖维度的动态数组
【发布时间】:2012-01-23 08:00:48
【问题描述】:

这有点复杂;我欢迎任何关于如何提高问题清晰度的 cmets。

好的,假设我有一个数组:

real, allocatable :: A(:,:,:)

我想在使用它之前分配它。第三维度的大小是否可能取决于第二维度的大小?

例如

do i=1,n
allocate(A(3,i,i**2))
end do

显然以上方法行不通。我想最终得到一个具有形状的数组(或一组数组)

(3,1,1), (3,2,4), (3,3,9), ... (3, n, n^2)

其中第三维的大小是第二维大小的平方。

我对依赖维度大小的规则有点复杂,但如果可以平方,我可以做剩下的事情。

这可能吗?如果是这样,我该如何在 Fortran 中实现它?

shape(A) 会返回什么?那会很有趣。

我的另一种选择是分配到所需的最大大小,并注意仅在计算中使用某些元素,即

allocate(A(3,n,n**2))

尽管目前我的记忆力并不强,但我希望有良好的编程习惯。无论如何,这是一个有趣的问题。

谢谢。

编辑:

如果一个维度的大小取决于另一个维度中元素的呢?

在下面的答案中,两个维度的数组大小取决于 B 的索引。我想要一些类似于

的东西
type myarray
    real :: coord(3)
    integer,allocatable :: lev(:)
    integer, allocatable :: cell(:)
endtype myarray

type(myarray), allocatable :: data

allocate(data(m))
allocate(data%lev(n))

forall (j=1:n) !simple now, for argument's sake
    lev(j)=j
endforall

! I was thinking of using a FORALL loop here, but the errors returned 
! suggested that the compiler (gfortran) didn't expect IF blocks and ALLOCATE 
! statements in a FORALL block
do i=1,m
    do j=1,n
        allocate(data(i)%cell(lev(j)**2))
    enddo
enddo

你明白我的意思吗?但是程序在尝试分配已经分配的变量时失败了,例如当i=1 分配data(1)%cell(1),然后尝试分配data(1)%cell(2)...呃哦。我想要的是这样的:

每个data(i) 都有一个lev(j) 值数组,j 从 1 到 n 运行,对于每个 lev(j) 值,我们有一个大小为 lev^2 的 cell。请注意,这些cell 对于每个data(i) 和每个lev 都是唯一的,并且该特定cell 的大小取决于相应的lev 值,也可能取决于相应的data(i)

我必须在派生类型中使用派生类型吗?

【问题讨论】:

  • 您知道,您要查找的数组类型称为“锯齿状”数组,而不是“矩形”数组。下面的IRO-bot有正确答案;在 Fortran 中,数组本身总是矩形的,但您可以使用定义的类型来制作自己的结构。
  • "Jagged"...嘿,这很有道理,从图片上看。 “...您可以使用已定义的类型来创建自己的结构。”真的吗?因此,如果我想要一个数组,它的形状增加到一半然后减小,或者遵循斐波那契数列,或者是完全随机的——这一切都可能不需要太多努力......酷!
  • @SamuelTan 我用更新的代码进一步编辑了我的答案,以解决您的新问题。比较这两个代码,看看你做错了什么。

标签: multidimensional-array fortran dynamic-memory-allocation


【解决方案1】:

我认为你可以简单地通过分配/释放数组来做到这一点

Program test
    Implicit none
    Real, dimension(:,:,:), allocatable :: A
    Integer  :: i,N
    Write(*,*)"Enter N"; Read(*,*)N
    Do i = 1, N
    if(Allocated(A)) then 
    deallocate(A);Allocate(A(i,i,i*i))
    else
    allocate(A(i,i,i*i))
    end if
    Write(*,*)Shape(A)
    End do
end program test

使用 gfortran 编译程序给出:

 Enter N
5
           1           1           1
           2           2           4
           3           3           9
           4           4          16
           5           5          25

【讨论】:

  • 我不确定,但我认为您可能错过了问题的重点。这个问题似乎是在询问非矩形/锯齿状数组,提问者已经提到了拥有最大超矩形并仅使用(小心)那些所需元素的想法。
  • 我认为这是这个问题的答案{第三维度的大小是否可能取决于第二维度的大小? }
  • 第三维的大小是否可能取决于第二维的大小? 那么是还是不是?我在你的回答中看不到这个问题的答案。
  • 我同意这确实是对字面问题的回答。但是,考虑到问题上的 cmets 和接受的答案(并且对问题的解释略有不同),我会读到更多想要的内容:“我可以有一个数组,其形状为 A(:,1,:) (3,1), A(:,2,:) 形状为 (3,4), ..., A(:,n,:) (3,n^2)。
  • 好吧,我同意 francescalus 的观点,我们对这个问题的解释都与你不同(我坚信更接近 OP 的意图)。他(因为他接受了上述答案)实际上是在问对于第二维的不同索引,第三维的大小是否可能不同?
【解决方案2】:

是的,您可以使用派生类型来完成此操作:

TYPE array
  REAL,DIMENSION(:,:,:),ALLOCATABLE :: A
ENDTYPE array

INTEGER :: i
INTEGER,PARAMETER :: n=10

TYPE(array),DIMENSION(:),ALLOCATABLE :: B

ALLOCATE(B(n))

DO i=1,n
  ALLOCATE(B(i)%A(3,i,i*i))
  WRITE(*,*)SHAPE(B(i)%A)
ENDDO

END

这种方法允许数组 B 的每个元素成为不同形状的多维数组。

程序的输出如预期:

        3            1            1
        3            2            4
        3            3            9
        3            4           16
        3            5           25
        3            6           36
        3            7           49
        3            8           64
        3            9           81
        3           10          100

编辑:进一步回答 OP 的编辑问题。是的,您似乎需要做这样的事情,使用嵌套派生类型(比较您的代码示例以找出您做错了什么):

integer,parameter :: m=3,n=5

type cellarray
  integer,dimension(:),allocatable :: c
endtype cellarray

type myarray
  integer,allocatable :: lev(:)
  type(cellarray),dimension(:),allocatable :: cell
endtype myarray

type(myarray),dimension(:),allocatable :: B

allocate(B(m))

! Allocate and assign lev and cell:
do i=1,m
  allocate(B(i)%lev(n))
  allocate(B(i)%cell(n))
  do j=1,n
    B(i)%lev(j)=j
  enddo
enddo

! Based on value of lev, allocate B%cell%c:    
do i=1,m
  do j=1,n
    allocate(B(i)%cell(j)%c(B(i)%lev(j)**2))
  enddo
enddo

! Print out to check that it works:
do j=1,n
  write(*,*)j,B(1)%lev(j),SIZE(B(1)%cell(j)%c)
enddo

end

用 gfortran 4.6.2 试过这个。它产生预期的输出:

       1           1           1
       2           2           4
       3           3           9
       4           4          16
       5           5          25

【讨论】:

  • 它适用于我最近遇到的任何 gfortran(4.1.2 已完全过时)。
  • @VladimirF 感谢您的评论 - 我编辑了我的答案。我没有意识到这一点,因为我几乎没有使用过 gfortran,这就是我们在计算集群上拥有的。我假设我们的系统管理员没有费心更新它。 :)
  • 这是一种安慰。我使用 gfortran,但我担心我可能不得不使用另一个编译器。
  • 如果我们也有 B 可分配的会发生什么?分配是否仍需要一个循环?
  • @SamuelTan 我对 HPC 中的派生类型没有经验 - 您必须自己弄清楚这一点。我认为它不应该明显变慢,记住派生类型只是数据的抽象,它都存储在内存中的一维数组中。作为一个练习,您可以通过两种方式来解决您的问题,看看您是否会在性能上有所不同。但是,我担心的是首先以最简单、最明显的方式实现程序,然后再担心以后的优化。
猜你喜欢
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 2019-07-05
  • 1970-01-01
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多