【问题标题】:generating unique combinations without running out of memory in php生成独特的组合而不会在php中耗尽内存
【发布时间】:2009-07-30 16:48:16
【问题描述】:

我正在编写一个算法来从数据库中生成项目组合。它们需要是唯一的排列(即 145、156 == 156、145)。我遇到的问题是如何跟踪以前的组合,这样我就不会得到 145、156 和 156、145。

目前我将它们添加到索引为 id1_id2... 的数组中(排序后 id 总是从最低到最高)并在生成组合时将值设置为 1,以便我可以检查 $combos[ $index] 存在与否。如果它不存在,则创建它。 (还有其他标准可以剔除每个排列,但它们无关紧要)一旦生成了这些组合,它们就会存储在 MySQL 的表中。

我遇到的问题是,对于我正在使用的测试项目(大约 85 个),我无法生成超过 3 个项目的组合(id1_id2_id3)而不会耗尽内存,因为组合的数量是 MASSIVE 并且$combos 数组占用的内存超过了我在 PHP 内存中分配的 64M。

有没有一种方法可以做到这一点 a) 不跟踪以前的组合或 b) 跳过 $combos 数组路由,只向 mysql 添加一个唯一行并让 mysql 处理重复检查。

这里有一些伪代码供参考:

$items = array(/*85 items*/);
foreach ($items as $item1){
    generate(array($item1));
        foreach($items as $item2){
            generate(array($item1, $item2));
        }
    }
}

function generate($items_arary){
    $temp_array = array();
    foreach ($items_array as $item){
        $temp_array[] = $item['id'];
    }

    sort($temp_array);
    $index = implode("_", $temp_array);

    if (!$combos[$index]){
        $combos[$index] = 1;
        /* some code to generate query to store to db */
    }
}

查询最终看起来像这样:(数据库在脚本开头被截断)

INSERT INTO `combos` (combo_id, more_info) VALUES ('id1_id2', 'Item Name');

在写这个问题的过程中,我想到了一个可能的解决方案:确保id3 > id2 > id1。这是消除对 $combos 需求的可行解决方案吗?

【问题讨论】:

  • 您能否提供有关数据来源的更多信息?您说它在数据库中,“之前”表的结构是什么。谢谢+1
  • 不太确定这有什么关系?

标签: php mysql algorithm


【解决方案1】:

我之所以询问 before 数据结构是因为你可以这样做:

$sql = "SELECT id FROM test_a";
$result = mysql_query($sql);
while ($row = mysql_fetch_array($result)) {
  $item1 = $row['id'];

  $sql2 = "SELECT id FROM test_a";
  $result2 = mysql_query($sql2);
  while ($row2 = mysql_fetch_array($result2)) {
    $item2 = $row2['id'];

    $combo1 = $item1 . "_" . $item2;
    $combo2 = $item2 . "_" . $item1;

    $sql3 = "SELECT * FROM combos WHERE combo_id = '$combo1' OR combo_id = '$combo2'";
    $result3 = mysql_query($sql3);
    if (mysql_num_rows($result3) == 0) {
      $sql4 = "INSERT INTO combos (combo_id, more_info) VALUES ('$combo1','Item Name')";
      $result4 = mysql_query($sql4);
    }
  }
}

当表 test_a 的值为 1、2、3 和 4 时,此脚本插入: 1_1 1_2 1_3 1_4 2_2 2_3 2_4 3_3 3_4 4_4

这应该没有任何内存问题。虽然如果你有一个庞大的数据库,你可能会遇到 php 的时间限制问题

【讨论】:

  • 是的,这是一个可行的选择,这正是我对 mysql 解决方案的考虑。我现在遇到了时间限制选项。
  • 通过选项你的意思是它有时间限制的问题?
  • 如下 - 如果您在第二个选择中添加 where 子句,则可以避免重复检查。
【解决方案2】:

这里的概念与我的其他答案相同,但采用全 SQL 格式。

INSERT INTO combos (combo_id, more_info) 
  SELECT CONCAT_WS("_",t1.id,t2.id), "item_name" 
  FROM test_a t1, test_a t2 
  WHERE NOT EXISTS (SELECT * FROM combos WHERE combo_id = CONCAT_WS("_",t1.id,t2.id))
    AND NOT EXISTS (SELECT * FROM combos WHERE combo_id = CONCAT_WS("_",t2.id,t1.id))

假设您可以从某处的数据库中获取 item_name,这可能是您最快且内存占用最少的解决方案。我目前正在对大约 1000 个 id 进行测试。完成后我会更新它。

【讨论】:

  • 您可以通过强加子句“WHERE t2.id >= t1.id”来消除子选择 - 这只会产生唯一的对。
【解决方案3】:

是的。您可以存储和使用组合的字典索引来重构/迭代它们,或者如果您需要迭代所有它们,则可以使用格雷码。

看看:“算法 515:从字典索引生成向量”; Buckles, B. P. 和 Lybanon, M. ACM 数学软件交易,卷。 3,第 2 期,1977 年 6 月。

我已经翻译成C语言here,并描述了更多here

【讨论】:

    【解决方案4】:

    如果您不需要自动强制执行参照完整性(如果您使用字符串连接则不需要),请为 85 个项目使用一个表,为每个项目指定一个索引 (0-84),然后使用第二个表来表示一组给定的项目,使用数字数据类型,其中数字中的每个位位置代表一个项目。 (例如 000001101 代表项目 0、2 和 3)

    对于超过 64 个的项目,您可能必须将它们拆分为多个字段,或者使用 BLOB 或字符串(gack!)。

    如果您将其用作主键字段,则可以强制不重复。

    【讨论】:

      【解决方案5】:

      在 TSQL 中,您可以使用递归 CTE,不记得我在哪里得到它,但很不错。注意 MYSQL 不使用“With”选项,所以它在 MySQL 中不起作用

      WITH Numbers(N) AS (
                          SELECT N
                          FROM ( VALUES(1), (2), (3), (4), (5), (6)) Numbers(N)),
                              Recur(N,Combination) AS (
                              SELECT N, CAST(N AS VARCHAR(20)) 
                              FROM Numbers
      
      
      UNION ALL
      
      SELECT n.N,CAST(r.Combination + ',' + CAST(n.N AS VARCHAR(10)) AS VARCHAR(20)) 
      FROM Recur r
      INNER JOIN Numbers n ON n.N > r.N)
      
      
      
      select Combination
      from RECUR
      ORDER BY LEN(Combination),Combination;
      

      【讨论】:

        【解决方案6】:

        增加内存变化

        memory_limit = 512M 在你的 php.ini

        php 脚本中的 ini_set('memory_limit', '512M')

        .htaccess 中的 php_value memory_limit 512M

        【讨论】:

        • 我知道我可以增加内存大小。它将在托管服务器上运行,我相当肯定我无法更改此值。除非 64M 小得离谱。
        • 64 M 绝对不小; 32 M 已经被一些托管公司认为是“相当大”的——而且我在 3 年中从未开发过需要超过 32 M 的“专业”应用程序(除了一些在晚上,通常)
        猜你喜欢
        • 1970-01-01
        • 2018-10-17
        • 2021-02-10
        • 1970-01-01
        • 2016-12-25
        • 2015-10-01
        • 2011-04-05
        • 2020-11-20
        相关资源
        最近更新 更多