使用一组字符串生成所有可能的组合

Posted

技术标签:

【中文标题】使用一组字符串生成所有可能的组合【英文标题】:Generate all possible combinations using a set of strings 【发布时间】:2012-08-23 01:18:27 【问题描述】:

我正在尝试生成一组字符串的所有可能组合,每个字符串最多使用一次。

未定义输出字符串的长度(最大长度是给定字符串的数量,因为您只能使用一次) 例如,字符串集 array('A','B') 将生成 A,B,AB,BA。 例如,字符串集array('ABC', 'Z') 将生成“ABC”、“Z”、“ZABC”和“ABCZ”。 一个字符串集可以有相同的条目,并且输出不需要是唯一的。例如,字符串集array('A', 'A')会生成'A'、'A'、'AA'、'AA'; (我实际上并不需要重复,但我不想让事情变得更困难)

我知道 2 个字符串有 4 种组合 (2=>4) 和 3=>15, 4=>64, 5=>325 ...

由于我不是程序员,我发现它至少“具有挑战性”。嵌套循环很快就太复杂了。一个更简单的解决方案可能是在带有字符串的数组的索引中找到一个模式。但这让我重复使用了字符串......

  $strings = array('T','O','RS');
  $num = 0;
  $stringcount = count($strings);
  $variations = array(0,1,4,15,64,325,1956,13699,109600,986409);

  for($i=0;$i<$variations[$stringcount];$i++)
    $index = base_convert($num, 10, $stringcount);
    $array_of_indexes = str_split($index);
    $out='';
    for($j=0;$j<count($array_of_indexes);$j++)
     $out .= $strings[$array_of_indexes[$j]];
    
    echo $out . '<br />';
    $num++;
  

结果: 吨 ○ RS 旧约 面向对象 口服补液盐 RST RSO RSRS OTT OTO OTRS OOT 噢噢噢 OORS

不好,很多重复+很多有效组合不包括在内

我知道这个解决方案在很多方面都是错误的,但我不知道从哪里开始?有什么建议?提前谢谢!

【问题讨论】:

我认为这些是排列,而不是组合。甚至可能没有。 在我看来,递归排列算法确实是一个很好的候选者,尽管结果是独立地包含每个原始字符串与“纯”排列略有不同。递归终止情况是当您只有两个元素时,递归部分将是第 n 个元素,然后是第 n+1 个和剩余元素的递归权限。不过要小心,因为置换计算可能会很快通过屋顶!祝你好运! 【参考方案1】:

在数学术语中,您要求输入集的所有可能的非空有序子集。在整数序列在线百科全书中,此类序列的数量显示为sequence A007526 - 请注意,此序列以 4、15、64、325 开头,与您发现的完全一样。

这个问题在 Python 中有一个非常简短、高效的解决方案,所以我将首先发布该解决方案:

def gen_nos(s):
    for i in sorted(s):
        yield i
        s.remove(i)
        for j in gen_nos(s):
            yield i+j
        s.add(i)

例子:

>>> list(gen_nos(set(['a', 'b', 'c'])))
['a', 'ab', 'abc', 'ac', 'acb', 'b', 'ba', 'bac', 'bc', 'bca', 'c', 'ca', 'cab', 'cb', 'cba']

请注意,sorted 不是绝对必要的;它只是确保输出按字典顺序排序(否则,元素按集合顺序迭代,这本质上是任意的)。

要将其转换为 php,我们必须使用带有额外数组参数的递归函数来保存结果:

function gen_nos(&$set, &$results) 
    for($i=0; $i<count($set); $i++) 
        $results[] = $set[$i];
        $tempset = $set;
        array_splice($tempset, $i, 1);
        $tempresults = array();
        gen_nos($tempset, $tempresults);
        foreach($tempresults as $res) 
            $results[] = $set[$i] . $res;
        
    

例子:

$results = array();
$set = array("a", "b", "c");
gen_nos($set, $results);
var_dump($results);

生产

array(15) 
  [0]=>
  string(1) "a"
  [1]=>
  string(2) "ab"
  [2]=>
  string(3) "abc"
  [3]=>
  string(2) "ac"
  [4]=>
  string(3) "acb"
  [5]=>
  string(1) "b"
  [6]=>
  string(2) "ba"
  [7]=>
  string(3) "bac"
  [8]=>
  string(2) "bc"
  [9]=>
  string(3) "bca"
  [10]=>
  string(1) "c"
  [11]=>
  string(2) "ca"
  [12]=>
  string(3) "cab"
  [13]=>
  string(2) "cb"
  [14]=>
  string(3) "cba"

【讨论】:

我应该指出,我最后一次熟练使用 PHP 是在大约四年前,所以如果我的方法很糟糕并且有更好的方法来做我所做的事情,请告诉我!【参考方案2】:

这是我使用组合和排列的基本数学定义编写了相当长一段时间的实现。也许这会有所帮助。

