Bash:查找数组

时间:2015-09-17 21:15:08

标签: bash permutation

我需要一个Bash脚本,它将获得一个包含n个元素作为输入的数组,并返回该数组的powerset。

因此对于array=(a, b, c, d),输出应为

a
ab
abc
abcd
abd
ac
acd
b
bc
bcd
c
cd
d

请注意,任何元素都不应重复(aab,accd,abbc无效),abc与cba相同(顺序不重要)。

我找到的类似问题的每个解决方案都给出了固定长度(长度为2或3的组合)或允许重复(如aacd),即使对于其他语言(不是我可以用其他语言做很多......)

我想出了这个:

string='a b c d'
read -a array <<< "$string"
count="${#array[@]}"
level=0
for (( level = 0; level < $count; level++ )); do
  for (( i = $level; i < $count; i++ )); do
    output+=" ${array[$i]}"
    echo $output
  done
output=''
done

我的输出是

a
a b
a b c
a b c d
b
b c
b c d
c
c d
d

它缺少一些条目,如ac,ad,abd ......

有什么想法吗?

4 个答案:

答案 0 :(得分:7)

通过将每个子集解释为二进制数,其可以直接在任何其他编程语言中完成,其中每个位指示是否选择了相应的元素。对于 n 元素集,从0到2 n -1计数并取 j -th当且仅当 i 的二进制表示中的 j 位置位时,才将项置于 i 子集中。

#! /bin/bash

items=(a b c d)
n=${#items[@]}
powersize=$((1 << $n))

i=0
while [ $i -lt $powersize ]
do
    subset=()
    j=0
    while [ $j -lt $n ]
    do
        if [ $(((1 << $j) & $i)) -gt 0 ]
        then
            subset+=("${items[$j]}")
        fi
        j=$(($j + 1))
    done
    echo "'${subset[@]}'"
    i=$(($i + 1))
done

输出:

''
'a'
'b'
'a b'
'c'
'a c'
'b c'
'a b c'
'd'
'a d'
'b d'
'a b d'
'c d'
'a c d'
'b c d'
'a b c d'

答案 1 :(得分:4)

这有效:

$ p(){ eval echo $(printf "{%s,}" "$@"); }
$ p a b c d
abcd abc abd ab acd ac ad a bcd bc bd b cd c d

或者,更便携:

p() {
        [ $# -eq 0 ] && { echo; return; }
        ( shift; p "$@" ) | while read a ; do printf '%b' "$1$a\n$a\n"; done
}
p "$@"

将其称为此(如果您想要一行,请使用echo):

$ echo $(p a b c d e)
abcde bcde acde cde abde bde ade de abce bce ace ce abe be ae e abcd bcd acd cd abd bd ad d abc bc ac c ab b a<br>

注意:它适用于项目的“复杂”值,只是不要使用echo。

$ p " a b " "-c d-" "gg hh"
 a b -c d-gg hh
-c d-gg hh
 a b gg hh
gg hh
 a b -c d-
-c d-
 a b 

并且,基于每个值的二进制表示的非递归选项(尽管较慢)。然而,它主要是bash,因为它广泛使用算术扩展。

#!/bin/bash

powerset(){
[[ $# -eq 0 ]] && { echo "Missing set of arguments" >&2; exit 2; }
local n ns; (( n=$#, ns=1<<n ))

for (( i=1; i<ns ; i++ )); do
    a=''; # printf "%4.4s " "$i"
    for (( j=1; j<=n; j++ )); do
    (( i & 1<<(j-1) )) && a="${a}""${!j}" ;
    done
    echo "$a"
done
}

powerset "$@"

如果您需要编号结果,请删除a=''后的注释符号(#)。

答案 2 :(得分:3)

假设你的数组是一组“简单”元素,有一个有趣的解决方案,涉及eval动态生成的大括号表达式。

$ array=(a b c d)
$ printf -v br "{%s,}" "${array[@]}"
$ echo $br
{a,}{b,}{c,}{d,}
$ eval "printf '%s\\n' $br"

(它省略了空字符串,它将成为每个powerset的一部分,但你可以在之后手动添加。)

答案 3 :(得分:2)

使用eval

建立在早期答案的基础上
eval echo $(sed 's/./{&,}/g' <<< "abcd") | tr ' ' '\n' | sort

会给你

a
ab
abc
abcd
abd
ac
acd
ad
b
bc
bcd
bd
c
cd
d