PHP数组组合
Posted
技术标签:
【中文标题】PHP数组组合【英文标题】:PHP array combinations 【发布时间】:2011-04-14 03:19:48 【问题描述】:我有一个由 7 个数字组成的数组(1、2、3、4、5、6、7),我想选择其中的 5 个数字,例如 (1,2,3,4,5), (1,2,3,4,6), (1,2,3,4,7)。 请注意,(1,2,3,4,5) 等于 (4,5,3,1,2),因此输出中应仅包含其中之一。
我想知道 php 中是否有函数或任何算法可以做到这一点? 我不知道从哪里开始。 你能帮帮我吗?
我希望将 7 个给定数字的所有组合(它们取自一个数组)放入 5 个插槽中,不考虑顺序。
【问题讨论】:
你能提供更多的规格吗?我很难从样本集中提取它——前提是我参加 SAT 已经十年了。 你想生成数字 1 到 7 的所有组合放入 5 个插槽中,不考虑顺序吗? 你想要元素的子集,对吧? @erisco - 我希望将 7 个给定数字的所有组合(它们取自数组)放入 5 个插槽中,不考虑顺序 【参考方案1】:您可以使用http://stereofrog.com/blok/on/070910 此处找到的解决方案。
如果链接失效,这里是代码......
class Combinations implements Iterator
protected $c = null;
protected $s = null;
protected $n = 0;
protected $k = 0;
protected $pos = 0;
function __construct($s, $k)
if(is_array($s))
$this->s = array_values($s);
$this->n = count($this->s);
else
$this->s = (string) $s;
$this->n = strlen($this->s);
$this->k = $k;
$this->rewind();
function key()
return $this->pos;
function current()
$r = array();
for($i = 0; $i < $this->k; $i++)
$r[] = $this->s[$this->c[$i]];
return is_array($this->s) ? $r : implode('', $r);
function next()
if($this->_next())
$this->pos++;
else
$this->pos = -1;
function rewind()
$this->c = range(0, $this->k);
$this->pos = 0;
function valid()
return $this->pos >= 0;
protected function _next()
$i = $this->k - 1;
while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
$i--;
if($i < 0)
return false;
$this->c[$i]++;
while($i++ < $this->k - 1)
$this->c[$i] = $this->c[$i - 1] + 1;
return true;
foreach(new Combinations("1234567", 5) as $substring)
echo $substring, ' ';
12345 12346 12347 12356 12357 12367 12456 12457 12467 12567 13456 13457 13467 13567 14567 23456 23457 23467 23567 24567 3456
【讨论】:
感谢您在此处发布此链接,因为链接现在似乎已失效:) 这对范围array(1, 2, 3, 4, 5, 6, 7)
很好,但假设范围是 1-80
??我收到Out of memory (allocated 1837629440)
@devpro 组合的数量像阶乘一样增长,甚至比指数还要快。对于 50 个元素,C(50,25) = 50!/(25!x 25!) = 126,410,606,437,752。假设生成序列不需要任何成本,并且您可以在 1µs(每秒一百万个序列)内处理任何给定序列,则处理 C(50,25) 个序列需要大约四年时间。使用生成器可能会避免内存问题,但无论如何你都没有足够的 CPU 来对结果做任何有用的事情。【参考方案2】:
<?php
echo "<pre>";
$test = array("test_1","test_2","test_3");
// Get Combination
$return = uniqueCombination($test);
//Sort
sort($return);
//Pretty Print
print_r(array_map(function($v) return implode(",", $v); , $return));
function uniqueCombination($in, $minLength = 1, $max = 2000)
$count = count($in);
$members = pow(2, $count);
$return = array();
for($i = 0; $i < $members; $i ++)
$b = sprintf("%0" . $count . "b", $i);
$out = array();
for($j = 0; $j < $count; $j ++)
$b$j == '1' and $out[] = $in[$j];
count($out) >= $minLength && count($out) <= $max and $return[] = $out;
return $return;
?>
输出
Array
(
[0] => test_1
[1] => test_2
[2] => test_3
[3] => test_1,test_2
[4] => test_1,test_3
[5] => test_2,test_3
[6] => test_1,test_2,test_3
)
【讨论】:
欢迎来到 Stack Overflow!尽管此代码可能有助于解决问题,但它并没有解释 why 和/或 如何 回答问题。提供这种额外的背景将显着提高其长期价值。请edit您的答案添加解释,包括适用的限制和假设。 还有我的!但是解释它的工作原理真的很有帮助!...尤其是那些sprintf()
和and
用法
这对范围 array(1, 2, 3, 4, 5, 6, 7)
很好,但假设范围是 1-80
?我收到Out of memory (allocated 1837629440)
【参考方案3】:
PEAR 存储库中的 Math_Combinatorics
完全符合您的要求:
返回所有组合和排列的包,没有 给定集合和子集大小的重复。关联数组是 保存。
require_once 'Math/Combinatorics.php';
$combinatorics = new Math_Combinatorics;
$input = array(1, 2, 3, 4, 5, 6, 7);
$output = $combinatorics->combinations($input, 5); // 5 is the subset size
// 1,2,3,4,5
// 1,2,3,4,6
// 1,2,3,4,7
// 1,2,3,5,6
// 1,2,3,5,7
// 1,2,3,6,7
// 1,2,4,5,6
// 1,2,4,5,7
// 1,2,4,6,7
// 1,2,5,6,7
// 1,3,4,5,6
// 1,3,4,5,7
// 1,3,4,6,7
// 1,3,5,6,7
// 1,4,5,6,7
// 2,3,4,5,6
// 2,3,4,5,7
// 2,3,4,6,7
// 2,3,5,6,7
// 2,4,5,6,7
// 3,4,5,6,7
【讨论】:
这对范围array(1, 2, 3, 4, 5, 6, 7)
很好,但假设范围是 1-80
?我收到Out of memory (allocated 1837629440)
80 个项目,一次 5 个,产生 24,040,016 个组合。太多了!
是的,Salman,它是真的,但我需要,它可以处理 50 个项目,有什么解决方案可以解决这个问题吗?【参考方案4】:
另一种基于堆栈的解决方案。它退出速度很快,但会占用大量内存。
希望对某人有所帮助。
详细说明:
function _combine($numbers, $length)
$combinations = array();
$stack = array();
// every combinations can be ordered
sort($numbers);
// startup
array_push($stack, array(
'store' => array(),
'options' => $numbers,
));
while (true)
// pop a item
$item = array_pop($stack);
// end of stack
if (!$item)
break;
// valid store
if ($length <= count($item['store']))
$combinations[] = $item['store'];
continue;
// bypass when options are not enough
if (count($item['store']) + count($item['options']) < $length)
continue;
foreach ($item['options'] as $index => $n)
$newStore = $item['store'];
$newStore[] = $n;
// every combine can be ordered
// so accept only options which is greater than store numbers
$newOptions = array_slice($item['options'], $index + 1);
// push new items
array_push($stack, array(
'store' => $newStore,
'options' => $newOptions,
));
return $combinations;
【讨论】:
【参考方案5】:改进了这个answer 也可以与关联数组一起使用:
function uniqueCombination($values, $minLength = 1, $maxLength = 2000)
$count = count($values);
$size = pow(2, $count);
$keys = array_keys($values);
$return = [];
for($i = 0; $i < $size; $i ++)
$b = sprintf("%0" . $count . "b", $i);
$out = [];
for($j = 0; $j < $count; $j ++)
if ($b[$j] == '1')
$out[$keys[$j]] = $values[$keys[$j]];
if (count($out) >= $minLength && count($out) <= $maxLength)
$return[] = $out;
return $return;
例如:
print_r(uniqueCombination([
'a' => 'xyz',
'b' => 'pqr',
]);
结果:
Array
(
[0] => Array
(
[b] => pqr
)
[1] => Array
(
[a] => xyz
)
[2] => Array
(
[a] => xyz
[b] => pqr
)
)
它仍然适用于非关联数组:
print_r(uniqueCombination(['a', 'b']);
结果:
Array
(
[0] => Array
(
[1] => b
)
[1] => Array
(
[0] => a
)
[2] => Array
(
[0] => a
[1] => b
)
)
【讨论】:
【参考方案6】:为组合算法优化速度和内存的新解决方案
心态:生成组合K个数字数组。新解决方案将使用 K 'for' 语句。一个“为”一个数字。 如:$K = 5 表示使用了 5 个 'for' 语句
$total = count($array);
$i0 = -1;
for ($i1 = $i0 + 1; $i1 < $total; $i1++)
for ($i2 = $i1 + 1; $i2 < $total; $i2++)
for ($i3 = $i2 + 1; $i3 < $total; $i3++)
for ($i4 = $i3 + 1; $i4 < $total; $i4++)
for ($i5 = $i4 + 1; $i5 < $total; $i5++)
$record = array();
for ($i = 1; $i <= $k; $i++)
$t = "i$i";
$record[] = $array[$$t];
$callback($record);
以及生成将由 eval() 函数执行的真实代码的代码的详细信息
function combine($array, $k, $callback)
$total = count($array);
$init = '
$i0 = -1;
';
$sample = '
for($icurrent = $iprevious + 1; $icurrent < $total; $icurrent++ )
body
';
$do = '
$record = array();
for ($i = 1; $i <= $k; $i++)
$t = "i$i";
$record[] = $array[$$t];
$callback($record);
';
$for = '';
for ($i = $k; $i >= 1; $i--)
switch ($i)
case $k:
$for = str_replace(['current', 'previous', 'body'], [$i, $i - 1, $do], $sample);
break;
case 1:
$for = $init . str_replace(['current', 'previous', 'body'], [$i, $i - 1, $for], $sample);
break;
default:
$for = str_replace(['current', 'previous', 'body'], [$i, $i - 1, $for], $sample);
break;
// execute
eval($for);
如何组合K个数组
$k = 5;
$array = array(1, 2, 3, 4, 5, 6, 7);
$callback = function ($record)
echo implode($record) . "\n";
;
combine($array, $k, $callback);
【讨论】:
【参考方案7】:我需要一个包含子集的组合函数,因此我采用了@Nguyen Van Vinh
的答案并根据我的需要对其进行了修改。
如果你将[1,2,3,4]
传递给函数,它会返回每个唯一的组合和子集,并排序:
[
[1,2,3,4], [1,2,3], [1,2,4], [1,3,4], [2,3,4], [1,2], [1,3], [1,4], [2,3], [2,4], [3,4], [1], [2], [3], [4]
]
函数如下:
function get_combinations_with_length( $numbers, $length )
$result = array();
$stack = array();
// every combinations can be ordered
sort($numbers);
// startup
array_push($stack, array(
'store' => array(),
'options' => $numbers,
));
while (true)
// pop a item
$item = array_pop($stack);
// end of stack
if (!$item) break;
// valid store
if ($length <= count($item['store']))
$result[] = $item['store'];
continue;
// bypass when options are not enough
if (count($item['store']) + count($item['options']) < $length)
continue;
foreach ($item['options'] as $i=>$n)
$newStore = $item['store'];
$newStore[] = $n;
// every combine can be ordered, so accept only options that are greater than store numbers
$newOptions = array_slice($item['options'], $i + 1);
// array_unshift to sort numerically, array_push to reverse
array_unshift($stack, array(
'store' => $newStore,
'options' => $newOptions,
));
return $result;
function get_all_combinations( $numbers )
$length = count($numbers);
$result = [];
while ($length > 0)
$result = array_merge($result, get_combinations_with_length( $numbers, $length ));
$length--;
return $result;
$numbers = [1,2,3,4];
$result = get_all_combinations($numbers);
echo 'START: '.json_encode( $numbers ).'<br><br>';
echo 'RESULT: '.json_encode( $result ).'<br><br>';
echo '('.count($result).' combination subsets found)';
【讨论】:
以上是关于PHP数组组合的主要内容,如果未能解决你的问题,请参考以下文章