<?php
/**
 * Generate all the combinations of $num elements in the given array
 *
 * @param array  $array   Given array
 * @param int    $num     Number of elements ot chossen
 * @param int    $start   Starter of the iteration
 * @return array          Result array
 */
function combine($array, $num, $start = 0) 

    static $level = 1;

    static $result = array();

    $cnt = count($array);

    $results = array();

    for($i = $start;$i < $cnt;$i++) 
        if($level < $num ) 
            $result[] = $array[$i];
            $start++;
            $level++;
            $results = array_merge($results, combine($array, $num, $start));
            $level--;
            array_pop($result);
        
        else 
            $result[] = $array[$i];
            $results[] = $result;
            array_pop($result);
        
    

    return $results;


/**
 * Generate all the permutations of the elements in the given array
 */
function permute($array) 

    $results = array();

    $cnt = count($array);

    for($i=0;$i<$cnt;$i++) 
        $first = array_shift($array);

        if(count($array) > 2 ) 
            $tmp = permute($array);
        
        elseif(count($array) == 2) 
            $array_ = $array;
            krsort($array_);
            $tmp = array($array, $array_);
        
        elseif(count($array) == 1) 
            $tmp = array($array);
        
        elseif(count($array) == 0) 
            $tmp = array(array());
        

        foreach($tmp as $k => $t) 
            array_unshift($t, $first);
            $tmp[$k] = $t;
        

        $results = array_merge($results, $tmp);

        array_push($array, $first);
    

    return $results;


$strings = array('T', 'O', 'RS');
$strings_count = count($strings);


$combinations = array();
for ($i = 1; $i <= $strings_count; $i++) 
  $combination = combine($strings, $i, 0);
  $combinations = array_merge($combinations, $combination);


$permutations = array();
foreach($combinations as $combination) 
  $permutation = permute($combination);
  $permutations = array_merge($permutations, $permutation);


print_r($combinations);
print_r($permutations);

【讨论】:

请注意,combine() 函数期望 $array 以数字和顺序索引。也许array_values() 可以消除这种担忧。如果键是组合的重要组成部分,则需要做更多的工作。【参考方案3】:

这是我幼稚的递归实现:

<?php
// Lists all ways to choose X from an array
function choose($x, array $arr) 
    $ret = array();
    if ($x === 0) 
        // I don't think this will come up.
        return array();
     else if ($x === 1) 
        foreach ($arr as $val) 
            $ret[] = array($val);
        
     else 
        $already_chosen = choose($x - 1, $arr);
        for ($i = 0, $size_i = sizeof($arr); $i < $size_i; $i++) 
            for ($j = 0, $size_j = sizeof($already_chosen); $j < $size_j; $j++) 
                if (!in_array($arr[$i], $already_chosen[$j])) 
                    $ret[] = array_merge(
                        array($arr[$i]),
                        $already_chosen[$j]
                    );
                
            
        
    
    return $ret;


function choose_all($arr) 
    for ($i = 1, $size = sizeof($arr); $i <= $size; $i++) 
        foreach (choose($i, $arr) as $val) 
            echo implode(":", $val).PHP_EOL;
        
    


choose_all(array(
    "A",
    "B",
));
echo "--".PHP_EOL;
choose_all(array(
    "ABC",
    "Z",
));
echo "--".PHP_EOL;
choose_all(array(
    'T',
    'O',
    'RS'
));

(当然不知道我做了什么)

http://codepad.org/5OKhsCJg

【讨论】:

有效,是的;不知道你做了什么,有点麻烦。尽管如此,我感觉现实世界的算法实现通常属于这一类......【参考方案4】:

您需要的数组本质上是笛卡尔积。这是集合数学的一个方面,在数据库系统和形式建模语言中很常见。如果你用谷歌搜索PHP这个术语,将会有许多实现解决方案。

(我从未直接做过任何 PHP,这就是为什么我不想向您发布不正确(或 hacky)的解决方案,但希望这会帮助您走上正确的道路)!

【讨论】:

实际上,不,他要求的不是笛卡尔积。这是一种允许省略的排列。 我不太确定我是否理解您的意思?它并没有说允许遗漏,并且从评论中(我实际上不需要重复,但我不希望让事情变得更困难),这向我表明笛卡尔积会更好......跨度> 你的产品是什么? 在这种情况下,每个单独的数组元素形成“一个集合” 不。那只会生成子集。我们想要有序的子集。

以上是关于使用一组字符串生成所有可能的组合的主要内容,如果未能解决你的问题,请参考以下文章

用一组字符替换字符串中的“#”,给出所有可能的字符串组合[关闭]

为字符串列表生成所有组合

如何生成字符之间带有空格的字符串的所有可能组合? Python

生成每个可能的 7 位数字组合的算法 [关闭]

使用多线程生成字符串的所有组合

生成所有可能的组合