【问题标题】:Reduced permutations减少排列
【发布时间】:2014-08-05 07:04:51
【问题描述】:

考虑以下字符串

abcd

我可以返回 2 个字符排列 (cartesian product) 像这样

$ echo {a,b,c,d}{a,b,c,d}
aa ab ac ad ba bb bc bd ca cb cc cd da db dc dd

但是我想删除多余的条目,例如

ba ca cb da db dc

和无效条目

aa bb cc dd

所以我就剩下了

ab ac ad bc bd cd

Example

【问题讨论】:

    标签: bash awk permutation cartesian-product powerset


    【解决方案1】:

    这是一个纯粹的 bash:

    #!/bin/bash
    
    pool=( {a..d} )
    for((i=0;i<${#pool[@]}-1;++i)); do
        for((j=i+1;j<${#pool[@]};++j)); do
            printf '%s\n' "${pool[i]}${pool[j]}"
        done
     done
    

    还有一个:

    #!/bin/bash
    
    pool=( {a..d} )
    while ((${#pool[@]}>1)); do
        h=${pool[0]}
        pool=("${pool[@]:1}")
        printf '%s\n' "${pool[@]/#/$h}"
    done
    

    它们可以写成函数(或脚本):

    get_perms_ordered() {
        local i j
        for((i=1;i<"$#";++i)); do
            for((j=i+1;j<="$#";++j)); do
                printf '%s\n' "${!i}${!j}"
            done
         done
    }
    

    get_perms_ordered() {
        local h
        while (("$#">1)); do
            h=$1; shift
            printf '%s\n' "${@/#/$h}"
        done
    }
    

    用作:

    $ get_perms_ordered {a..d}
    ab
    ac
    ad
    bc
    bd
    cd
    

    最后一个可以很容易地转换为递归函数以获得给定长度的有序排列(无需替换 - 我使用的是愚蠢的球瓮概率词汇表),例如,

    get_withdraws_without_replacement() {
        # $1=number of balls to withdraw
        # $2,... are the ball "colors"
        # return is in array gwwr_ret
        local n=$1 h r=()
        shift
        ((n>0)) || return
        ((n==1)) && { gwwr_ret=( "$@" ); return; }
        while (("$#">=n)); do
            h=$1; shift
            get_withdraws_without_replacement "$((n-1))" "$@"
            r+=( "${gwwr_ret[@]/#/$h}" )
        done
        gwwr_ret=( "${r[@]}" )
    }
    

    然后:

    $ get_withdraws_without_replacement 3 {a..d}
    $ echo "${gwwr_ret[@]}"
    abc abd acd bcd
    

    【讨论】:

      【解决方案2】:

      您可以使用 awk 过滤掉不需要的条目:

      echo {a,b,c,d}{a,b,c,d} | awk -v FS="" -v RS=" " '$1 == $2 { next } ; $1 > $2 { SEEN[ $2$1 ] = 1 ; next } ; { SEEN[ $1$2 ] =1 } ; END { for ( I in SEEN ) { print I } }'
      

      详细说明:

      echo {a,b,c,d}{a,b,c,d} \
      | awk -v FS="" -v RS=" " '
      
         # Ignore identical values
         $1 == $2 { next }
      
         # Reorder and record inverted entries
         $1 > $2  { SEEN[ $2$1 ] = 1 ; next }
      
                  # Record everything else
                  { SEEN[ $1$2 ] = 1 }
      
         # Print the final list
         END      { for ( I in SEEN ) { print I } }
       '
      

      FS="" 告诉 awk 每个字符都是一个单独的字段。 RS=" " 使用空格分隔记录。

      【讨论】:

        【解决方案3】:

        我确定有人会在一行 awk 中执行此操作,但这是 bash 中的内容:

        #!/bin/bash
        seen=":"
        result=""
        
        for i in "$@"
        do
            for j in "$@"
            do
                if [ "$i" != "$j" ]
                then
                    if [[ $seen != *":$j$i:"* ]]
                    then
                        result="$result $i$j"
                        seen="$seen$i$j:"
                    fi
                fi
            done
        done
        echo $result
        

        输出:

        $ ./prod.sh a b c d
        ab ac ad bc bd cd
        
        $ ./prod.sh I have no life
        Ihave Ino Ilife haveno havelife nolife
        

        【讨论】:

        • 是 bash 还是 sh?你说的话和你的shebang之间有些不一致。
        • @gniourf_gniourf 我按原样测试了它,所以它是 sh。不过,它也适用于 bash。
        • 是的,但是在 bash 中,您可以使用 if [[ $seen != *":$j$i:"* ]] ... 摆脱 grep。另外,使用更多引号,并使用"$@" 而不是$*
        【解决方案4】:

        根据您的限制,这里有一个伪代码来实现这一点,并且 为你的角色使用一个数组:

        for (i=0;i<array.length;i++)
        {
            for (j=i+1;j<array.length;j++)
            {
                print array[i] + array[j]; //concatenation
            }
        }
        

        【讨论】:

          【解决方案5】:

          我意识到我不是在寻找排列,而是在寻找 power set。这里 是 awk 中的一个实现:

          {
            for (c = 0; c < 2 ^ NF; c++) {
              e = 0
              for (d = 0; d < NF; d++)
                if (int(c / 2 ^ d) % 2) {
                  printf "%s", $(d + 1)
                }
              print ""
            }
          }
          

          输入:

          a b c d
          

          输出:

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

          Example

          【讨论】:

            猜你喜欢
            • 2013-06-13
            • 2017-06-30
            • 2019-06-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-08
            • 1970-01-01
            • 2012-12-21
            相关资源
            最近更新 更多