【问题标题】:Permutations/combinations on PHPPHP上的排列/组合
【发布时间】:2015-02-12 15:41:38
【问题描述】:

我正在尝试编写一个脚本来执行从 0 到 45 的 6 个数字的所有排列/组合,没有重复,但它不起作用,因为某些数字在同一行重复。

我做错了什么?

代码:

for($a=0; $a<45-5; $a++)
    for($b=$a+1; $b<45-4; $b++)
        for($c=$b+1; $c<45-3; $c++)
            for($d=$c+1; $d<45-2; $d++)
                for($e=$d+1; $d<45-1; $d++)
                    for($f=$e+1; $d<45; $d++)
                echo "$a $b $c $d $e $f \n";

我正在测试另一个代码,但收到此错误:

致命错误:允许的内存大小为 33554432 字节已用尽(尝试分配 2348617 字节)

<?php
function permutations($arr,$n)
{
     $res = array();

     foreach ($arr as $w)
     {
           if ($n==1) $res[] = $w;
           else
           {
                 $perms = permutations($arr,$n-1);

                 foreach ($perms as $p)
                 {
                      $res[] = $w." ".$p."<p>";
                 } 
           }
     }

     return $res;
}

$words = array('00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45');

$pe = permutations($words,6);

print_r($pe);
?>

我做错了什么?

谢谢

【问题讨论】:

  • 所以00 01 02..01 00 02.. 都应该出现,而不是00 00 01?请记住,45^6 大约是 80 亿。你真的需要 80 亿个结果吗?你肯定会用完内存。
  • 是的,我需要所有的组合,不要重复任何数字。我知道有近 9 百万种组合,但我需要将所有这些数字插入数据库或 csv 或文本文件中。

标签: php permutation


【解决方案1】:

这将生成所有排列(不是组合):

$words = array("00", "01", "02", "03", "04");

pick($words, 3);

function pick($words, $num, $picked = array()) {
    for ($i = 0; $i < count($words); $i += 1) {
        $word = $words[$i];
        $remaining_words = array_diff($words, array($word));
        if ($num > 1) {
            // pick the remaning $num-1 words
            pick(array_values($remaining_words), $num - 1, array_merge($picked, array($word)));
        } else {
            echo implode(",", array_merge($picked, array($word))) . "\n";
        }
    }
}

它会从m 选项中挑选n 字词,以便您获得m! / (m-n)! 结果。对于n=3m=5,你会得到6045!/39! 给了5,864,443,200


由于您希望输出为 csv,您可以对其进行修改:

$handle = fopen("perms.csv", "w");

// code

    // instead of echo:
    fputcsv($handle, array_merge($picked, array($word)));

// end code

fclose($handle);

这根本不应该消耗太多内存。它将有一个上限为$num*$words 元素,它应该远低于1mb(深度优先是好的,广度优先会很糟糕)。现在输出文件会很大:P


我不知道生成这个文件需要多长时间。我建议您进行一些测试。您可能需要set_time_limit(0); 为您的脚本提供无限时间,但首先尝试较低的值(或准备终止脚本)。可能有办法让它更快,有些数组逻辑不是特别快。

【讨论】:

    【解决方案2】:

    您的第一个程序有一些拼写错误:$e$f 的 for 循环包含 $d。如果我改变它,这个程序的输出对我来说看起来没问题。它找到了 8145060 个排列。

    您的第二个解决方案适用于递归,这显然嵌套太深,因此超出了您拥有的内存。

    【讨论】:

      【解决方案3】:

      PHP 每个变量至少使用 72 字节的内存,80 亿个结果大约需要 600 GB 的内存。 PHP7,又名 PHPNG 将其减少到 56 个字节,但这对您的情况没有多大帮助。即使在编译语言中,它也会很大。

      当我过去完成需要大量结果的单词组合时,我最终将其写入文件,因此它处理数据块并批量写入文件。它是更复杂的代码,但它是做这种事情所必需的。

      对于处理大型数据集,您可以做的另一件事是,如果您不需要它们都可以轻松访问并且可以分块工作,您可以对数组集进行 json 编码,这样就不会产生每个变量的开销。但在你的情况下,这仍然是徒劳的。

      看起来你有 PHP 允许 32MB 的 RAM,如果这是一个受控环境,你可以调用:

      ini_set('memory_limit','256M');
      

      设置当前脚本的最大内存。即使这样,您也会耗尽内存并需要重构代码。

      【讨论】:

      • 我修改了代码,还是不能正常工作:for($a=0; $a
      • 在官方文档中查找 PHP fopen 和 PHP fputcsv。然后我会让最里面的循环添加到数组而不是回显,也许“d”循环会写入 csv 文件或文本文件。为了更快的测试,我会将 45 减少到 10。在递归方法上,您可以根据文件的深度写入文件。
      猜你喜欢
      • 2020-08-12
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 1970-01-01
      • 2012-05-30
      相关资源
      最近更新 更多