如何在Fortran代码中将二维数组转换为一维数组?

4

如何将r(i,j)转换为1D数组,以便可以轻松排序数字?

program sort
  implicit none
  character CN*8,O*7
  integer j,iconf,nconf
  integer i,nbins,t
  integer n,nmax,ind,num,b
  parameter (n=216)
  double precision xbox,rq
  parameter (nmax=3091,nconf=1)
  double precision atom(nmax),id(nmax),ox(nmax),oy(nmax),oz(nmax)
  double precision xij,yij,zij,rij
  double precision r(n,n),A(n)
  open(unit=10,status='unknown',file='1000.gro')
   do iconf= 1,nconf
    write(*,*)iconf
     read(10,*)
     read(10,*)
   do i=1,n
     read(10,'(A8,A7,1i5,3f8.3)')CN,O,num,ox(i),oy(i),oz(i)
   enddo
     read(10,*)xbox        ! read the xbox for PBC

  open(unit=3,file='dist.txt')

   do i=1,n
    do j=1,n
   if(i .ne. j) then
   xij=ox(i)-ox(j)
   yij=oy(i)-oy(j)
   zij=oz(i)-oz(j)
   r(i,j)=dsqrt(xij**2 + yij**2 + zij**2)
      write(3,'(i3,2x,i3,4x,f17.15)') i,j, r(i,j)
    endif 
    enddo
    enddo
    enddo
    END

我需要计算距离并将其保存在数组中,数组名为r(i,j)。我想将r(i,j)转换为一维数组,以便于排序。


3
几乎是F77的语法,只有DO..END DO不是F77的。但是,现在询问F77已经没有意义了。 - Steve Lionel
3
自由格式、隐式声明、感叹号用于注释。 - Ian Bush
这里的输入数据是 oxoyoz。您还可以决定将距离 r 存储在长度为 n*(n-1)/2 的一维数组中,索引为“(i-1)*n+j”(其中 i 是两个索引中较小的那个)。 - Pierre de Buyl
1
重塑也是一种选择,但最好编写一个适用于二维数组的小程序,因为底层数据本质上是二维的。但很可能克劳迪娅想要对数组进行排名或索引,而不是对其进行排序,以便保留哪些索引对处于什么距离。但这超出了stackoverflow的任务范围。 - Ian Bush
@lan Bush,如果您能帮我对保留i、j的2D数组进行排序,仅对r(i,j)进行排序。 - claudia smith
5个回答

6
两个之前的答案都回答了如何将一个二维数组转换成一维数组的字面问题。它们使用不同的方法:
  • 将值复制到一个新的一维数组中;
  • 在最终子程序中,将一个一维虚拟参数与一个二维实际参数相关联。
我们可以对这两种方法进行扩展。
在其最简单的形式中, reshape 返回所需的一维数组。作为替代,我们可以说,形式为[x]的数组构造器也可以实现这一点:
real r1d(6), r2d(3,2)
r2d=5.
r1d = [r2d]

当实际参数是数组元素时,数组虚拟参数和包括实际参数及其后面的数组元素处于序列关联状态。
在序列关联中,虚拟数组将被假定为大小或显式形状。由于我们在这种情况下对整个数组感兴趣,因此我们可以直接传递整个数组。
real r2d(3,2)
call sub(r2d)

在哪里

subroutine sub(r1d)
  real r1d(*) ! or r1d(6), etc.
end subroutine

重要的是,对于显式形状和假定大小的虚拟参数,其秩不需要与实际参数匹配。
如前所述,这组方法中的第一组涉及创建新数组。序列关联的使用则不需要。第三种方法也可用:具有指向秩2目标的秩1指针。当然,创建副本也意味着任何更改都不会反映在原始秩2数组中。序列关联和指针将看到传递的更改。
有关其中任何内容的更多详细信息可以在其他问题和答案中找到。
对于排序来说,是否将秩2数组视为秩1数组是另一个考虑。

如何在将2D转换为1D的过程中控制元素的顺序?简单的[]reshape会按列优先的方式给出数组元素。如何按行优先的顺序获取它们。 - Eular
对于一个二维数组,可以使用“转置”来获得元素顺序相反的中间数组;或者在数组构造器中使用更复杂的表达式,例如[(a(i,:),i=1,n)]。当然,如果这样做,复制是可能的结果。 - francescalus

4

你可以将r(1, 1)传递给声明参数为一维数组的子程序。这在Fortran中是合法的(有一些限制不适用于您的代码),并使用了一种称为“序列关联”的功能。


这是一种高级且高效的功能,就像C语言中的指针一样。 - MathArt

