16

I am looking for an algorithm that will take numbers or words and find all possible variations of them together and also let me define how many values to look for together.

Example lets say the string or array is:

cat  
dog  
fish  

then the results for a value of 2 could be:

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

SO the results from the set of 3 items are 6 possible variations of it at 2 results matching
with 3 results matching it would be:

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

...probably more options even

I have found a link on Stackoverflow to this example that does this but it is in javascript, I am wondering if anyone knows how to do this in PHP maybe there is something already built?

http://www.merriampark.com/comb.htm (dead link)

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
JasonDavis
  • 48,204
  • 100
  • 318
  • 537

2 Answers2

24

Take a look at 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"; 
}

prints

cat dog
dog cat
cat fish
fish cat
dog fish
fish dog
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • Very nice PEAR package, I'll use it in the future. – Alix Axel Aug 10 '09 at 18:08
  • That is awsome thanks for the info, do you have any idea what the difference between these 2 is $combinations and $permutations in the example script? – JasonDavis Aug 10 '09 at 20:46
  • In permutations the order of the elements counts, in combinations the order is irrelevant. E.g. (cat,dog) and (dog,cat) both are included in the result of permutations() while the result of combinations() will only include one of them, the second is considered equal. – VolkerK Aug 11 '09 at 10:38
  • btw, you can copy [Math_Combinatronics](https://github.com/pear/Math_Combinatorics) from [here](https://raw.githubusercontent.com/pear/Math_Combinatorics/trunk/Combinatorics.php) it works fine on it's own – Timo Huovinen Jun 24 '14 at 10:06
15

If your looking for how something like this works, this is how i acheived it with no php libraries using binary.

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;
}

Note that this will return only unique combinations though, but can easily be extended to get every possible order of combinations so in your example this outputs:

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

Edit (More clarification)

Basic theory

So firstly, binary numbers as you probably know are a string of 1's and 0's. The length of the number is the number of 'bits' it has, eg. the number 011001 has 6 bits (The numbers 25 in case you interested). Then if each bit of the number corresponds to one of the terms, each time it counts up, if the bit is 1, the term is included in the output, whereas if it's 0, it is ignored. So thats the basic theory of what's happening.

Delving into the code

PHP has no way of counting in binary, but you can convert decimals to binary. So this function actually counts up in decimal, and converts that to binary. But because the number of bits is important, as each term needs its own bit, you need to add leading 0's, so thats what this bit does: str_pad(decbin($dec), $bits, '0', STR_PAD_LEFT)

Now this function uses a while loop, but as the number of times it needs to loop changes depending on how many terms there are, a bit of maths needs to be done. If you have ever worked with binary, you will know that the maximum number you can make is the 2^n (where n is the number of bits).

I think that should have covered all the confusing bits of the function, let me know if I've missed anything.

See what's happening

Use the following code to output the logic used, it may make a bit more sense seeing it this way!

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;
}
Adi Bradfield
  • 2,063
  • 1
  • 17
  • 27
  • I have no idea how this works, can you add a bit more detail on the concept? – Timo Huovinen Jul 25 '13 at 13:33
  • @TimoHuovinen I've updated my answer. Please let me know if it answers your questions, otherwise just let me know and I'll try to explain more – Adi Bradfield Jul 25 '13 at 18:36
  • thank you for taking the time to clarify it, will delve into it later today. – Timo Huovinen Jul 26 '13 at 10:57
  • @TimoHuovinen Try the function I've added at the end of my answer, it might make it a bit clearer to see whats going on, as it outputs a table for each iteration – Adi Bradfield Jul 29 '13 at 13:42
  • 3
    Oh! Now I get it! The visual information helped! 1. You take 2 to the power of the number of words which is 8 for 3 words: "cat dog fish", if you count in binary until 8 it's 000,001,010,011,100,101,110,111. If you treat the first digit in the count as the rule to include "cat", the second for "dog" and the third for "fish" then counting in binary will include every possible combination! 001 = fish, 101 = cat fish, 111 = cat dog fish – Timo Huovinen Jul 30 '13 at 17:14
  • @TimoHuovinen Spot on! Nifty isn't it? – Adi Bradfield Jul 30 '13 at 17:22
  • Yes! it definitely is. – Timo Huovinen Jul 30 '13 at 17:37
  • @TimoHuovinen The problem is I thought of it, but now I have to scrap it since it's been made redundant in my code! Never mind, I might find a use for it some day! – Adi Bradfield Jul 30 '13 at 17:42
  • StackOverflow is cool in this way, it allows you to store bits of working code and share it with everyone, I often end up with code that is not necessary in any of my active projects but I can come back to it, tweak it and it starts to live again. – Timo Huovinen Jul 30 '13 at 18:04
  • @TimoHuovinen Yeah SO is an amazing resource! Helped me out in so many situations, I just wish I had a bit more to give back to the community! :') – Adi Bradfield Jul 30 '13 at 18:08