排列 - 所有可能的数字集
Posted
技术标签:
【中文标题】排列 - 所有可能的数字集【英文标题】:Permutations - all possible sets of numbers 【发布时间】:2011-07-27 06:52:17 【问题描述】:我有从 0 到 8 的数字。我想在结果中显示这些数字的所有可能集合,每个集合都应该使用所有数字,每个数字在一个集合中只能出现一次。
我希望看到可以打印出结果的 php 解决方案。或者,至少,我想要一些组合学理论的更新,因为我早就忘记了。有多少排列的计算公式是什么?
示例集:
0-1-2-3-4-5-6-7-8 0-1-2-3-4-5-6-8-7 0-1-2-3-4-5-8-6-7 0-1-2-3-4-8-5-6-7 0-1-2-3-8-4-5-6-7 0-1-2-8-3-4-5-6-7 等等……【问题讨论】:
【参考方案1】:使用递归的简单解决方案
function filterElement($element)
if(is_array($element[0]))
return $element[0];
# base case
return $element;
function permutation($input, $path)
// base case 1
if(count($input) == 0)
return [$path];
$output = [];
foreach($input as $index => $num) # 1, 2,3, 4
$copyPath = $path; # copy the path - []
$copyPath[] = $num; # append the number [1]
# remove the current number
$inputLocal = $input;
unset($inputLocal[$index]); # [2, 3, 4]
$permute = permutation($inputLocal, $copyPath); # call [2, 3, 4], [1]
# for all element find add to output
foreach($permute as $ele)
# filter ouput
$output[] = filterElement($ele);
return $output;
print_r(permutation([1,2,3,4], []));
输出
Array
(
[0] => Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
[1] => Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 3
)
[2] => Array
(
[0] => 1
[1] => 3
[2] => 2
[3] => 4
)
[3] => Array
(
[0] => 1
[1] => 3
[2] => 4
[3] => 2
)
[4] => Array
(
[0] => 1
[1] => 4
[2] => 2
[3] => 3
)
[5] => Array
(
[0] => 1
[1] => 4
[2] => 3
[3] => 2
)
[6] => Array
(
[0] => 2
[1] => 1
[2] => 3
[3] => 4
)
[7] => Array
(
[0] => 2
[1] => 1
[2] => 4
[3] => 3
)
[8] => Array
(
[0] => 2
[1] => 3
[2] => 1
[3] => 4
)
[9] => Array
(
[0] => 2
[1] => 3
[2] => 4
[3] => 1
)
[10] => Array
(
[0] => 2
[1] => 4
[2] => 1
[3] => 3
)
[11] => Array
(
[0] => 2
[1] => 4
[2] => 3
[3] => 1
)
[12] => Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 4
)
[13] => Array
(
[0] => 3
[1] => 1
[2] => 4
[3] => 2
)
[14] => Array
(
[0] => 3
[1] => 2
[2] => 1
[3] => 4
)
[15] => Array
(
[0] => 3
[1] => 2
[2] => 4
[3] => 1
)
[16] => Array
(
[0] => 3
[1] => 4
[2] => 1
[3] => 2
)
[17] => Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
)
[18] => Array
(
[0] => 4
[1] => 1
[2] => 2
[3] => 3
)
[19] => Array
(
[0] => 4
[1] => 1
[2] => 3
[3] => 2
)
[20] => Array
(
[0] => 4
[1] => 2
[2] => 1
[3] => 3
)
[21] => Array
(
[0] => 4
[1] => 2
[2] => 3
[3] => 1
)
[22] => Array
(
[0] => 4
[1] => 3
[2] => 1
[3] => 2
)
[23] => Array
(
[0] => 4
[1] => 3
[2] => 2
[3] => 1
)
)
【讨论】:
【参考方案2】://function call
print_r(combinations([1,2,3,4,5,6,7,8,9,10,11,12,13]));
/**
* @param $mainArray
* @param int $size - optional
* @param array $combinations - optional
* @return mixed
*/
function combinations($mainArray, $size = 3, $combinations = [])
if (empty($combinations))
$combinations = $mainArray;
if ($size == 1)
return str_replace('-','',$combinations);;
$newCombination = array();
foreach ($mainArray as $key => $val)
foreach ($combinations as $char)
if(in_array($val, explode('-', $char)))
continue;
$newCombination[] = $val . '-' . $char;
return combinations($mainArray, $size - 1, $newCombination);
//========================== 下一个解决方案================== ================
function sampling($chars, $size, $combinations = array())
# if it's the first iteration, the first set
# of combinations is the same as the set of characters
if (empty($combinations))
$combinations = $chars;
# we're done if we're at size 1
if ($size == 1)
return $combinations;
# initialise array to put new values in
$new_combinations = array();
# loop through existing combinations and character set to create strings
foreach ($combinations as $combination)
foreach ($chars as $char)
$new_combinations[] = $combination .'-'. $char ;
# call same function again for the next iteration
return $this->sampling($chars, $size - 1, $new_combinations);
function array_has_dupes($array)
return count($array) !== count(array_unique($array));
function total()
// Generate ticket price
$arrfinal = array();
// combinations
$chars = array(1,2,3,4,5,6,7,8,9,10,11,12,13); // for 10 digits
$combinations = $this->sampling($chars, 3);
//print_r($combinations); //exit;
foreach($combinations as $key => $val)
$arr = explode('-', $val);//str_split($val);
if(!$this->array_has_dupes($arr))
$arrfinal[] = str_replace('-', '', $val);
echo '<pre>'; print_r($arrfinal); echo '</pre>';
【讨论】:
添加一些上下文来支持你的代码 Pravin... 它让 OP 和其他正在阅读它的人更容易理解代码。【参考方案3】:我已经移植了here 列出的 Python itertools 代码(使用生成器)。与迄今为止发布的解决方案相比,优势在于它允许您指定 r(排列大小)。
function permutations($pool, $r = null)
$n = count($pool);
if ($r == null)
$r = $n;
if ($r > $n)
return;
$indices = range(0, $n - 1);
$cycles = range($n, $n - $r + 1, -1); // count down
yield array_slice($pool, 0, $r);
if ($n <= 0)
return;
while (true)
$exit_early = false;
for ($i = $r;$i--;$i >= 0)
$cycles[$i]-= 1;
if ($cycles[$i] == 0)
// Push whatever is at index $i to the end, move everything back
if ($i < count($indices))
$removed = array_splice($indices, $i, 1);
array_push($indices, $removed[0]);
$cycles[$i] = $n - $i;
else
$j = $cycles[$i];
// Swap indices $i & -$j.
$i_val = $indices[$i];
$neg_j_val = $indices[count($indices) - $j];
$indices[$i] = $neg_j_val;
$indices[count($indices) - $j] = $i_val;
$result = [];
$counter = 0;
foreach ($indices as $indx)
array_push($result, $pool[$indx]);
$counter++;
if ($counter == $r) break;
yield $result;
$exit_early = true;
break;
if (!$exit_early)
break; // Outer while loop
它对我有用,但没有承诺! 示例用法:
$result = iterator_to_array(permutations([1, 2, 3, 4], 3));
foreach ($result as $row)
print implode(", ", $row) . "\n";
【讨论】:
太棒了。谢谢eddiewould 你节省了我的时间,Brilliant! 感谢您的解决方案。我试图生成(0-9 和 a-z)的 5 个字符排列。 $result = iterator_to_array(permutations([0,1, 2, 3, 4,5,6,7,8,9,a,b,c,d], 5));工作正常。但是当我添加 [0,1, 2, 3, 4,5,6,7,8,9,a,b,c,d, e] - 它停止工作。有什么想法吗?【参考方案4】:您正在寻找排列公式:
nPk = n!/(n-k)!
在您的情况下,您有 9 个条目并且您想选择所有条目,即 9P9 = 9! = 362880
您可以在 O'Reilly 的“PHP Cookbook”的配方 4.26 中找到一种 PHP 算法来置换。
pc_permute(array(0, 1, 2, 3, 4, 5, 7, 8));
抄自 O'Reilly:
function pc_permute($items, $perms = array( ))
if (empty($items))
print join(' ', $perms) . "\n";
else
for ($i = count($items) - 1; $i >= 0; --$i)
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
pc_permute($newitems, $newperms);
【讨论】:
哇,这太疯狂了。也许这属于用于堆栈交换的数学站点,但为什么是 0! = 1 你知道任何算法来生成所有这些吗? :) 我可以将数量减少到 8!= 40320,据我所知,第一个数字只能是 0 1 或 4。而 40k 并不算多... @Jason:根据定义 0。就像x^0 = 1
(x = 0
除外,因为 0^0 = 0
)。
@Deele 如果您想要以 0 1 或 4 开头的排列,您正在查看 8 位数字的三种不同排列(不同的集合),所以它是 8!+8!+8! = 120960
是的,我已经计算过了.【参考方案5】:
字典顺序。没有递归。数组长度几乎没有限制。 没有排序。它运行得相当快。这很容易理解。 减号:它会发出通知,但您可以添加一个条件来开始与第二个元素或 error_reporting(0) 进行比较。
$a = array(
1,
2,
3,
4,
5
);
$b = array_reverse($a);
print_r($a);
//here need "br"
while ($a != $b)
foreach(array_reverse($a, true) as $k => $v)
if ($v < $a[$k + 1])
foreach(array_reverse($a, true) as $ka => $val)
if ($val > $v) break;
$ch = $a[$k];
$a[$k] = $a[$ka];
$a[$ka] = $ch;
$c = array_slice($a, 0, $k + 1);
print_r($a = array_merge($c, array_reverse(array_slice($a, $k + 1))));
//here need "br"
break;
【讨论】:
修正通知:if (isset($a[$key + 1]) && $value < $a[$key + 1])
(编辑太短:)【参考方案6】:
这是我的课程版本。此类构建并返回置换数组作为结果
class Permutation
private $result;
public function getResult()
return $this->result;
public function permute($source, $permutated=array())
if (empty($permutated))
$this->result = array();
if (empty($source))
$this->result[] = $permutated;
else
for($i=0; $i<count($source); $i++)
$new_permutated = $permutated;
$new_permutated[] = $source[$i];
$new_source = array_merge(array_slice($source,0,$i),array_slice($source,$i+1));
$this->permute($new_source, $new_permutated);
return $this;
$arr = array(1,2,3,4,5);
$p = new Permutation();
print_r($p->permute($arr)->getResult());
最后三行测试我的课。
【讨论】:
【参考方案7】:从 PHP 5.5 开始,您可以使用Generators。生成器节省了大量内存并且速度更快(与 pc_permute() 相比超过一半)。因此,如果您有任何安装 PHP 5.5 的机会,那么您肯定想要生成器。 这个片段是从 Python 移植过来的:https://***.com/a/104436/3745311
function permutations(array $elements)
if (count($elements) <= 1)
yield $elements;
else
foreach (permutations(array_slice($elements, 1)) as $permutation)
foreach (range(0, count($elements) - 1) as $i)
yield array_merge(
array_slice($permutation, 0, $i),
[$elements[0]],
array_slice($permutation, $i)
);
示例用法:
$list = ['a', 'b', 'c'];
foreach (permutations($list) as $permutation)
echo implode(',', $permutation) . PHP_EOL;
输出:
a,b,c
b,a,c
b,c,a
a,c,b
c,a,b
c,b,a
【讨论】:
你将如何调整它以只选择一个子集。例如。返回以上但也a,b | a,c | b,a | b,c | c,a | c,b |一个 |乙 | c ? 查看我的答案以获得一个函数,该函数允许您指定它将生成的排列的大小。从那里生成所有排列是微不足道的。【参考方案8】:这是我的建议,希望比接受的答案更清楚。
function permutate($elements, $perm = array(), &$permArray = array())
if(empty($elements))
array_push($permArray,$perm); return;
for($i=0;$i<=count($elements)-1;$i++)
array_push($perm,$elements[$i]);
$tmp = $elements; array_splice($tmp,$i,1);
permutate($tmp,$perm,$permArray);
array_pop($perm);
return $permArray;
及用法:
$p = permutate(array('a','b','c'));
foreach($p as $perm)
print join(",",$perm)."|\n";
【讨论】:
【参考方案9】:由于这个问题经常出现在 Google 搜索结果中,这里有一个已接受答案的修改版本,它返回数组中的所有组合并将它们作为函数的返回值传递。
function pc_permute($items, $perms = array( ))
if (empty($items))
$return = array($perms);
else
$return = array();
for ($i = count($items) - 1; $i >= 0; --$i)
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
$return = array_merge($return, pc_permute($newitems, $newperms));
return $return;
使用方法:
$value = array('1', '2', '3');
print_r(pc_permute($value));
【讨论】:
内存使用过多:“PHP 致命错误:允许的内存大小为 134217728 字节已用尽(尝试分配 71 字节)...” @Kayvarini_set('memory_limit', -1);
【参考方案10】:
这是一个简单的递归函数,可以打印所有排列(用伪代码编写)
function rec(n, k)
if (k == n)
for i = 0 to n-1
print(perm[i], ' ');
print('\n');
else
for i = 0 to n-1
if (not used[i])
used[i] = true;
perm[k] = i;
rec(n, k+1);
used[i] = false;
它是这样称呼的:
rec(9, 0);
【讨论】:
【参考方案11】:我有你可能喜欢的东西
function combination_number($k,$n)
$n = intval($n);
$k = intval($k);
if ($k > $n)
return 0;
elseif ($n == $k)
return 1;
else
if ($k >= $n - $k)
$l = $k+1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $n-$k ; $i++)
$m *= $i;
else
$l = ($n-$k) + 1;
for ($i = $l+1 ; $i <= $n ; $i++)
$l *= $i;
$m = 1;
for ($i = 2 ; $i <= $k ; $i++)
$m *= $i;
return $l/$m;
function array_combination($le, $set)
$lk = combination_number($le, count($set));
$ret = array_fill(0, $lk, array_fill(0, $le, '') );
$temp = array();
for ($i = 0 ; $i < $le ; $i++)
$temp[$i] = $i;
$ret[0] = $temp;
for ($i = 1 ; $i < $lk ; $i++)
if ($temp[$le-1] != count($set)-1)
$temp[$le-1]++;
else
$od = -1;
for ($j = $le-2 ; $j >= 0 ; $j--)
if ($temp[$j]+1 != $temp[$j+1])
$od = $j;
break;
if ($od == -1)
break;
$temp[$od]++;
for ($j = $od+1 ; $j < $le ; $j++)
$temp[$j] = $temp[$od]+$j-$od;
$ret[$i] = $temp;
for ($i = 0 ; $i < $lk ; $i++)
for ($j = 0 ; $j < $le ; $j++)
$ret[$i][$j] = $set[$ret[$i][$j]];
return $ret;
这里是如何使用它:
要获得组合的数量:
combination_number(3,10); // returns number of combinations of ten-elements set.
获取所有可能的组合:
$mySet = array("A","B","C","D","E","F");
array_combination(3, $mySet); // returns all possible combinations of 3 elements of six-elements set.
希望你能利用它。
【讨论】:
可爱,但这只是维持秩序。 @Piotr Salaciak 这里缺少一些东西 array_combination(2, $mySet);不会返回 AA 或 BB 或 CC 的组合 @Guru 是不重复的组合link 仅适用于 3 个元素,当我尝试 4 个时,它返回一个组合 @delboy1978uk 我猜你正在尝试组合只有 3 个元素的集合的 3 个元素。【参考方案12】:您基本上是在谈论n
和k
均为9 的排列,因此您将拥有9!
不同的排列;看到这个:http://en.wikipedia.org/wiki/Permutation。
【讨论】:
以上是关于排列 - 所有可能的数字集的主要内容,如果未能解决你的问题,请参考以下文章
c_cpp 给定一组数字,返回所有可能的排列。例如,[1,2,3]具有以下排列:[1,2,3],[1,3,2],[2