一个典型的枚举问题,并不复杂。本文给出一个 Fortran 代码。
组合数量为:238905 个;运行耗时为:50 毫秒。
算法要点:
A. 确定组合的最大数量。按照最小数组成,用等差数列求和来预估:ia(ia+1)/2 = km;ia² < ia²+ia = 2km;ia < sqrt(2*km)。
B. 计算并传递和值累加的中间值 b,大于 km 的,提前退出本层递归,减少重复计算,提高速度。
C. 保存组合的数组 a 设置为公共变量,不在递归过程中传递。
Fortran 代码:
$freeform
! 递归枚举:列出正整数1到32和值为100的所有组合
! 2023-08-22
! szw_sh@163.com
! CVF编译项:DF /Ox
module aa ! 定义公共变量
parameter(kk=32,km=100,ia=(km*2)**0.5) ! 常数:kk,最大数;km,和值;ia,最大个数
integer a(0:ia) ! 数组:a,保存组合
data m/0/
end module
use aa ! 主程序
a=0 ! a赋初值
call cc(0,1) ! 调用递归主程序cc
k=log10(m*1.0)+1
write(*,'(/5x,a,i<k>)') 'total = ',m ! 输出组合的总数
end
recursive subroutine cc(b,n) ! 子程序,递归枚举
use aa
integer b ! b用于传递和值计算的中间值,减少重复运算
do i=a(n-1)+1,kk ! 可用数从a(n-1)+1开始
a(n)=i ! 赋值a(n)
k=b+i ! 计算和值
if(k.gt.km) then ! 和值大于km,跳出本层递归
exit
else if(k.eq.km) then ! 和值相符,计数、输出
m=m+1
write(*,'(i10.6,a,<ia>i3)') m,' ==> ',a(1:n)
else
if(n.lt.ia) call cc(k,n+1) ! 未达到ia层,继续递归
end if
end do
end
附:计算结果
限于篇幅,只列出头尾部分
000001 ==> 1 2 3 4 5 6 7 8 9 10 11 12 22
000002 ==> 1 2 3 4 5 6 7 8 9 10 11 13 21
000003 ==> 1 2 3 4 5 6 7 8 9 10 11 14 20
000004 ==> 1 2 3 4 5 6 7 8 9 10 11 15 19
000005 ==> 1 2 3 4 5 6 7 8 9 10 11 16 18
000006 ==> 1 2 3 4 5 6 7 8 9 10 12 13 20
000007 ==> 1 2 3 4 5 6 7 8 9 10 12 14 19
000008 ==> 1 2 3 4 5 6 7 8 9 10 12 15 18
000009 ==> 1 2 3 4 5 6 7 8 9 10 12 16 17
000010 ==> 1 2 3 4 5 6 7 8 9 10 13 14 18
000011 ==> 1 2 3 4 5 6 7 8 9 10 13 15 17
000012 ==> 1 2 3 4 5 6 7 8 9 10 13 32
000013 ==> 1 2 3 4 5 6 7 8 9 10 14 15 16
000014 ==> 1 2 3 4 5 6 7 8 9 10 14 31
000015 ==> 1 2 3 4 5 6 7 8 9 10 15 30
000016 ==> 1 2 3 4 5 6 7 8 9 10 16 29
000017 ==> 1 2 3 4 5 6 7 8 9 10 17 28
000018 ==> 1 2 3 4 5 6 7 8 9 10 18 27
000019 ==> 1 2 3 4 5 6 7 8 9 10 19 26
000020 ==> 1 2 3 4 5 6 7 8 9 10 20 25
000021 ==> 1 2 3 4 5 6 7 8 9 10 21 24
000022 ==> 1 2 3 4 5 6 7 8 9 10 22 23
000023 ==> 1 2 3 4 5 6 7 8 9 11 12 13 19
000024 ==> 1 2 3 4 5 6 7 8 9 11 12 14 18
000025 ==> 1 2 3 4 5 6 7 8 9 11 12 15 17
000026 ==> 1 2 3 4 5 6 7 8 9 11 12 32
000027 ==> 1 2 3 4 5 6 7 8 9 11 13 14 17
000028 ==> 1 2 3 4 5 6 7 8 9 11 13 15 16
000029 ==> 1 2 3 4 5 6 7 8 9 11 13 31
000030 ==> 1 2 3 4 5 6 7 8 9 11 14 30
000031 ==> 1 2 3 4 5 6 7 8 9 11 15 29
000032 ==> 1 2 3 4 5 6 7 8 9 11 16 28
000033 ==> 1 2 3 4 5 6 7 8 9 11 17 27
000034 ==> 1 2 3 4 5 6 7 8 9 11 18 26
000035 ==> 1 2 3 4 5 6 7 8 9 11 19 25
000036 ==> 1 2 3 4 5 6 7 8 9 11 20 24
000037 ==> 1 2 3 4 5 6 7 8 9 11 21 23
000038 ==> 1 2 3 4 5 6 7 8 9 12 13 14 16
000039 ==> 1 2 3 4 5 6 7 8 9 12 13 30
000040 ==> 1 2 3 4 5 6 7 8 9 12 14 29
000041 ==> 1 2 3 4 5 6 7 8 9 12 15 28
000042 ==> 1 2 3 4 5 6 7 8 9 12 16 27
000043 ==> 1 2 3 4 5 6 7 8 9 12 17 26
000044 ==> 1 2 3 4 5 6 7 8 9 12 18 25
000045 ==> 1 2 3 4 5 6 7 8 9 12 19 24
000046 ==> 1 2 3 4 5 6 7 8 9 12 20 23
000047 ==> 1 2 3 4 5 6 7 8 9 12 21 22
000048 ==> 1 2 3 4 5 6 7 8 9 13 14 28
000049 ==> 1 2 3 4 5 6 7 8 9 13 15 27
000050 ==> 1 2 3 4 5 6 7 8 9 13 16 26
000051 ==> 1 2 3 4 5 6 7 8 9 13 17 25
000052 ==> 1 2 3 4 5 6 7 8 9 13 18 24
000053 ==> 1 2 3 4 5 6 7 8 9 13 19 23
000054 ==> 1 2 3 4 5 6 7 8 9 13 20 22
000055 ==> 1 2 3 4 5 6 7 8 9 14 15 26
...... ........................
238870 ==> 20 21 27 32
238871 ==> 20 21 28 31
238872 ==> 20 21 29 30
238873 ==> 20 22 26 32
238874 ==> 20 22 27 31
238875 ==> 20 22 28 30
238876 ==> 20 23 25 32
238877 ==> 20 23 26 31
238878 ==> 20 23 27 30
238879 ==> 20 23 28 29
238880 ==> 20 24 25 31
238881 ==> 20 24 26 30
238882 ==> 20 24 27 29
238883 ==> 20 25 26 29
238884 ==> 20 25 27 28
238885 ==> 21 22 25 32
238886 ==> 21 22 26 31
238887 ==> 21 22 27 30
238888 ==> 21 22 28 29
238889 ==> 21 23 24 32
238890 ==> 21 23 25 31
238891 ==> 21 23 26 30
238892 ==> 21 23 27 29
238893 ==> 21 24 25 30
238894 ==> 21 24 26 29
238895 ==> 21 24 27 28
238896 ==> 21 25 26 28
238897 ==> 22 23 24 31
238898 ==> 22 23 25 30
238899 ==> 22 23 26 29
238900 ==> 22 23 27 28
238901 ==> 22 24 25 29
238902 ==> 22 24 26 28
238903 ==> 22 25 26 27
238904 ==> 23 24 25 28
238905 ==> 23 24 26 27
total = 238905