使用 PHP 关联数组查找笛卡尔积 [英] Finding cartesian product with PHP associative arrays

查看:25
本文介绍了使用 PHP 关联数组查找笛卡尔积的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个如下所示的数组:

Say that I have an array like the following:

Array
(
    [arm] => Array
        (
            [0] => A
            [1] => B
            [2] => C
        )
    [gender] => Array
        (
            [0] => Female
            [1] => Male
        )
    [location] => Array
        (
            [0] => Vancouver
            [1] => Calgary
        )
)

如何在保留外部关联数组的键并在内部关联数组中使用它们的同时找到笛卡尔积?算法的结果应该是这样的:

How can I find the cartesian product while preserving the keys of the outer associative array and using them in the inner ones? The result of the algorithm should be this:

Array
(
    [0] => Array
        (
            [arm] => A
            [gender] => Female
            [location] => Vancouver
        )

    [1] => Array
        (
            [arm] => A
            [gender] => Female
            [location] => Calgary
        )

    [2] => Array
        (
            [arm] => A
            [gender] => Male
            [location] => Vancouver
        )

...etc.

我已经查找了很多笛卡尔积算法,但我一直被困在如何保留关联键的细节上.我使用的当前算法仅给出数字索引:

I've looked up quite a number of cartesian product algorithms but I'm getting stuck on the specifics of how to preserve the associative keys. The current algorithm I am using gives numerical indices only:

    $result = array();
    foreach ($map as $a) {
        if (empty($result)) {
            $result = $a;
            continue;
        }
        $res = array();
        foreach ($result as $r) {
            foreach ($a as $v) {
                $res[] = array_merge((array)$r, (array)$v);
            }
        }
        $result = $res;
    }

    print_r($result);

任何帮助将不胜感激.

推荐答案

这是一个我不会羞于展示的解决方案.

Here's a solution I wouldn't be ashamed to show.

假设我们有一个带有 N 个子数组的输入数组 $input,如您的示例所示.每个子数组有Cn项,其中n是它在$input中的索引,它的key是Kn.我将第 n 个子数组的 i 项称为 Vn,i.

Assume that we have an input array $input with N sub-arrays, as in your example. Each sub-array has Cn items, where n is its index inside $input, and its key is Kn. I will refer to the ith item of the nth sub-array as Vn,i.

可以通过归纳证明以下算法有效(排除错误):

The algorithm below can be proved to work (barring bugs) by induction:

1) 对于 N = 1,笛卡尔积就是 array(0 => array(K1 => V1,1), 1 => array(K1 => V1,2),... ) -- 总共 C1 项.这可以通过一个简单的 foreach 来完成.

1) For N = 1, the cartesian product is simply array(0 => array(K1 => V1,1), 1 => array(K1 => V1,2), ... ) -- C1 items in total. This can be done with a simple foreach.

2) 假设 $result 已经保存了前 N-1 个子数组的笛卡尔积.$result 和第 N 个子数组的笛卡尔积可以这样产生:

2) Assume that $result already holds the cartesian product of the first N-1 sub-arrays. The cartesian product of $result and the Nth sub-array can be produced this way:

3) 在 $product 内的每个项目(数组)中,添加值 KN =>VN,1.记住结果项目(带有附加值);我将其称为 $item.

3) In each item (array) inside $product, add the value KN => VN,1. Remember the resulting item (with the added value); I 'll refer to it as $item.

4a) 对于 $product 中的每个数组:

4a) For each array inside $product:

4b) 对于集合 VN,2 ... VN,CN 中的每个值,将 $item 的副本添加到 $product>,但将键值 KN 更改为 VN,m(对于所有 2 <= m <= CN).

4b) For each value in the set VN,2 ... VN,CN, add to $product a copy of $item, but change the value with the key KN to VN,m (for all 2 <= m <= CN).

两次迭代 4a(在 $product 上)和 4b(在第 N 个输入子数组上)以 $result 结束,具有 CN 迭代之前它拥有的每个项目的项目,所以最后 $result 确实包含前 N 个子数组的笛卡尔积.

The two iterations 4a (over $product) and 4b (over the Nth input sub-array) ends up with $result having CN items for every item it had before the iterations, so in the end $result indeed contains the cartesian product of the first N sub arrays.

因此该算法适用于任何 N.

Therefore the algorithm will work for any N.

这比本来应该更难写.我的正式证明肯定会生锈...

function cartesian($input) {
    $result = array();

    while (list($key, $values) = each($input)) {
        // If a sub-array is empty, it doesn't affect the cartesian product
        if (empty($values)) {
            continue;
        }

        // Seeding the product array with the values from the first sub-array
        if (empty($result)) {
            foreach($values as $value) {
                $result[] = array($key => $value);
            }
        }
        else {
            // Second and subsequent input sub-arrays work like this:
            //   1. In each existing array inside $product, add an item with
            //      key == $key and value == first item in input sub-array
            //   2. Then, for each remaining item in current input sub-array,
            //      add a copy of each existing array inside $product with
            //      key == $key and value == first item of input sub-array

            // Store all items to be added to $product here; adding them
            // inside the foreach will result in an infinite loop
            $append = array();

            foreach($result as &$product) {
                // Do step 1 above. array_shift is not the most efficient, but
                // it allows us to iterate over the rest of the items with a
                // simple foreach, making the code short and easy to read.
                $product[$key] = array_shift($values);

                // $product is by reference (that's why the key we added above
                // will appear in the end result), so make a copy of it here
                $copy = $product;

                // Do step 2 above.
                foreach($values as $item) {
                    $copy[$key] = $item;
                    $append[] = $copy;
                }

                // Undo the side effecst of array_shift
                array_unshift($values, $product[$key]);
            }

            // Out of the foreach, we can add to $results now
            $result = array_merge($result, $append);
        }
    }

    return $result;
}

用法

$input = array(
    'arm' => array('A', 'B', 'C'),
    'gender' => array('Female', 'Male'),
    'location' => array('Vancouver', 'Calgary'),
);

print_r(cartesian($input));

这篇关于使用 PHP 关联数组查找笛卡尔积的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