I don't know how that "permutation" is called (it's not a permutation even probably), but it looked promising to me exploiting the fact that the elements in the set are ordered (if not, there is the order of index, so use index instead of value) so to shift from right to left and combine all left with right combinations.
This is either possible with recursion or with a stack, I normally prefer the stack.
The usage as suggested (encapsulating the function call):
$funky = function($a, $b) {
printf("(%s) and (%s)\n", implode(',', $a), implode(',', $b));
};
$paired = function($function) {
return function(array $array) use ($function) {
...
};
};
$funkyAll = $paired($funky);
$funkyAll(range(1, 5));
Running this with a sorted (more memory required) stack consuming strategy gives the following output (formatted in columns):
(1) and (2,3,4,5) (2,4) and (1,3,5) (1,4,5) and (2,3)
(2) and (1,3,4,5) (2,5) and (1,3,4) (2,3,4) and (1,5)
(3) and (1,2,4,5) (3,4) and (1,2,5) (2,3,5) and (1,4)
(4) and (1,2,3,5) (3,5) and (1,2,4) (2,4,5) and (1,3)
(5) and (1,2,3,4) (4,5) and (1,2,3) (3,4,5) and (1,2)
(1,2) and (3,4,5) (1,2,3) and (4,5) (1,2,3,4) and (5)
(1,3) and (2,4,5) (1,2,4) and (3,5) (1,2,3,5) and (4)
(1,4) and (2,3,5) (1,2,5) and (3,4) (1,2,4,5) and (3)
(1,5) and (2,3,4) (1,3,4) and (2,5) (1,3,4,5) and (2)
(2,3) and (1,4,5) (1,3,5) and (2,4) (2,3,4,5) and (1)
The exemplary implementation (full source-code as gist) is memory optimized and produces this order (array_pop
instead of array_shift
):
(1) and (2,3,4,5) (2,4) and (1,3,5) (1,4,5) and (2,3)
(2) and (1,3,4,5) (2,5) and (1,3,4) (1,3,4) and (2,5)
(3) and (1,2,4,5) (2,4,5) and (1,3) (1,3,5) and (2,4)
(4) and (1,2,3,5) (2,3,4) and (1,5) (1,3,4,5) and (2)
(5) and (1,2,3,4) (2,3,5) and (1,4) (1,2,3) and (4,5)
(4,5) and (1,2,3) (2,3,4,5) and (1) (1,2,4) and (3,5)
(3,4) and (1,2,5) (1,2) and (3,4,5) (1,2,5) and (3,4)
(3,5) and (1,2,4) (1,3) and (2,4,5) (1,2,4,5) and (3)
(3,4,5) and (1,2) (1,4) and (2,3,5) (1,2,3,4) and (5)
(2,3) and (1,4,5) (1,5) and (2,3,4) (1,2,3,5) and (4)
Implementation:
$stack[] = array(array(), $array);
while (list($left, $right) = array_pop($stack)) {
$min = end($left);
foreach ($right as $value)
{
if ($value < $min) continue;
$left2 = array_merge($left, array($value));
$right2 = array_diff($right, $left2);
if (!($left2 && $count = count($right2))) continue;
$function($left2, $right2);
--$count && $stack[] = array($left2, $right2);
}
}
I hope this is useful.
Another array related algorithm: Sorting with a modulus (just for reference, while writing this one I was reminded to that matrix thing)