我正在尝试编写一个函数/子程序,它计算大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
答案 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教程。