【问题标题】:Concatenate values of n arrays in php在php中连接n个数组的值
【发布时间】:2010-02-13 17:37:52
【问题描述】:

我有未知数量的数组,每个数组包含未知数量的单词。我想连接每个列表中的值,以便将单词的所有可能变体存储到最终数组中。

例如,如果数组 1 包含:

dog
cat

并且数组 2 包含:

food
tooth

数组 3 包含:

car
bike

我希望输出是:

dog food car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat tooth bike

可能有超过 3 个列表,每个列表很可能有超过 2 个单词。

我想在 PHP 中执行此操作。

如果我知道列表的数量,我就知道该怎么做,尽管这可能不是最节省资源的方法。但是如果你知道数组的数量,嵌套的foreach 循环就可以工作。如果你不这样做呢?如果假设有 100 个数组,每个数组包含 100 个单词,那么有哪些方法可以解决这个问题。还是1000?

谢谢!

【问题讨论】:

  • 你也想要, , 狗粮, 狗牙, 猫粮猫牙还是只组合所有数组的组合?
  • 只是针对这个特定问题的所有数组的组合,虽然这也很有趣。
  • 如果您希望能够处理 100 个大小为 100 的数组,那么在尝试实际生成数组时会遇到严重的内存问题。组合的数量是一个非常巨大的数字。这是一个笛卡尔积。你需要一个迭代器。这种方法以时间换空间,速度较慢,但​​可以让您保持在内存限制内。我会发布一个链接,但它会指向一个活跃成员的网站。如果他不亲自来回答,我会发布链接。但除此之外,我不想偷走他的荣耀。
  • 酷,谢谢克里斯。我很期待。我怀疑我永远不会有 100x100 的地图。它很可能会少于 5 个数组,每个数组少于 20 个项目。但从理论上讲,我对大规模的想法很感兴趣。

标签: php arrays concatenation combinatorics


【解决方案1】:

您可以将所有单词数组放入一个数组中,然后使用 recursive 函数,如下所示:

function concat(array $array) {
    $current = array_shift($array);
    if(count($array) > 0) {
        $results = array();
        $temp = concat($array);
        foreach($current as $word) {
          foreach($temp as $value) {
            $results[] =  $word . ' ' . $value;
          }
        }
        return $results;           
    }
    else {
       return $current;
    }
}

$a = array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike'));

print_r(concat($a));

返回:

Array
(
    [0] => dog food car
    [1] => dog food bike
    [2] => dog tooth car
    [3] => dog tooth bike
    [4] => cat food car
    [5] => cat food bike
    [6] => cat tooth car
    [7] => cat tooth bike
)

但我猜这对于大型数组来说表现不佳,因为输出数组会非常大。


要解决这个问题,您可以使用类似的方法直接输出组合:

function concat(array $array, $concat = '') {
    $current = array_shift($array);

    $current_strings = array();

    foreach($current as $word) {
            $current_strings[] = $concat . ' ' . $word;
    }

    if(count($array) > 0) {
        foreach($current_strings as $string) {
            concat($array, $string);
        }       
    }
    else {
      foreach($current_strings as $string) {
          echo $string . PHP_EOL;
      }   
    }
}

concat(array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike')));

这给出了:

dog food car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat tooth bike

使用这种方法也很容易获得“子连接”。只需在concat($array, $string); 之前插入echo $string . PHP_EOL;,输出为:

 dog
 dog food
 dog food car
 dog food bike
 dog tooth
 dog tooth car
 dog tooth bike
 cat
 cat food
 cat food car
 cat food bike
 cat tooth
 cat tooth car
 cat tooth bike

【讨论】:

  • Felix - 这适用于小型阵列。我刚刚在 5 个长度为 100 的数组上进行了尝试,得到了这个:Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 11 bytes) in /Users/qwerty/- on line 9 - 我不知道我会有那么多单词,所以你的解决方案可能适用于我正在做的事情。但在更大的阵列上肯定有一些滞后。感谢您的想法!
  • @Felix 还没有。刚看到。谢谢!
  • 挑剔:第二种解决方案不是“迭代”。您只需保留并重用中间结果(如缓存)。尽管如此,foreach($current_strings as $string) { concat($array, $string); 仍然是递归的。
  • @VolkerK:好的,我不确定,谢谢。但是对于不使用调用者函数中的返回值的递归是否有一个特殊的术语?
  • 如果有那么我不知道......我已经在我的回答和最后一条评论中用完了我今天/本周的“几乎但不完全正确”的解释;- ) 最好问问真正知道的人。
【解决方案2】:

您可以枚举结果集的元素,即对于 0....(元素数)-1 之间的每个整数,您可以判断要返回哪个元素(即存在自然顺序)。对于给定的示例:

