Bash: 求数组的幂集
Bash: Finding Powerset of array
我需要一个 Bash 脚本来获得一个包含 n 个元素的数组作为输入和 return 该数组的幂集。
所以 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...
有什么想法吗?
它可以像在任何其他编程语言中一样直接完成,方法是将每个子集解释为二进制数,其中每个位表示是否选择了相应的元素。对于 n 元素集,您从 0 数到 2n -1 并取 j-th 项进入 i-th 子集当且仅当 j-th 位在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'
假设您的数组是一组 "simple" 个元素,有一个有趣的解决方案涉及 eval
动态生成的大括号表达式。
$ array=(a b c d)
$ printf -v br "{%s,}" "${array[@]}"
$ echo $br
{a,}{b,}{c,}{d,}
$ eval "printf '%s\n' $br"
(它省略了每个 powerset 的一部分的空字符串,但您可以在之后手动添加它。)
这个有效:
$ 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' "$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>
注意:它适用于 "complex" 项的值,只是不要使用 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=''
后的注释符号 (#)。
在早期答案的基础上使用 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
我需要一个 Bash 脚本来获得一个包含 n 个元素的数组作为输入和 return 该数组的幂集。
所以 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...
有什么想法吗?
它可以像在任何其他编程语言中一样直接完成,方法是将每个子集解释为二进制数,其中每个位表示是否选择了相应的元素。对于 n 元素集,您从 0 数到 2n -1 并取 j-th 项进入 i-th 子集当且仅当 j-th 位在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'
假设您的数组是一组 "simple" 个元素,有一个有趣的解决方案涉及 eval
动态生成的大括号表达式。
$ array=(a b c d)
$ printf -v br "{%s,}" "${array[@]}"
$ echo $br
{a,}{b,}{c,}{d,}
$ eval "printf '%s\n' $br"
(它省略了每个 powerset 的一部分的空字符串,但您可以在之后手动添加它。)
这个有效:
$ 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' "$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>
注意:它适用于 "complex" 项的值,只是不要使用 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=''
后的注释符号 (#)。
在早期答案的基础上使用 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