【问题标题】:PHP array combinationsPHP数组组合
【发布时间】: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 个插槽中,不考虑顺序

标签: php arrays combinations


【解决方案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($i{current} = $i{previous} + 1; $i{current} < $total; $i{current}++ ) {
                  {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)';
        

        【讨论】:

          猜你喜欢
          • 2010-11-28
          • 2010-09-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多