能否对以下Fortran彩票算法程序进行优化?
当然可以优化这段Fortran代码!我先梳理下原代码的核心逻辑——生成多组无重复的随机整数,然后从可读性、效率、健壮性几个维度来给出优化方案:
1. 优化变量命名,提升代码可读性
原代码的变量名(比如RAN4、N129)虽然能猜出大致用途,但更直观的命名能让代码更容易维护和理解。同时移除未使用的冗余变量:
program Lotto implicit none integer :: i, rand_idx integer, dimension(4) :: unique_4nums ! 替代原RAN4 integer, dimension(9) :: num_pool_9 ! 替代原N129(假设是1-9的候选池) integer, dimension(5) :: lotto_5nums ! 替代原LOTTO5 real :: r integer :: start_time, end_time, clock_rate ! 补充完整计时变量(如果需要)
2. 用洗牌法替代循环重试,大幅提升生成无重复随机数的效率
原代码用DO...CYCLE的方式重复生成随机数直到得到不重复值,这种方法在候选范围小、要选的数量多的时候,重试概率会越来越高,效率很低。洗牌法是更高效的方案:先创建包含所有候选数的数组,打乱顺序后取前N个,一次就能得到无重复结果。
比如生成4个1-9的无重复数:
! 初始化候选池为1-9 num_pool_9 = [(i, i=1, 9)] ! 洗牌操作(Fisher-Yates算法) do i = size(num_pool_9), 2, -1 call random_number(r) rand_idx = 1 + int(i * r) ! 生成1到当前i的随机索引 ! 交换当前位置和随机位置的元素 num_pool_9([i, rand_idx]) = num_pool_9([rand_idx, i]) end do ! 取前4个作为结果 unique_4nums = num_pool_9(1:4)
Fisher-Yates洗牌的时间复杂度是O(n),无论选多少个无重复数,效率都很稳定。
3. 优化随机数种子,提升随机性
原代码的call random_seed()默认初始化,部分编译器会使用固定种子,导致每次运行生成相同的随机序列。可以用系统时间来初始化种子,保证每次运行的随机性:
integer, dimension(:), allocatable :: seed integer :: seed_size, seed_val call random_seed(size=seed_size) allocate(seed(seed_size)) ! 用系统时钟生成初始种子 call system_clock(count=seed_val) seed = seed_val + 37 * [(i-1, i=1, seed_size)] ! 扩散种子值,避免全0 call random_seed(put=seed) deallocate(seed)
4. 结构化代码,封装通用逻辑
把“生成无重复随机数”的逻辑封装成子过程,方便复用和维护:
subroutine gen_unique_randoms(pool_size, pick_count, result_arr) implicit none integer, intent(in) :: pool_size, pick_count integer, intent(out) :: result_arr(pick_count) integer :: i, rand_idx integer, dimension(pool_size) :: pool real :: r ! 初始化候选池 pool = [(i, i=1, pool_size)] ! 洗牌 do i = pool_size, 2, -1 call random_number(r) rand_idx = 1 + int(i * r) pool([i, rand_idx]) = pool([rand_idx, i]) end do ! 提取结果 result_arr = pool(1:pick_count) end subroutine gen_unique_randoms
主程序里直接调用即可:
call gen_unique_randoms(9, 4, unique_4nums) call gen_unique_randoms(49, 5, lotto_5nums) ! 假设乐透是从49个数里选5个,可按需调整
5. 补充计时逻辑(如果需要)
如果需要统计代码运行时间,可以用system_clock实现:
call system_clock(start_time, clock_rate) ! 这里放要计时的代码,比如生成随机数的逻辑 call gen_unique_randoms(9, 4, unique_4nums) call gen_unique_randoms(49, 5, lotto_5nums) call system_clock(end_time) print *, "生成随机数耗时: ", real(end_time - start_time)/real(clock_rate), "秒"
优化后的完整代码示例
program Lotto implicit none integer :: start_time, end_time, clock_rate integer, dimension(4) :: unique_4nums integer, dimension(5) :: lotto_5nums ! 初始化随机数种子 call init_random_seed() ! 计时开始 call system_clock(start_time, clock_rate) ! 生成无重复随机数 call gen_unique_randoms(9, 4, unique_4nums) call gen_unique_randoms(49, 5, lotto_5nums) ! 计时结束并输出 call system_clock(end_time) print *, "4个1-9的无重复随机数: ", unique_4nums print *, "5个1-49的无重复随机数: ", lotto_5nums print *, "总耗时: ", real(end_time - start_time)/real(clock_rate), "秒" contains subroutine init_random_seed() implicit none integer, dimension(:), allocatable :: seed integer :: seed_size, seed_val call random_seed(size=seed_size) allocate(seed(seed_size)) call system_clock(count=seed_val) seed = seed_val + 37 * [(i-1, i=1, seed_size)] call random_seed(put=seed) deallocate(seed) end subroutine init_random_seed subroutine gen_unique_randoms(pool_size, pick_count, result_arr) implicit none integer, intent(in) :: pool_size, pick_count integer, intent(out) :: result_arr(pick_count) integer :: i, rand_idx integer, dimension(pool_size) :: pool real :: r pool = [(i, i=1, pool_size)] do i = pool_size, 2, -1 call random_number(r) rand_idx = 1 + int(i * r) pool([i, rand_idx]) = pool([rand_idx, i]) end do result_arr = pool(1:pick_count) end subroutine gen_unique_randoms end program Lotto
内容的提问来源于stack exchange,提问作者F2HMcDonnell
相关产品推荐
相关产品推荐

