Pergunta

Finding a proper title for this is rather hard for me.

Imagine a set of numbers:

$numbers = array(10, 12, 20, 24, 34, 38, 41, 48);

Then there is a specific amount of picks:

$picks = 6;

What i would like to do is generate an array that contains combinations of each $picks numbers, but those numbers must be contained in $numbers.

The final array result should contain all possible combinations of the values specified in $numbers, but without the order being of any importance (i.e. meaning that [1,2,3] is equal to [1,3,2]).

Any attempts to write the function myself failed because i don't even know what condition i could base my loop on.

function computeCombinations(array $numbers, $picks){
    while(?){
    }
    return $results;
}

Due to a lack of maths skills i cannot wrap my head around how to write this and i lack the proper naming for this algorithm to search for it on the web or on stackoverflow.

Whether the function ends up as javascript or PHP is not of importance.

UPDATE

I have been able to write a function that produces the desired result, but with one restriction: there is no way to really know when all combinations have been found. Therefore i used a rather sloppy workaround - a total iterations counter which will throw me out of the loop. Of course that function is not usable for practical applications, but i post it so that readers may be better able to understand what it was that i wanted to achieve:

function computeCombinations(array $numbers, $pickCount){

  $results=array();
  $totalIterations=0;

  while(true){
      shuffle($numbers);
      $picks=array_slice($numbers, 0, $pickCount);
      asort($picks);
      $results[implode('',$picks)]=$picks;
      if($totalIterations++ > 10000000)
          break;
  }

  return $results;

}

$out = computeCombinations(array(10, 20, 30, 40, 50, 60, 70, 80), 6);

foreach($out as $v){
    echo implode(', ',$v)."\n";
}

Prints:

30, 40, 50, 60, 70, 80
10, 20, 40, 50, 60, 80
20, 30, 40, 50, 60, 80
10, 20, 40, 60, 70, 80
10, 40, 50, 60, 70, 80
20, 30, 50, 60, 70, 80
10, 20, 30, 40, 50, 70
10, 20, 30, 50, 60, 70
20, 30, 40, 60, 70, 80
10, 30, 40, 60, 70, 80
10, 30, 50, 60, 70, 80
10, 20, 30, 50, 60, 80
10, 20, 30, 40, 50, 60
10, 20, 30, 60, 70, 80
10, 30, 40, 50, 60, 70
10, 20, 50, 60, 70, 80
10, 20, 40, 50, 70, 80
10, 20, 30, 50, 70, 80
10, 20, 30, 40, 60, 70
10, 30, 40, 50, 60, 80
20, 30, 40, 50, 60, 70
10, 20, 30, 40, 60, 80
10, 20, 30, 40, 70, 80
20, 30, 40, 50, 70, 80
10, 30, 40, 50, 70, 80
10, 20, 40, 50, 60, 70
20, 40, 50, 60, 70, 80
10, 20, 30, 40, 50, 80
Foi útil?

Solução

This is not a perfect solution, but anyway:

You can do this recursively:

Version 2:

function computeCombination(array $source,$picks,$startIndex=0,array $pad=array())
{
    if ($picks<=0) {
        return $pad;
    } elseif ($picks>=count($source)-$startIndex) {
        return array(array_merge($pad,array_slice($source,$startIndex)));
    }
    $result=array();
    foreach (range($startIndex,count($source)-$picks) as $subStartIndex) {
        $subPad=$pad;
        $subPad[]=$source[$subStartIndex];
        if (empty($pad)) {// to make the output look nice 
            $result=array_merge($result,computeCombination($source,$picks-1,$subStartIndex+1,$subPad));
        } else {
            $result=array_merge($result,array(computeCombination($source,$picks-1,$subStartIndex+1,$subPad)));
        }
    }
    return $result;
}
print_r(computeCombination(array(1,2,3,4,5),2));

Online demo

The print_r output:

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 2
        )

    [1] => Array
        (
            [0] => 1
            [1] => 3
        )

    [2] => Array
        (
            [0] => 1
            [1] => 4
        )

    [3] => Array
        (
            [0] => 1
            [1] => 5
        )

    [4] => Array
        (
            [0] => 2
            [1] => 3
        )

    [5] => Array
        (
            [0] => 2
            [1] => 4
        )

    [6] => Array
        (
            [0] => 2
            [1] => 5
        )

    [7] => Array
        (
            [0] => 3
            [1] => 4
        )

    [8] => Array
        (
            [0] => 3
            [1] => 5
        )

    [9] => Array
        (
            [0] => 4
            [1] => 5
        )

)

(Old version 1)

function computeCombination(array $source,$picks,$startIndex=0,array $pad=array())
{
    if ($picks<=0) {
        return $pad;
    } elseif ($picks>=count($source)-$startIndex) {
        return array_merge($pad,array_slice($source,$startIndex));
    }
    $result=array();
    foreach (range($startIndex,count($source)-$picks) as $subStartIndex) {
        $subPad=$pad;
        $subPad[]=$source[$subStartIndex];
        $result=array_merge($result,array(computeCombination($source,$picks-1,$subStartIndex+1,$subPad)));
    }
    return $result;
}
print_r(computeCombination(array(1,2,3,4,5),2));

Online demo

Outras dicas

EDIT

Here's another which is a bit more configurable:

<?php
$TOTAL = 7;
$MAX = 46;
$MIN = 1;

$numbers = array(10, 12, 20, 24, 34, 38, 41, 48);
for($i = 0; $i < $TOTAL; $i++) {
do {
    $new_number = mt_rand($MIN,$MAX);
}
while(in_array($new_number, $numbers));
echo $new_number . "\n";
}
?>

Here's another:

<?php
// $numbers = range(1, 7);
$numbers = array(10, 12, 20, 24, 34, 38, 41, 48);
shuffle($numbers);
foreach ($numbers as $number) {
    echo "$number ";
}
?>

And see if this will do the job:

I borrowed the example from The PHP.net Website - array_rand()

<?php
$input = array(10, 12, 20, 24, 34, 38, 41, 48);

$rand_keys = array_rand($input, 7);
echo $input[$rand_keys[0]] . "\n";
echo $input[$rand_keys[1]] . "\n";
echo $input[$rand_keys[2]] . "\n";
echo $input[$rand_keys[3]] . "\n";
echo $input[$rand_keys[4]] . "\n";
echo $input[$rand_keys[5]] . "\n";
echo $input[$rand_keys[6]] . "\n";
echo $input[$rand_keys[7]] . "\n";
?>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top