【问题标题】:How can I efficiently generate an array of all possible alphabetical combinations of a given length of letters?如何有效地生成给定字母长度的所有可能字母组合的数组?
【发布时间】:2014-08-01 16:51:16
【问题描述】:

想象一下,我有一袋 26 个拼字游戏 - 一个对应英文字母表中的每个字母。

我的目标是创建一个由所有可能的字符串组成的数组,最长为 n 个字母。说n=3

约束:

  1. 字母必须始终按字母顺序排列(ABC,而不是 CBA;组合,而不是排列)
  2. 字符串的长度必须是 n 字母(允许算法在给定长度下跳出任何循环)
  3. 字母不能重复(A,不是AA

如何在 PHP 中最有效地生成这个数组?

换句话说,我如何避免暴力循环遍历所有可能的组合并过滤掉那些不符合上述规则的组合?


如果我的字母表只包含 3 个字母 — $alphabet = range('a','c'); — 我希望输出一个包含 7 个项目 (3C1+3C2+3C3) 的数组:[A,B,C,AB,AC,BC,ABC]

如果我的字母表只包含 4 个字母 — $alphabet = range('a','d'); — 我希望输出一个包含 15 个项目 (4C1+4C2+4C3+4C4) 的数组:[A,B,C,D,AB,AC,AD,BC,BD,CD,ABC,ABD,ACD,BCD,ABCD]。但如果我只想限制字符串 <= 3 字母长,那么我会忽略 ABCD 导致只有 14 个项目 (4C1+4C2+4C3)。


$alphabet = range('a','z');

print_r(generate_strings($alphabet,1));
// expected output: A,B,C,...Z

print_r(generate_strings($alphabet,2));
// expected output: A..Z, AB..AZ, BC..BZ, CD, ..YZ

print_r(generate_strings($alphabet,3));
// expected output: A..Z, AB..YZ, ABC..XYZ

print_r(generate_strings($alphabet,10));
// expected output: A .. JKLMN .. AGKQRZ .. QRSTUVWXYZ
//                        ^         ^          ^10 character max, no repeats
//                        |         still alphabetical order
//                        alphabetical order

function generate_strings($alphabet,$max_word_length) {

    // how can I efficiently generate this array
    // without brute force looping through all of
    // the invalid and duplicate items like AA and CBA?

    return $array_of_all_possible_strings;
}

【问题讨论】:

  • 那么this question呢?
  • @MarkBaker 该问题涉及排列(即 aaa 是有效的)。这个问题是关于组合的。我可以强制所有排列,然后过滤组合,但我希望第一次跳过所有无效组合。
  • 如果存在重复,为什么不直接强制排列并且不将其添加到最终结果数组中?例如,计算AA 字符串,获取每个字符的计数,如果 > 1 则不要将其添加到输出中,本质上只是检查有效性。至少您不必为有效性检查第二次循环。
  • 无论哪种方式,我都必须遍历许多无效选项。我宁愿完全跳过处理无效选项。
  • 嗯,我的第一个想法是递归解决方案,但我希望有人想出一个更聪明的解决方案。

标签: php arrays algorithm recursion


【解决方案1】:

我觉得这看起来很有趣。这是我的尝试,值得:

function recurse($letters, &$words, $start, $end, $depth, $prefix = "") {
    $depth--;
    for ($i = $start; $i < $end; $i++) {
        $word = $prefix . $letters[$i];
        $words[] = $word;
        if ($depth) recurse($letters, $words, ++$start, $end, $depth, $word);
    }
}
function generate_strings($letters, $max_word_length) {
    $words = array();
    recurse($letters, $words, 0, count($letters), $max_word_length);
    return $words;      
}

【讨论】:

    【解决方案2】:

    不懂php,但算法很清楚:

    1. 将 N 个字母(如果还没有的话)排序到一个数组中。
    2. 维护两个动态数组:长度为 N 或更短的所有组合的列表(您想要的),以及长度为 N-1 或更短的所有字符串的列表。
    3. 从最后一个字符开始循环向后遍历字符数组。
    4. 将第 i 个字符作为单个字符串添加到所有字符串的列表中。如果 N > 1,也添加到长度为 N-1 的字符串列表中。
      1. 现在循环遍历长度为 N-1 或更短的字符串列表,从数组的开头到数组的当前结尾
      2. 对于每个较短的字符串,通过添加第 i 个字符来创建一个新字符串。
      3. 将此新字符串添加到所有字符串列表中。
      4. 如果长度为 N -1 或更短,则添加到较短字符串的列表中。 (此处小心迭代器 - 您不想访问刚刚在此内部循环中添加的字符串。)
    5. 返回字符串列表。

    【讨论】:

    • 谢谢。如果有人想提供代码解决方案,我将把它留得更久一些,但我相信你的算法是正确的。
    【解决方案3】:

    我今天编辑了我的this similar post(因为我意识到我最初发布了一个错误的方法)并且在研究这个主题时,我发现你的问题很有趣。

    据我所知,Don'tPanic 的递归方法似乎有效。我只是想我会根据我的链接方法发布一个非递归的“堆叠”方法。我相信我们的解决方案之间几乎没有性能差异,也许您会发现我的解决方案更易于阅读(也许不是)。

    代码:(Demo)

    function permStacker($array,$length){
        $stack=[[]];  // declare intitial empty element
        for($x=0; $x<$length; ++$x){  // limit the total number of passes / max string length
            foreach($stack as $combination){
                foreach(array_diff($array,range('a',end($combination))) as $left){  // do not include iterate letter that come earlier than rightmost letter
                    $merge=array_merge($combination,[$left]);
                    $stack[implode($merge)]=$merge;  // keys hold desired strings, values hold subarray of combinations for iterated referencing
                }
            }
        }
        unset($stack[0]); // remove initial empty element
        return array_keys($stack);  // return array of permutations as space delimited strings
    }
    
    $permutations=permStacker(range('a','h'),4);
    echo 'Total Permutations: ',sizeof($permutations),"\n";
    var_export($permutations);
    

    输出(截断):

    Total Permutations: 162
    array (
      0 => 'a',
      1 => 'b',
      2 => 'c',
      3 => 'd',
      4 => 'e',
      5 => 'f',
      6 => 'g',
      7 => 'h',
      8 => 'ab',
      9 => 'ac',
      10 => 'ad',
      11 => 'ae',
      12 => 'af',
      13 => 'ag',
      14 => 'ah',
      15 => 'bc',
      16 => 'bd',
      17 => 'be',
      18 => 'bf',
      19 => 'bg',
      20 => 'bh',
      21 => 'cd',
      22 => 'ce',
      23 => 'cf',
      24 => 'cg',
      25 => 'ch',
      26 => 'de',
      ...
      126 => 'afgh',
      127 => 'bcde',
      128 => 'bcdf',
      129 => 'bcdg',
      130 => 'bcdh',
      131 => 'bcef',
      132 => 'bceg',
      133 => 'bceh',
      134 => 'bcfg',
      135 => 'bcfh',
      136 => 'bcgh',
      137 => 'bdef',
      138 => 'bdeg',
      139 => 'bdeh',
      140 => 'bdfg',
      141 => 'bdfh',
      142 => 'bdgh',
      143 => 'befg',
      144 => 'befh',
      145 => 'begh',
      146 => 'bfgh',
      147 => 'cdef',
      148 => 'cdeg',
      149 => 'cdeh',
      150 => 'cdfg',
      151 => 'cdfh',
      152 => 'cdgh',
      153 => 'cefg',
      154 => 'cefh',
      155 => 'cegh',
      156 => 'cfgh',
      157 => 'defg',
      158 => 'defh',
      159 => 'degh',
      160 => 'dfgh',
      161 => 'efgh',
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-29
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2015-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多