优化等比例组合筛选Bash脚本,降低计算耗时
优化Bash脚本:按频率等比例筛选14组5数组合
原方法的核心问题
随机试错的无限循环纯纯是浪费算力——100选14的组合数超过3万亿,靠随机抽选刚好匹配频率比例的概率极低,完全是做无用功。coproc在这里也派不上用场,因为当前瓶颈是算法逻辑,不是并行IO或多进程任务,强行用反而会增加复杂度。
优化思路
直接从「频率等比例」的核心要求出发,用统计预计算+贪心筛选+局部调整的逻辑替代随机试错:
- 先统计100组里每个数字的总出现次数,算出14组对应的目标出现次数(原次数 × 14/100,取整后微调保证总和合理)
- 用贪心算法先选14组,每次挑能让当前频率和目标偏差最小的组合
- 最后做局部微调,替换个别组合进一步缩小频率偏差
具体优化脚本
#!/bin/bash # 假设原始100组数据存在文件groups.txt,每行是5个空格分隔的数字 # 示例格式: # 1 3 5 7 9 # 2 4 6 8 10 # ... # 第一步:统计原始100组的数字频率 declare -A freq_orig total_groups=100 target_groups=14 while read -ra nums; do for n in "${nums[@]}"; do ((freq_orig[$n]++)) done done < groups.txt # 第二步:计算每个数字的目标出现次数(等比例缩放) declare -A freq_target total_freq=0 for n in "${!freq_orig[@]}"; do # 四舍五入取整保证比例近似 freq_target[$n]=$(( (freq_orig[$n] * target_groups + total_groups / 2) / total_groups )) ((total_freq += freq_target[$n])) done # 调整总频率,确保等于14×5=70(14组每组5个数) diff=$((70 - total_freq)) while ((diff != 0)); do # 找频率偏差最大的数字调整 max_dev=0 adjust_num="" for n in "${!freq_target[@]}"; do dev=$((freq_orig[$n] * target_groups - freq_target[$n] * total_groups)) if ((diff > 0 && dev > max_dev)) || ((diff < 0 && dev < max_dev)); then max_dev=$dev adjust_num=$n fi done ((freq_target[$adjust_num] += diff > 0 ? 1 : -1)) ((diff += diff > 0 ? -1 : 1)) done # 第三步:贪心筛选初始14组 selected=() declare -A freq_current # 定义计算当前频率偏差的函数(平方和越小越匹配) calc_deviation() { local dev=0 for n in "${!freq_target[@]}"; do local diff=$((freq_current[$n] - freq_target[$n])) ((dev += diff * diff)) done echo $dev } # 循环选14组 while (( ${#selected[@]} < target_groups )); do best_dev=999999 best_group="" # 遍历所有未选中的组 while read -ra nums; do group_str="${nums[*]}" # 跳过已选中的组 if [[ " ${selected[*]} " =~ " $group_str " ]]; then continue fi # 模拟加入该组后的频率偏差 declare -A temp_freq=("${freq_current[@]}") for n in "${nums[@]}"; do ((temp_freq[$n]++)) done temp_dev=0 for n in "${!freq_target[@]}"; do diff=$((temp_freq[$n] - freq_target[$n])) ((temp_dev += diff * diff)) done # 更新最优组 if ((temp_dev < best_dev)); then best_dev=$temp_dev best_group=$group_str fi unset temp_freq done < groups.txt # 选中最优组,更新当前频率 selected+=("$best_group") read -ra nums <<< "$best_group" for n in "${nums[@]}"; do ((freq_current[$n]++)) done done # 第四步:局部调整(可选,进一步缩小偏差) current_dev=$(calc_deviation) for i in "${!selected[@]}"; do # 临时移除当前组 removed_group="${selected[$i]}" read -ra removed_nums <<< "$removed_group" for n in "${removed_nums[@]}"; do ((freq_current[$n]--)) done # 找能让偏差最小的替换组 best_replace_dev=$current_dev best_replace_group="" while read -ra nums; do group_str="${nums[*]}" if [[ " ${selected[*]} " =~ " $group_str " ]]; then continue fi declare -A temp_freq=("${freq_current[@]}") for n in "${nums[@]}"; do ((temp_freq[$n]++)) done temp_dev=0 for n in "${!freq_target[@]}"; do diff=$((temp_freq[$n] - freq_target[$n])) ((temp_dev += diff * diff)) done if ((temp_dev < best_replace_dev)); then best_replace_dev=$temp_dev best_replace_group=$group_str fi unset temp_freq done < groups.txt # 如果找到更好的替换,更新选中组和频率 if [[ -n $best_replace_group && best_replace_dev < current_dev ]]; then selected[$i]="$best_replace_group" read -ra replace_nums <<< "$best_replace_group" for n in "${replace_nums[@]}"; do ((freq_current[$n]++)) done current_dev=$best_replace_dev else # 没找到更好的,恢复原组 for n in "${removed_nums[@]}"; do ((freq_current[$n]++)) done fi done # 输出结果 echo "筛选出的14组:" for group in "${selected[@]}"; do echo "$group" done echo -e "\n频率匹配情况:" for n in $(printf "%s\n" "${!freq_target[@]}" | sort -n); do echo "数字$n: 目标${freq_target[$n]}次,实际${freq_current[$n]:-0}次" done
关键优化点说明
- 完全抛弃随机试错,用统计计算明确目标,再通过贪心+微调快速逼近要求,耗时从“无限长”压缩到毫秒级
- 不需要coproc:当前场景是单进程的计算逻辑,coproc适合处理并行IO或独立子任务,强行用反而会增加进程通信开销
- 用平方和计算频率偏差,确保整体匹配度最优,最后局部调整进一步缩小误差
内容的提问来源于stack exchange,提问作者Benjamin Thuret
相关产品推荐
相关产品推荐

