在php中生成独特的组合而不会耗尽内存

时间:2009-07-30 16:48:17

标签: php mysql algorithm

我正在编写一种算法来生成数据库中的项目组合。它们需要是唯一的排列(即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的需求?

6 个答案:

答案 0 :(得分:3)

我之前询问过数据结构的原因是因为你可以这样做:

$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的时间限制问题

答案 1 :(得分:1)

这是与我的其他答案相同的概念,但采用的是所有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))

假设您可以从某个地方的db获取item_name,这可能是您最快且内存最少的解决方案。我目前正在对大约1000个ids进行测试。完成后我会更新。

答案 2 :(得分:0)

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

看看:“算法515:从字典索引生成矢量”; Buckles,B.P。和Lybanon,M。ACM Transactions on Mathematical Software,Vol。 1977年6月3日第2期。

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

答案 3 :(得分:0)

如果您不需要自动强制执行参照完整性(如果使用字符串连接则不是这样),对85个项目使用一个表,为每个项目分配一个索引(0-84),并使用第二个表用于表示给定项集的表,使用数字数据类型,其中数字中的每个位位置代表一个项。 (例如000001101表示项目0,2和3)

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

如果您将此作为主键字段使用,则可以强制执行非重复项。

答案 4 :(得分:0)

在TSQL中,您可以使用递归CTE,但不记得我在哪里获得它,但非常甜蜜。注意MYSQL没有使用"使用"选项,所以它不会在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;

答案 5 :(得分:-1)

增加记忆力变化

你的php.ini中的

memory_limit = 512M 或
你的php脚本中的ini_set('memory_limit','512M') 或
你的.htaccess中的php_value memory_limit 512M