bash 中的数组元素所有组合

Array elements all combinations in bash

我需要帮助来查找 bash 中数组元素的所有组合(总和)。 这是代码的摘录:

    #!/bin/bash
array=("31" "-41" "59" "26" "-53" "58" "97" "-93" "-23" "84") # min 1 vlaue
arrLength=("${#array[@]}")
for (( i=0; i < $arrLength; i++))
do
    #echo "$i"
    for (( j=$i; j > 0; j-- ))
    do
        summ=$(( array[j] ))
        bak=$((array[0] + summ))
        echo "$summ ; $bak"
    done
    echo "_____________________________"
done

这会找到单对和双对。缺少的是三对(例如 31+(-41)+59)、四对的组合……等等。我不想对其进行硬编码,因为元素的数量在我的程序中可能会发生变化。

如有帮助,我将不胜感激。谢谢。

根据其他人的评论,我们有 10 个数字的 1023 种组合,不包括 空集。这些组合可以与位模式相关联 在 00000000011111111111 之间。那你试试看:

#!/bin/bash

array=("31" "-41" "59" "26" "-53" "58" "97" "-93" "-23" "84")
n=${#array[@]}                          # number of elements
for (( i = 1; i < (1 << n); i++ )); do  # loop between 1 and 1023
    sum=0; list=()                      # initialize variables
    for (( j = 0; j < n; j++ )); do     # loop over the bit slice position
        if (( (1 << j) & i )); then     # if the bit is set
            (( sum += ${array[j]} ))    # then pick the element to sum
            list+=("${array[j]}")       # append to an array to report
        fi
    done
    (IFS=,; printf "(%s) = %d\n"  "${list[*]}" "$sum")
                                        # report the sum of the set
done

输出的前几行:

(31) = 31
(-41) = -41
(31,-41) = -10
(59) = 59
(31,59) = 90
(-41,59) = 18
(31,-41,59) = 49
<snip>

总共会打印1023行。

一个 awk 使用递归函数的想法:

array=("31" "-41" "59" "26" "-53" "58" "97" "-93" "-23" "84")

printf "%s\n" "${array[@]}" |
awk '
function find_sum(i, sum, label,    j) {

      printf "%8s = %s\n",sum,label

      for (j=i+1;j<=FNR;j++)
          find_sum(j, sum+item[j], label " + " item[j])
}

    { item[FNR]= }

END { for (i=1;i<=FNR;i++)
          find_sum(i, item[i], item[i])
    }
'

注意: 因为我们通过单个 awk 调用执行所有操作,所以我们可以将总 运行 时间从 ~13 秒(bash 循环结构) ~0.1 秒 (awk)

这会生成:

      31 = 31
     -10 = 31 + -41
      49 = 31 + -41 + 59
      75 = 31 + -41 + 59 + 26
      22 = 31 + -41 + 59 + 26 + -53
      80 = 31 + -41 + 59 + 26 + -53 + 58
     177 = 31 + -41 + 59 + 26 + -53 + 58 + 97
      84 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + -93
      61 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + -93 + -23
     145 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + -93 + -23 + 84
     168 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + -93 + 84
     154 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + -23
     238 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + -23 + 84
     261 = 31 + -41 + 59 + 26 + -53 + 58 + 97 + 84
     -13 = 31 + -41 + 59 + 26 + -53 + 58 + -93

... snip ...

      35 = 58 + -23
     119 = 58 + -23 + 84
     142 = 58 + 84
      97 = 97
       4 = 97 + -93
     -19 = 97 + -93 + -23
      65 = 97 + -93 + -23 + 84
      88 = 97 + -93 + 84
      74 = 97 + -23
     158 = 97 + -23 + 84
     181 = 97 + 84
     -93 = -93
    -116 = -93 + -23
     -32 = -93 + -23 + 84
      -9 = -93 + 84
     -23 = -23
      61 = -23 + 84
      84 = 84

如果您的记忆可以容纳它,请通过添加新项目复制它来将每个新项目的数组加倍。此解决方案依赖于包含空集(总和 0)来进行初始化。要排除它,请在最后将其删除。

a=(31 -41 59 26 -53 58 97 -93 -23 84)
b=(0)
for i in ${a[@]}; do for j in ${b[@]}; do b+=( $((i+j)) ); done; done
$ echo ${#b[@]}
1024

$ printf '%s\n' ${b[@]}
0
31
-41
-10
59
:
:
86
155
186
114
145

相同的方法包括计算步骤:

a=(31 -41 59 26 -53 58 97 -93 -23 84)
b=("0 = 0")
for i in ${a[@]}; do for j in "${b[@]}"; do b+=("${j%=*}+ $i = $((i+${j#*=}))"); done; done
$ echo ${#b[@]}
1024

$ printf '%s\n' "${b[@]}"
0 = 0
0 + 31 = 31
0 + -41 = -41
0 + 31 + -41 = -10
0 + 59 = 59
:
:
0 + 31 + -41 + 26 + -53 + 58 + 97 + -93 + -23 + 84 = 86
0 + 59 + 26 + -53 + 58 + 97 + -93 + -23 + 84 = 155
0 + 31 + 59 + 26 + -53 + 58 + 97 + -93 + -23 + 84 = 186
0 + -41 + 59 + 26 + -53 + 58 + 97 + -93 + -23 + 84 = 114
0 + 31 + -41 + 59 + 26 + -53 + 58 + 97 + -93 + -23 + 84 = 145