3
这似乎是为重塑函数量身定制的:https://gcc.gnu.org/onlinedocs/gfortran/RESHAPE.html 在这个小例子中,reshape首先被用来创建一个二维数组A,然后再次调用reshape以创建一维数组C。
Program reshape_demo
    use, intrinsic :: iso_c_binding

    implicit none
    real(kind=c_float),allocatable :: A(:,:),C(:)
    integer(kind=c_int) :: krow
    allocate(A(3,3))
    A=reshape((/1,2,3,4,5,6,7,8,9/),(/3,3/))
    do krow=1,3
       write(*,fmt="(1p3e10.3)")A(krow,:)
    end do
    C=reshape(A,(/9/))
    write(*,fmt="(9(1x,f4.1))")C
End Program reshape_demo

3
这将复制数组 - 可能不是所需的。事实上,在此过程中可能会复制两份。 - Steve Lionel
1
此外,一个更美观的现代Fortran数组构造器将使用方括号[],而不是`(//)。 - Scientist

0

现在,FORTRAN用户不是很多,因此问题不太常见。接受的答案非常好,并且可以形成一个简单的函数作为解决方案1。但是还有另一种选择,即使用C_F_POINTER而不制作源数组的副本(可以在处理大型数组时获得更高的效率)。

解决方案1:

function downsize(A)result(C)
 real::A(:,:)
 real,allocatable::C(:)
 C=reshape(A,[size(A)])
end function downsize

使用该函数

real,allocatable::C(:); C=downsize(A);

解决方案 2:
ISO Binding 中的 C_F_POINTER 可以用来像 C 语言一样传输内存地址。

USE, INTRINSIC :: ISO_C_BINDING
!EDIT...
integer::i,i1d(9)=[(i,i=1,9)]; !reshape((real([(i,i=1,9)])),[9])
integer,dimension(:,:),pointer::p2d
! Here has to be "Deferred-shape" but it only defined a pointer to an array, not an array of pointers as is always misleading...
!EDIT...
real::r2d(3,3)=reshape([(i*1.0,i=1,9)],[3,3])
real, pointer ::pr1d(:)
!Let's Grow the size from I1D to I2D by the pointer pI2D...
call c_f_pointer(c_loc(i1d),pi2d,[3,3])
!The pointer pi2d is a 2d array with the contents of i1d. Note you cannot deallocate the pointer pi2d - any deallocation has to be of pointee i1d.
!Let's Down the size from R2D to R1D by the pointer pR1D ...
call c_f_pointer(c_loc(r2d), pr1d,[size(r2d)])

实际上,FORTRAN用户并不是很多。我们过去三十年一直在使用Fortran(好吧,我并不是那么老,我只用了不到二十年)。在解决方案2中,我会使用Fortran指针重映射。 - Vladimir F Героям слава
i1d(9)=reshape((real([(i,i=1,9)])),[9]) 这种写法非常复杂,与 i1d(9)=[(i,i=1,9)] 的写法相比,是不是?无论哪种方式,这个示例代码都不是有效的Fortran代码,因为 i1d 不是 c_loc 的有效参数。(正如Vladimir F指出的那样,使用Fortran指针直接完成同样的事情,是否有充分的理由使用C地址?) - francescalus
感谢francescalus!是的,我同意。i1d是从r1d复制的。但我看不出为什么i1d不能用于c_loc。 - MathArt
除非i1d是指针或目标,否则不允许使用c_loc(i1d) - francescalus

0

通过这样做,您将失去有关距离对应的一切信息,但如果您只想要距离,那我就不需要得到所有复杂的答案。为什么不只是这样做:

[....]

    implicit none
    real(8), dimension(:), allocatable:: dist_arr

    allocate(dist_arr(n**2))

    k=0
    do i=1,n
        do j=1,n
            k=k+1
            dist_arr(k)=r(i,j)
        enddo
    enddo

[....]

    

你的意思是 dist_arr 的元素顺序与 r 不同吗?(这样 dist_arr(2) 就匹配 r(1,2) 而不是 r(2,1)。)如果是的话,那么最好说明一下以避免后来的混淆。 - francescalus
@francescalus 这个问题表述不够清晰,但从我所了解的来看,他们只是想要一个距离降序/升序列表。在这种情况下,他们不关心转换过程中的任何相关性,因为他们无论如何都会对这个一维数组进行排序。 因此,最终你想要的是将表格“解开”成一条直线(一维数组)。你可以按行或按列进行,没有关系。 - Monochromatic
如果顺序无关紧要,那么为什么我们会更喜欢这种方法,而不是明显更简单的 dist_arr=[r] 呢? - francescalus
@francescalus,老实说,我之前不知道这个存在。在我多年编写Fortran程序的经历中,我总是更喜欢走“弯路”,而不是使用预先构建的函数,主要是因为我希望以后能够进行微调。但是没错,就像我上面解释的那样,他们只是想要一个易于排序的一维数组,而不是二维表格,所以这应该可以很好地工作。 - Monochromatic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接