一个能够接受数字或单词并找出所有可能组合的算法。

16

我正在寻找一种算法,它可以将数字或单词组合在一起,并找出所有可能的排列方式。同时,我也想能够定义要组合在一起的值的数量。

例如,假设给定的字符串或数组为:

cat  
dog  
fish  

那么对于值为2的结果可能是:

cat dog  
cat fish  
dog cat  
dog fish  
fish cat  
fish dog   

如果有三个项目并且有两个结果匹配,则可能会出现6种可能的变化
如果有三个项目并且有三个结果匹配,则可能会出现:

cat dog fish  
cat fish dog  
dog cat fish  
dog fish cat  
fish cat dog  
fish dog cat  

我在Stackoverflow上找到了一个链接指向一个用JavaScript实现这个功能的例子,不知道有没有人知道如何在PHP中实现,或者说可能已经有相关的构建好的库了吗?

http://www.merriampark.com/comb.htm(链接已失效)


JavaScript链接已失效。 - Timo Huovinen
2个回答

24

看看http://pear.php.net/package/Math_Combinatorics

<?php
require_once 'Math/Combinatorics.php';
$words = array('cat', 'dog', 'fish');
$combinatorics = new Math_Combinatorics;
foreach($combinatorics->permutations($words, 2) as $p) {
  echo join(' ', $p), "\n"; 
}

打印

cat dog
dog cat
cat fish
fish cat
dog fish
fish dog

非常不错的PEAR软件包,我将来会使用它。 - Alix Axel
太棒了,谢谢您的信息。您知道示例脚本中$combinations 和 $permutations 之间有什么区别吗? - JasonDavis
在排列中,元素的顺序很重要;而在组合中,元素的顺序则无关紧要。例如,(cat,dog) 和 (dog,cat) 都会被包含在 permutations() 的结果中,而 combinations() 的结果只会包含其中一个,因为第二个被视为相等。 - VolkerK
顺便提一句,你可以从这里复制Math_Combinatorics(https://raw.githubusercontent.com/pear/Math_Combinatorics/trunk/Combinatorics.php),它本身可以正常工作。 - Timo Huovinen

15

如果你想知道类似这样的东西如何运作,这就是我使用二进制实现它且不使用任何PHP库的方法。

function search_get_combos($query){
$list = explode(" ", $query);
$bits = count($list); //bits of binary number equal to number of words in query;
//Convert decimal number to binary with set number of bits, and split into array
$dec = 1;
$binary = str_split(str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT));
while($dec < pow(2, $bits)) {
    //Each 'word' is linked to a bit of the binary number.
    //Whenever the bit is '1' its added to the current term.
    $curterm = "";
    $i = 0;
    while($i < ($bits)){
        if($binary[$i] == 1) {
            $curterm .= $list[$i]." ";
        }
        $i++;
    }
    $terms[] = $curterm;
    //Count up by 1
    $dec++;
    $binary = str_split(str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT));
}
return $terms;
}

请注意,这将仅返回唯一组合,但可以轻松扩展以获取每个可能的组合顺序,因此在您的示例中,这将输出:

Array
(
    [0] => fish 
    [1] => dog 
    [2] => dog fish 
    [3] => cat 
    [4] => cat fish 
    [5] => cat dog 
    [6] => cat dog fish 
)

编辑(更多澄清)

基本理论

首先,二进制数如您所知是由一串 1 和 0 组成的。该数字的长度指其具有的“位”数,例如数字 011001 具有6个位(如果您感兴趣,这个数字代表25)。然后,如果数字的每个位对应于其中一个术语,每次计数时,如果该位为1,则将该术语包含在输出中,而如果为0,则忽略该项。因此,这就是正在发生的基本原理。

深入了解代码

PHP 没有二进制计数的方法,但可以将十进制转换为二进制。因此,此函数实际上是使用十进制进行计数,并将其转换为二进制。但是,由于位数很重要,因为每个术语都需要自己的位,所以您需要添加前导零,这就是此部分所做的工作:str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT)

现在,此函数使用 while 循环,但由于它需要循环的次数取决于有多少个术语,因此需要进行一些数学计算。如果您曾经使用过二进制,您将知道可以制造的最大数字是 2^n(其中n是位数)。

我认为这应该涵盖了函数的所有混淆部分,如果我漏掉了什么,请告诉我。

查看正在发生的情况

使用以下代码输出所使用的逻辑,可能更容易理解!

function search_get_combos_demo($query){
    $list = explode(" ", $query);
    $bits = count($list);
    $dec = 1;
    while($dec < pow(2, $bits)) {
        $binary = str_split(str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT));
        $curterm = "";
        $i = 0;
        while($i < ($bits)){
            if($binary[$i] == 1) {
                $curterm[] = $list[$i]." ";
            }
            $i++;
        }
        //-----DISPLAY PROCESS-----//
        echo "Iteration: $dec <table cellpadding=\"5\" border=\"1\"><tr>";
        foreach($binary as $b){
            echo "<td>$b</td>";
        }
        echo "</tr><tr>";
        foreach($list as $l){
            echo "<td>$l</td>";
        }
        echo "</tr></table>Output: ";
        foreach($curterm as $c){
            echo $c." ";
        }
        echo "<br><br>";
        //-----END DISPLAY PROCESS-----//
        $terms[] = $curterm;
        $dec++;
    }
    return $terms;
}

@TimoHuovinen 我已经更新了我的答案。如果它回答了你的问题,请告诉我,否则请让我知道,我会尽力解释更多。 - Adi Bradfield
谢谢您抽出时间澄清这个问题,我今天稍后会深入研究它。 - Timo Huovinen
@TimoHuovinen 请尝试我在答案末尾添加的函数,它可能会使情况更加清晰,因为它会输出每次迭代的表格。 - Adi Bradfield
3
哦!现在我明白了!视觉信息帮助了!1.对于3个单词“猫 狗 鱼”,您需要将2的次方数目8取出,如果你使用二进制计算到8,它是000,001,010,011,100,101,110,111。如果您把二进制计数中的第一个数字看作是包括“猫”的规则,第二个数字为“狗”,第三个数字为“鱼”,那么在二进制计数中会包含每种可能的组合!001=鱼,101=猫鱼,111=猫狗鱼 - Timo Huovinen
@TimoHuovinen 很准确!很棒,不是吗? - Adi Bradfield
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接