简体   繁体   English

bash中的数组元素所有组合

[英]Array elements all combinations in bash

I need help to find all combinations (sum) of array elements in bash. here is an excerpt from the code:我需要帮助来查找 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

This finds the single pairs and the double pairs.这会找到单对和双对。 What is missing are the three-pairs (eg 31+(-41)+59), the combinations of four...and so on.缺少的是三对(例如 31+(-41)+59)、四对的组合……等等。 I don't want to hard code it, because the number of elements can change in my program.我不想对其进行硬编码,因为元素的数量可能会在我的程序中发生变化。

For help I would be very grateful.如有帮助,我将不胜感激。 Thank you.谢谢你。

As commented by others, we have 1023 combinations for 10 numbers, excluding the empty set.正如其他人评论的那样,我们有 10 个数字的 1023 种组合,不包括空集。 These combinations can be associated with the bit pattern between 0000000001 and 1111111111 .这些组合可以与00000000011111111111之间的位模式相关联。 Then would you please try:那么请你试试:

#!/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

First few lines of the output: output 的前几行:

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

It will print 1023 lines in total.它将总共打印 1023 行。

One awk idea using a recursive function:一个awk使用递归 function 的想法:

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]=$1 }

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

NOTE: since we perform all operations with a single awk call we can reduce the total run time from ~13 seconds ( bash looping construct) to ~0.1 second ( awk )注意:由于我们使用单个awk调用执行所有操作,因此我们可以将总运行时间从约 13 秒( bash循环构造)减少到约 0.1 秒( awk

This generates:这会产生:

      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

If your memory can hold it, double the array with each new item by replicating it with the new item added.如果您的 memory 可以容纳它,则通过使用添加的新项目复制它来将每个新项目的数组加倍。 This solution relies on including the empty set (with sum 0 ) for initializiation.该解决方案依赖于包含空集(总和为0 )来进行初始化。 To exclude it, strip it off at the end.要排除它,请在最后将其剥离。

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

The same approach including the steps of calculation:同样的方法包括计算步骤:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM