如何修改传统的笛卡尔积以减少内存开销?

时间:2015-05-14 13:25:16

标签: php algorithm cartesian-product

对于我的问题,您可以从5-10,000个项目的池中选择最多24个项目。换句话说,我们正在生成配置。

数字24来自项目类别,每个项目与特定安装位置相关联,位置1中的项目无法安装在位置10中,因此我已安排我的关联数组来组织数据。每个项目看起来像:

$items[9][] = array("id" => "0", "2" => 2, "13" => 20);

第一个参数($item[9])告诉您允许的位置。如果您想要,可以考虑不能在排气管的现场安装轮胎的想法。

项目存储在mySQL数据库中。用户可以指定对解决方案的限制,例如,属性2的最终值必须为25或更多。他们可能有多种竞争限制。查询检索具有所考虑属性的任何值的项目(存储未指定的属性,但我们不对它们进行任何计算)。 PHP脚本然后删除任何冗余选择(例如:如果项目1的属性值为3,项目2的属性值为5,则在没有其他选项的情况下 限制你永远不会选择第1项。)

在完成所有处理之后,获得一个类似于:

的关联数组
$items[10][] = array("id" => "3", "2" => 2, "13" => 100);
$items[10][] = array("id" => "4", "2" => 3, "13" => 50);
$items[9][] = array("id" => "0", "2" => 2, "13" => 20);
$items[9][] = array("id" => "1", "2" => -1, "13" => 50);

我在this pastebin link.发布了一个完整的示例数据集我有理由相信我对数据集中接受的内容更加严格,但即使每个选项限制为2个元素也存在问题。< / p>

在array()值中,id是对数组中项的索引的引用,其他值是属性id和值对。所以$items[10][] = array("id" => "3", "2" => 2, "13" => 100);意味着在位置10中有一个id为3的项目,属性2中的值为2,属性13中的值为100.如果它有助于想到一对项目被识别,例如( 10,0)是位置10中的项目0。

我知道我不具体,有61个属性,我认为它不会改变问题的结构与它们所代表的内容。如果需要,我们可以将属性2视为权重,将属性13视为成本。用户想要解决的问题可能是生成一个配置,其中权重精确到25并且成本最小化。

信封背面数学表示粗略估计,如果每个位置只有2个选项,则是2 ^ 24个选择x记录的大小。假设一个32位整数可以编码以某种方式表示单个记录,我们正在查看16,777,216 * 4 = 67,108,864字节的内存(完全忽略数据结构开销),并且没有理由相信这些假设中的任何一个都会虽然在67兆的范围内绑定了高端内存的算法是可接受的内存大小,但它是有效的。

没有特别的理由坚持这种表示,我使用了关联数组,因为我有一些可变数量的属性可供使用,并且可以让我避免使用大型稀疏数组。高于“2”=&gt; 2实际上意味着id#2的过滤属性值为2,类似属性13的值为100.我很乐意将我的数据结构更改为更紧凑的内容。

我有一个想法是我确实有一个评估标准可以用来丢弃大多数中间配置。例如,我可以计算75 *“2”“+ 10 *”值“13”以提供解决方案的相对权重。换句话说,如果对问题没有其他限制,则每个值属性2中的1的改进成本75和属性13的每个值的改进成本10.继续了解汽车零件的想法,将其想象为购买库存零件并让机械师根据我们的规格对其进行修改。

我看到过早丢弃配置的一个问题是加权函数没有考虑诸如“最终结果必须具有”2“的限制,例如恰好为25”。所以,如果我有一个完整的24元素配置,我可以通过一个限制循环,丢弃不匹配的解决方案,然后最终按功能对其余解决方案进行排名,但是我不确定它是否有效允许我早点丢弃解决方案的想法。

有没有人对如何前进有任何建议?虽然语言无关的解决方案很好,但如果有一些可能有用的相关语言功能,我将在PHP中实现。

1 个答案:

答案 0 :(得分:0)

我通过执行深度优先的笛卡尔积解决了我的记忆问题。我可以一次称量一个解决方案,如果我选择或者只是输出它们就可以保留一些,就像我在这个代码片段中所做的那样。

此解决方案的主要灵感来自the very concise answer on this question。这是我的代码,因为它似乎找到一个PHP深度的第一个笛卡尔积算法并不简单。

function dfcartesian ( $input, $current, $index ) {
    // sample use: $emptyArray = array();
    //             dfcartesian( $items, $emptyArray, 0 )
    if ( $index == count( $input ) ) {
        // If we have iterated over the entire space and are at the bottom
        // do whatever is relevant to your problem and return.
        //
        // If I were to improve the solution I suppose I'd pass in an
        // optional function name that we could pass data to if desired.
        var_dump( $current );
        echo '<br><br>';
        return;
    }

    // I'm using non-sequential numerical indicies in an associative array
    // so I want to skip any empty numerical index without aborting.
    //
    // If you're using something different I think the only change that
    // needs attention is to change $index + 1 to a different type of
    // key incrementer.  That sort of issue is tackled at
    // https://stackoverflow.com/q/2414141/759749
    if ( isset ( $input[$index] ) ) {
        foreach ( $input[$index] as $element ) {
            $current[] = $element;
            // despite my concern about recursive function overhead,
            // this handled 24 levels quite smoothly.
            dfcartesian( $input, $current, ( $index + 1 ) );
            array_pop( $current );
        }
    } else {
        // move to the next index if there is a gap
        dfcartesian( $input, $current, ( $index + 1 ) );
    }
}   

我希望这对处理同样问题的其他人有用。