更多二项式系数和FORTRAN 95

时间:2014-10-18 04:33:31

标签: fortran memoization

我正在尝试编写一个函数/子程序,它计算大n和k的二项式系数(n选择k)。几天前,我发布了一个子程序,它运行正常,但十进制错误很小。问题似乎是由于划分了非常大的数字。无论如何,我认为尝试递归算法可能会更好,因为添加可能没有这个问题。另外,考虑到涉及大量记忆,将是必要的。这是我在统计语言R中编写的一个函数,它看起来效果很好,

 options("scipen"=100)
 mm <- matrix(0,100,100)
 combo <- function (n,k) {
       if (k==1) {
       return(n)
       }
       if (k==n) {
       return(1)
       }
       if (mm[n,k] != 0) {
       return(mm[n,k])
       }
       if (k!=1 & n!=k) {
       ans <<- combo(n-1,k) + combo(n-1,k-1)
       mm[n,k] <<- ans
       return(combo(n-1,k) + combo(n-1,k-1))
       }
 }
 combo(40,20)

我需要能够在FORTRAN 95中做同样的事情。这是没有记忆的递归代码,工作正常

 program fctrecur
 implicit none
 integer (kind=8) i,j,combo

 print*,"What is n?"
 read*,i

 print*,"What is k?"
 read*,j

 print*,combo(i,j)
 end  

 recursive function combo(n,k) result(cmb)
 implicit none
 integer (kind=8) n,k,cmb
 if (k .EQ. n) then
    cmb = 1
 endif
 if (k .EQ. 1) then
    cmb = n
 endif
 if ((k .NE. 1) .AND. (k .NE. n)) then
    cmb = combo(n-1,k-1) + combo(n-1,k)
 endif      
 end function

我的问题是......是否可以修改上述FORTRAN程序以包含记忆?我试图模仿R函数,但似乎没有任何效果。谢谢,杰里

弗拉基米尔F提出的模块建议非常好......但我无法将其作为内部功能。这是代码

 program fctrecur
 implicit none
 integer, parameter :: iknd = selected_int_kind(18)
 integer(kind=iknd) :: mm(100,100)
 integer (kind=iknd) i,j,combo

 print*,"What is n?"
 read*,i

 print*,"What is k?"
 read*,j

 print*,combo(i,j)
 end

 recursive function combo(n,k) result(cmb)
 integer, parameter :: iknd = selected_int_kind(18)
 integer(kind=iknd) :: cmb
 integer(kind=iknd), intent(in) :: n,k

if (k == n) then
    cmb = 1
else if (k == 1) then
    cmb = n
else if (mm(n,k) /=0)  then
        print*,"hello"
    cmb = mm(n,k)
else if ((k /= 1) .and. (k /= n)) then
    cmb = combo(n-1,k-1) + combo(n-1,k)
    mm(n,k) = cmb
end if      

 end function

,它给出以下错误 “赋值语句的左侧必须是该行的变量或函数结果” mm(n,k)= cmb。即使我在函数中声明数组mm它也不起作用(它可以工作,但忽略了memoization,“mm(n,k)= cmb”

我明白了......

 program fctrecur
 implicit none
 integer i,j,mm(50,50)

 print*,"What is n?"
 read*,i

 print*,"What is k?"
 read*,j

 print*,combo(i,j)

 contains
 recursive function combo(n,k) result(cmb)
 implicit none
 integer cmb
 integer, intent(in) :: n,k

 if (k == n) then
    cmb = 1
 else if (k == 1) then
    cmb = n
 else if (mm(n,k) /=0)  then
        print*,"hello"
    cmb = mm(n,k)
 else if ((k /= 1) .and. (k /= n)) then
    cmb = combo(n-1,k-1) + combo(n-1,k)
    mm(n,k) = cmb
 end if      

 end function
 end program

我现在只使用常规INTEGER ...将调整以允许更大的n和k。

这是适用于相当大的n和k

的最终代码

主程序

 program fctrecur
 use binom
 implicit none
 integer i,j

 print *, "What is n?"
 read *, i

 allocate(mm(i,i))
 mm = 0.D0

 print *, "What is k?"
 read *, j

 print *,combo(i,j)

 end

模块

 module binom
 implicit none

 integer, parameter :: iknd = selected_real_kind(31)
 real(iknd), allocatable ::  mm(:,:)

 contains

 recursive function combo(n,k) result(cmb)
 real (kind=iknd) :: cmb
 integer, intent(in) :: n,k
    if (k == n) then
    cmb = real(1,16)
 else if (k == 1) then
    cmb = real(n,16)
 else if (mm(n,k) /=0)  then
    cmb = mm(n,k)
 else if ((k /= 1) .and. (k /= n)) then
    cmb = combo(n-1,k-1) + combo(n-1,k)
    mm(n,k) = cmb
 end if      
 end function

 end module

1 个答案:

答案 0 :(得分:1)

您的R代码中存在严重错误。你正在计算所有东西两次(虽然这里的memoization有帮助)! 你应该使用:

   ans <<- combo(n-1,k) + combo(n-1,k-1)
   mm[n,k] <<- ans
   return(ans)

它在Fortran中看起来如何。这是一个完全直截了当的翻译。

module binom
  implicit none

  integer, parameter :: iknd = selected_int_kind(36)
  ! 36 is necessary for very large n and k, integer(kind=8) wasn't enough!

  integer(iknd) :: mm(100,100)

contains

  recursive function combo(n,k) result(cmb)
    integer(kind=iknd) :: cmb
    integer(kind=iknd), intent(in) :: n,k

    if (k == n) then
        cmb = 1
    else if (k == 1) then
        cmb = n
    else if (mm(n,k) /=0)  then
        cmb = mm(n,k)
    else if ((k /= 1) .and. (k /= n)) then
        cmb = combo(n-1,k-1) + combo(n-1,k)
        mm(n,k) = cmb
    end if      
  end function

end module

program fctrecur
  use binom

  implicit none

  integer(kind=iknd) i,j

  print *, "What is n?"
  read *, i

  print *, "What is k?"
  read *, j

  print *,combo(i,j)
  print *,combo(i,j)

end

注意函数在模块中,您也可以在程序内部定义它并将数组mm移动到主程序中。外部函数是过去的残余,并不是新代码的首选。内部和模块程序接收&#34;显式接口&#34;这有助于发现错误并启用许多高级传递机制。

在最终代码中,你想要数组可分配,并且你想确保你没有超出界限,但是你也没有在R中这样做,所以我为你省略了。

请注意selected_int_kind以便携方式定义种类,kind=8不可移植,强烈建议不要使用。在您的情况下,kind=8实际上太低,不允许大n接近100。

您可以使用更易读的操作符==/=

请注意,条件的结构应该使用else if来完成,因为您在各个分支中没有任何立即return语句。

一般来说,我建议你阅读一些很好的现代Fortran教程。