在给定的大小为n的数组中找到r元素的可能组合

时间:2017-10-05 08:49:04

标签: php

每个人都知道在给定的n大小算法数组中找到r元素的可能组合,但我还需要更进一步。

我有一系列物品。这些项是数组,有id,name,group_id bla bla bla。

我必须使用不同的group_id计算可能的组合。如果Items具有相同的group_id,则无法创建组合。

这是我计算所有可能组合的函数;

private function calculateComb(array $temp, int $start, int $end, int $index, int $size)
    {
        try {
            if ($index === $size) {
                $this->groups[$size]['combinations'][] = $temp;
                return;
            }
            for ($i = $start; $i < $end; $i++) {
                $temp[$index] = $this->combinationItems[$i];
                $this->calculateComb($temp, $i + 1, $end, $index + 1, $size);
            }
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
    }

我尝试使用group_id创建一个新函数来计算可能的组合;

private function calculateCombinations(array $temp, int $start, int $end, int $size, int $index, array &$groupIds)
    {
        try {
            if ($index === $size) {
                $this->groups[$size]['combinations'][] = $temp;
                unset($groupIds[$index]);
                return;
            }
            for ($i = $start; $i < $end; $i++) {
                if (in_array($this->combinationItems[$i]['group_id'], $groupIds)) {
                    $this->calculateCombinations($temp, $i + 1, $end, $size, $index, $groupIds);
                } else {
                    $temp[$index] = $this->combinationItems[$i];
                    $groupIds[$index] = $this->combinationItems[$i]['group_id'];

                    $this->calculateCombinations($temp, $i + 1, $end, $size, $index + 1, $groupIds);
                }
            }
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
    }

但这不起作用。你有什么想法吗?

数组示例;

[
    [
        'id': 1,
        'name': 'Test',
        'group_id': 1
    ], [
        'id': 2,
        'name': 'Test2',
        'group_id': 1
    ], [
        'id': 3,
        'name': 'Test3',
        'group_id': 2
    ],
    .
    .
    .
    .
    .
]

感谢。

2 个答案:

答案 0 :(得分:0)

2元素组合的解决方案

我不知道你是否想要一个奇特的解决方案,但这可能是一个非常简单的解决方案,基本上,你所做的是在不同的group_id上加入。以下本质上是嵌套循环连接。如果你想要/需要更高效/更快,可能会有不同的连接算法更适合,但我认为嵌套循环连接在这种情况下非常有效。

嵌套循环连接:

$output = [];
foreach($array as $left) {
    foreach($array as $right) {
        if($left['group_id'] != $right['group_id']) { // <-- join condition
             // create output, you probably want something else here...
             $output[] = ['left' => $left, 'right' => $right];
        }
    }
}

这会创建所有组合:如果(a,b)是组合,(b,a)也是组合。 如果这不是你想要的,你也可以包括密钥:

// ...
foreach($array as $lid => $left) { // <-- notice the l(eft) id
    foreach($array as $rid => $right) { // <-- notice r(ight) id
        if($lid < $rid && $left['group_id'] != $right['group_id']) {
           // ^^^^^^^ this ensures only one of the two combination will be joined
        }
    }
}

(编辑......)

k - 元素组合,其中k ==组数

现在没有太多解释,如果您需要解释,请发表评论。 但是这段代码可能会有一些小错误,但是没有对它进行测试。

  1. 构建一个数组($grouped),其中每个元素都是一个包含具有相同group_id的项的数组,所以基本上

    $grouped = [];
    foreach($array as $item) {
        if(empty($grouped[$item['group_id']]) $grouped[$item['group_id']] = [];
        $grouped[$item['group_id']][] = $item; 
    }
    
  2. 递归地取每组的一个元素:

    $grouped = array_values($grouped); // this simplifies keys
    
    function get_combinations($grouped, $i) {
        // in the k-th group, return all elements
        if(count($grouped)-1 == $i) {
            $ret = [];
            foreach($grouped[$i] as $item) {
                $ret[] = [$item];
            }
            return $ret;
        }
    
        // in the $i-th group ($i < k), build combinations of each element
        // in i-th group with every combination given from sub-call
        $subcombinations = get_combinations($grouped, $i+1);
        $result = []; // <--- REFERENCED LINE ...
        foreach($grouped[$i] as $item) {
            foreach($subcombinations as $comb) {
                $result[] = array_merge([$item], $comb);
            }
        }
        // return new combinations
        return $result;
    }
    
    $combinations = get_combinations($grouped, 0);
    
  3. 现在,如果包含许多大型组,这对于非常大的阵列来说可能非常低效。

    与至少一个项目的所有组合

    如果你还想计算那些含有k个元素的组合,你可以用

    替换引用的行(参见上面代码中的REFERENCED LINE)
    // add combinations without current group to result
    // also add single-element combinations where element is from current group
    $result = array_merge($subcombinations, get_combinations([$grouped[$i]],0));
    

答案 1 :(得分:0)

最后我再次找到了我的解决方案:)

private function calculateCombinations(array $temp, int $start, int $end, int $size, int $index, array &$groupIds)
    {
        try {
            if ($index === $size) {
                $this->groups[$size]['combinations'][] = $temp;
                $groupIds = [];
                return;
            }
            for ($i = $start; $i < $end; $i++) {
                if (in_array($this->combinationItems[$i]['group_id'], $groupIds)) {
                    continue;
                } else {
                    $temp[$index] = $this->combinationItems[$i];
                    $groupIds[$index] = $this->combinationItems[$i]['group_id'];

                    $this->calculateCombinations($temp, $i + 1, $end, $size, $index + 1, $groupIds);
                }
            }
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
    }