嵌套循环是把一个循环放入另一个循环体中的结构。外层循环先固定一种选择,内层循环再枚举下一种选择,适合系统检查多种数量组合、坐标组合和参数组合。
本文用一个找零问题实践嵌套循环:顾客支付100元,购买5元商品后需要找零95元。假设商家拥有数量不限的50元、20元、10元、5元和1元纸币,并且只比较各面额的数量,不区分纸币交付顺序。
本篇先让程序完整枚举五种纸币的数量组合,下一篇《算法初识》再讨论如何减少不必要的枚举。
本文代码
! 嵌套循环枚举95元找零的纸币组合。
! 假设各面额数量不限,且不区分纸币发放顺序。
program nestedLoopChangeDemo
implicit none
integer :: changeAmount
integer :: fiftyCount
integer :: twentyCount
integer :: tenCount
integer :: fiveCount
integer :: oneCount
integer :: totalAmount
integer :: solutionCount
integer :: resultUnit
integer :: ioStatus
changeAmount = 95
solutionCount = 0
open (newunit=resultUnit, file="result.txt", status="replace", &
action="write", iostat=ioStatus)
if (ioStatus /= 0) error stop "无法打开result.txt"
write (resultUnit,"(A,I3,A)") "找零金额:", changeAmount, " 元"
write (resultUnit,"(A)") "前12种方案:"
! 每一层循环固定一种面额的数量,再进入下一层。
do fiftyCount = 0, changeAmount / 50
do twentyCount = 0, (changeAmount - 50 * fiftyCount) / 20
do tenCount = 0, (changeAmount - 50 * fiftyCount - &
20 * twentyCount) / 10
do fiveCount = 0, (changeAmount - 50 * fiftyCount - &
20 * twentyCount - 10 * tenCount) / 5
do oneCount = 0, (changeAmount - 50 * fiftyCount - &
20 * twentyCount - 10 * tenCount - 5 * fiveCount)
totalAmount = 50 * fiftyCount + 20 * twentyCount + &
10 * tenCount + 5 * fiveCount + oneCount
if (totalAmount == changeAmount) then
solutionCount = solutionCount + 1
if (solutionCount <= 12) then
write (resultUnit,"(A,I3,A,I3,A,I3,A,I3,A,I3)") &
"50元=", fiftyCount, " 20元=", twentyCount, &
" 10元=", tenCount, " 5元=", fiveCount, &
" 1元=", oneCount
end if
end if
end do
end do
end do
end do
end do
write (resultUnit,"(A,I3,A)") "全部方案数量:", solutionCount, " 种"
close (resultUnit)
end program nestedLoopChangeDemo
代码结构
- changeAmount保存待找零金额95;solutionCount保存已经找到的方案数量。
- 五层do循环分别枚举50元、20元、10元、5元和1元纸币的数量,外层循环固定一种面额后,内层循环继续枚举剩余面额。
- 每一层循环的上限都根据剩余金额计算,因此不会枚举已经超过95元的组合。
- 最内层固定oneCount后,程序计算totalAmount;金额恰好等于95时,才把方案数量加1。
- resultUnit负责打开、写入和关闭result.txt,程序正常运行时不在终端输出结果。
- solutionCount <= 12只限制写入的示例数量,不限制统计数量;程序仍会继续遍历并统计全部方案。
编译运行
(base) hong@hongdeMacBook-Pro 025.nestedLoop % gfortran -Wall -Wextra -std=f2018 exampleNestedLoop.f90
(base) hong@hongdeMacBook-Pro 025.nestedLoop % ./a.out
(base) hong@hongdeMacBook-Pro 025.nestedLoop % cat result.txt
找零金额: 95 元
前12种方案:
50元= 0 20元= 0 10元= 0 5元= 0 1元= 95
50元= 0 20元= 0 10元= 0 5元= 1 1元= 90
50元= 0 20元= 0 10元= 0 5元= 2 1元= 85
50元= 0 20元= 0 10元= 0 5元= 3 1元= 80
50元= 0 20元= 0 10元= 0 5元= 4 1元= 75
50元= 0 20元= 0 10元= 0 5元= 5 1元= 70
50元= 0 20元= 0 10元= 0 5元= 6 1元= 65
50元= 0 20元= 0 10元= 0 5元= 7 1元= 60
50元= 0 20元= 0 10元= 0 5元= 8 1元= 55
50元= 0 20元= 0 10元= 0 5元= 9 1元= 50
50元= 0 20元= 0 10元= 0 5元= 10 1元= 45
50元= 0 20元= 0 10元= 0 5元= 11 1元= 40
全部方案数量:294 种
结果分析
程序运行时没有把结果直接打印到终端,而是把示例方案和最终数量写入result.txt。使用cat result.txt可以查看文件内容。
五层循环完整遍历了所有满足上限的非负整数数量组合,最终得到294种方案。下一篇会把其中最后两层循环替换为数学计数公式,比较两种算法的结构。
知识点总结
- 嵌套循环适合枚举多个离散变量的组合。
- 每一层循环可以对应一个面额、一个坐标维度或一个独立选择。
- 根据剩余金额限制循环上限,可以减少无效组合。
- 方案计数必须明确是否区分排列顺序;本例只区分各面额数量。
- 程序可以把结果写入文本文件,供后续检查、保存或继续处理。