【问题标题】:Permutation up to N letters using BASH使用 BASH 排列最多 N 个字母
【发布时间】:2019-05-05 19:16:42
【问题描述】:

我想使用 bash 脚本生成给定字符串的 N 个字母排列

我已经成功编写了一个代码来查找给定单词的排列。但是,我似乎无法得到最多 N 个字母的排列。 注意:我必须避免使用 sed 和 awk 命令。 这是我尝试过的:

#!/bin/bash
x=$1
counter=0
ARRAY=()
function permutate {
    if [ "${#1}" = 1 ]; then
       echo "${2}${1}"
    ARRAY+=("${2}${1}")
    else
        for i in $(seq 0 $((${#1}-1)) ); do
            pre="${2}${1:$i:1}"
            seg1="${1:0:$i}"
            seg2="${1:$((i+1))}"
            seg="${seg1}${seg2}"
            permutate "$seg" "$pre"
        done
    fi
}
permutate $x

例如,如果我有一个单词“JACK”并且我想要 3 个字母排列,那么它应该给出: 江淮汽车 开甲 杰克 ETC... 但我似乎无法深究。

【问题讨论】:

  • sed 与这样的任务完全无关,但整个事情应该在 awk 中完成,否则您将花费​​数小时等待 shell 脚本返回一个相当长的单词- 你为什么要避免 awk?
  • 我想知道今天早些时候问stackoverflow.com/questions/55991544/…(关于尝试解决相同问题时遇到的不同问题)的同学是不是?

标签: linux bash unix


【解决方案1】:

字母的排列可以通过大括号展开来获得。示例:

$ echo {A,B}{A,B}
AA AB BA BB

所以我们的想法是稍微利用一下这个大括号扩展。假设你有一个字符串str,那么你可以得到一个大括号展开为:

$ str="JACK"
$ eval echo "{$(echo "$str" | fold -w1 | paste -sd,)}"
J A C K

你可以一步一步看到它的作用:

$ echo "$str" | fold -w1 | paste -sd,
J,A,C,K
  • fold -w1$str$ 的每个字符放在一行中
  • paste -sd, 将所有行合并为一行,中间用逗号分隔。

我们需要这种组合,因为我们不能使用sed。命令eval 最终会强制大括号展开。

现在的关键是重复大括号扩展n次。为此,我们使用printf。如果你有一个字符串"foo",你可以用printf重复它n次,方法如下:

$ printf "foo%.0s" {1..3}
foofoofoo

所以,所有排列,有重复,可以找到:

$ str="JACK"
$ n=3
$ bracestring=$(printf "{$(echo "$str" | fold -w1 | paste -sd,)}%.0s" $(seq 1 $n))
$ eval echo $bracestring
JJJ JJA JJC JJK JAJ JAA JAC JAK JCJ JCA JCC JCK JKJ JKA JKC JKK AJJ AJA AJC AJK AAJ AAA AAC AAK ACJ ACA ACC ACK AKJ AKA AKC AKK CJJ CJA CJC CJK CAJ CAA CAC CAK CCJ CCA CCC CCK CKJ CKA CKC CKK KJJ KJA KJC KJK KAJ KAA KAC KAK KCJ KCA KCC KCK KKJ KKA KKC KKK

【讨论】:

    【解决方案2】:

    使用现有代码打印所有唯一排列。

    for VAR in "${ARRAY[@]}"; do
        echo ${VAR:0:3}
    done
    
    JAC
    JAK
    JCA
    JCK
    JKA
    JKC
    AJC
    AJK
    ACJ
    ACK
    AKJ
    AKC
    CJA
    CJK
    CAJ
    CAK
    CKJ
    CKA
    KJA
    KJC
    KAJ
    KAC
    KCJ
    KCA
    

    【讨论】:

    • 只有当 N 正好比字符串长度小 1 时才有效。
    【解决方案3】:

    下面是使用 Heap's algorithm 的 awk 实现从字符串列表生成 maxLgth 子字符串的排列的开始:

    $ cat npermutations.awk
    function get_perm(A,            i, lgth, sep, str) {
        lgth = length(A)
        lgth = (lgth > maxLgth ? maxLgth : lgth)
        for (i=1; i<=lgth; i++) {
            str = str sep A[i]
            sep = " "
        }
        return str
    }
    
    function swap(A, x, y,  tmp) {
        tmp  = A[x]
        A[x] = A[y]
        A[y] = tmp
    }
    
    function generate(n, A, B,      i) {
        if (n == 1) {
            B[get_perm(A)]
        }
        else {
            for (i=1; i <= n; i++) {
                generate(n - 1, A, B)
                if ((n%2) == 0) {
                    swap(A, 1, n)
                }
                else {
                    swap(A, i, n)
                }
            }
        }
    }
    
    function get_perms(A,B, lgth) {
        lgth = length(A)
        maxLgth = (maxLgth ? maxLgth : lgth)
        generate(lgth, A, B)
    }
    
    ###################
    
    # Input should be a list of strings
    {
        split($0,A)
        delete B
        get_perms(A,B)
        PROCINFO["sorted_in"] = "@ind_str_asc"
        for (perm in B) {
            print perm
        }
    }
    

    例如,使用 sed 将单词转换为 awk 脚本所期望的字符串列表:

    $ echo jack | sed 's/./ &/g' | awk -v maxLgth=3 -f npermutations.awk
    a c j
    a c k
    a j c
    a j k
    a k c
    a k j
    c a j
    c a k
    c j a
    c j k
    c k a
    c k j
    j a c
    j a k
    j c a
    j c k
    j k a
    j k c
    k a c
    k a j
    k c a
    k c j
    k j a
    k j c
    

    如果您确实想要这样做,请转换为 shell,但希望您能看到解决问题的结构。以上使用 GNU awk for sorted_in 对输出进行排序,但您不需要。

    【讨论】:

    • 我不允许使用 awk
    • 你说的对,但我正在向你展示一种在 awk 中执行此操作的算法(因为如果你必须认真执行此操作,那是你真正可以使用的工具),然后你可以将其转换为 shell用于家庭作业。创建 shell 函数而不是 awk 函数,使用 local var 声明局部变量(在调用代码中实际未提供的 awk 函数参数列表中大空格之后的变量)并进行任何其他明显的语法调整以进行转换将 awk 语法转换为 shell 语法。
    【解决方案4】:

    要将每个排列限制为 3 个字符,我们可以使用 grep。对三个字符的限制可能会导致重复。我们使用sort -u 删除这些重复项。

    yourPermutationFunction JACK | grep -Eo '^.{3}' | sort -u
    

    这种方法有点低效,因为它生成所有排列只是为了丢弃其中的一些。但是,由于您使用的是bash 和递归函数,我认为您不太关心效率。

    顺便说一句:命令crunch可以生成单词的排列;可能比任何纯 bash 函数都快得多:

    crunch 0 0 -p JACK | grep -Eo '^.{3}' | uniq
    

    这里我们可以使用uniq 代替sort -u,因为crunch 总是按排序顺序生成排列。
    要抑制crunch 的信息输出,请在第一个| 之前直接添加2&gt;&amp;-

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2015-05-31
      • 2021-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多