0 => array1[0], array2[0], array3[0]
1 => array1[0], array2[0], array3[1]
2 => array1[0], array2[1], array3[0]
7 => array1[1], array2[1], array3[1]

您只需要一个(整数)索引 n 和一个将索引“翻译”为(自然有序)集合的第 n 个元素的函数。由于您只需要一个整数来存储当前状态,因此当您拥有许多/大型数组时,内存消耗不会“爆炸”。正如克里斯在他的评论中所说,你用速度(使用较小的集合时)换取低内存消耗。 (虽然我认为——php的实现方式——这也是一个合理的快速解决方案。)

$array1 = array('dog', 'cat');
$array2 = array('food', 'tooth');
$array3 = array('car', 'bike');

function foo( $key /* , ... */ ) {
  $params = func_get_args();
  $rv = array();

  $key = array_shift($params);
  $i=count($params);

  while( 0 < $i-- ) {
    array_unshift($rv, $params[$i][ $key % count($params[$i]) ]);
    $key = (int)($key / count($params[$i]));
  }
  return $rv;
}

for($i=0; $i<8; $i++) {
  $a = foo($i, $array1, $array2, $array3);
  echo join(', ', $a), "\n";
}

您可以使用它来实现例如Iterator SeekableIterator 甚至可能是 ArrayAccess(因此与递归解决方案相比,控制反转,几乎就像 python 或 ruby​​ 中的 yield

<?php
$array1 = array('dog', 'cat', 'mouse', 'bird');
$array2 = array('food', 'tooth', 'brush', 'paste');
$array3 = array('car', 'bike', 'plane', 'shuttlecraft');
$f = new Foo($array1, $array2, $array3);
foreach($f as $e) {
  echo join(', ', $e), "\n";
}

class Foo implements Iterator {
  protected $data = null;
  protected $limit = null;
  protected $current = null;

  public function __construct(/* ... */ ) {  
    $params = func_get_args();
    // add parameter arrays in reverse order so we can use foreach() in current()
    // could use array_reverse(), but you might want to check is_array() for each element.
    $this->data = array();
    foreach($params as $p) {
      // <-- add: test is_array() for each $p  -->
      array_unshift($this->data, $p);
    }
    $this->current = 0;
    // there are |arr1|*|arr2|...*|arrN| elements in the result set
    $this->limit = array_product(array_map('count', $params));
  }

  public  function current() {
    /* this works like a baseX->baseY converter (e.g. dechex() )
       the only difference is that each "position" has its own number of elements/"digits"
    */
    // <-- add: test this->valid() -->
    $rv = array();
    $key = $this->current;
    foreach( $this->data as $e) {
      array_unshift( $rv, $e[$key % count($e)] );
      $key = (int)($key/count($e));
    }
    return $rv;
  }

  public function key() { return $this->current;  }
  public function next() { ++$this->current; }
  public function rewind () { $this->current = 0; }
  public function valid () { return $this->current < $this->limit; }
}

打印

dog, food, car
dog, food, bike
dog, food, plane
dog, food, shuttlecraft
dog, tooth, car
dog, tooth, bike
[...]
bird, paste, bike
bird, paste, plane
bird, paste, shuttlecraft

(顺序似乎还可以;-))

【讨论】:

    【解决方案3】:

    我没有在巨大的单词列表上测试过这个,但是它在中等大小的列表上非常快并且不使用递归,我认为(如果我错了,请纠正我)可能会导致内存限制问题:

    $lines = array('');
    
    foreach ($arrays as $array) {
    
      $old_lines = $lines;
      $lines = array();
    
      foreach ($array as $word) {
    
        foreach ($old_lines as $line) {
    
          $lines[] = trim($line .' '. $word);
    
        } // foreach
    
      } // foreach
    
    } // foreach
    

    【讨论】:

    • 我猜内存限制是由大结果数组引起的,在你的方法中是一样的。但是打印线应该没问题。我的意思是数组中的 100^5 个元素很多;)
    • 在较小的阵列上效果很好,在大型阵列上不那么热。不过我喜欢!
    【解决方案4】:

    我的看法

    class Combinator
    {
         protected $words;
         protected $combinator;
    
         public function __construct($words, $combinator = null)
         {
             $this->words = $words;
             $this->combinator = $combinator;
         }
    
         public function run($combo = '')
         {
             foreach($this->words as $word) {
                 if($this->combinator !== null) {
                     $this->combinator->run("$combo $word"); 
                 } else {
                     echo "$combo $word", PHP_EOL;
                 }
             }
         }
    }
    
    $c = new Combinator(array('dog', 'cat'), 
                        new Combinator(array('food', 'tooth'),
                                       new Combinator(array('car', 'bike'))));
    
    $c->run();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 2021-05-19
      相关资源
      最近更新 更多