【问题标题】:Finding every combination of elements that includes one value from each row in a 2D array in PHP在 PHP 中查找二维数组中每一行中包含一个值的每个元素组合
【发布时间】:2019-06-20 12:05:19
【问题描述】:

我在我的项目中使用更大的数组,但为了简化,假设我们有这个 3x3 数组:

$a = Array( Array(1,2,3),
            Array(4,5,6),
            Array(7,8,9) );

我想找到每行包含一个值的总和的每个组合,即 1+4+7、1+4+8、1+4+9、1+5+7、1+5+8 , 1+5+9, 1+6+7, 1+6+8, 1+6+9, 2+4+7, 2+4+8, 2+4+9, 2+5+7, . ..

希望模式很明显。我首先尝试了一个嵌套循环(列,然后是行),但这并没有提供所有组合。经过一番搜索,我确信解决方案需要递归,但每次我尝试为此编写递归函数时,我都会感到困惑。

虽然非常感谢工作代码,但对我来说可能更重要的是了解问题和解决方案。

【问题讨论】:

  • 如果您发布了无法工作的代码,也许有人可以帮助您了解问题所在。
  • 我删除它正是因为它不起作用。我为理解这一点所做的大部分工作都写在纸上。
  • 对于我们这样的初学者来说,解释起来并不容易。但是这个问题是找到到达矩阵左上角到右下角的最佳路径的精确匹配。对于上述场景,我们应该找到到达目的地的每条可能路径的距离。假设一名足球运动员在左上角,他需要以最小距离伸手到球门柱,因为您需要计算所有可能的距离并获得最小值。像那样你应该继续..如果你需要解决方案,请告诉我。我真的很乐意帮助你。谢谢顺便说一句。
  • 潜在解决方案的一个重要因素是要知道数组的行在列数上是否全部相等(例如,方阵),如您的示例所示?
  • 也许这会有所帮助:geeksforgeeks.org/… 适用于一维数组,但您可能会更好地理解这个问题。

标签: php recursion multidimensional-array


【解决方案1】:

给定数组中未知数量的行和列,获得所需结果的最佳方法是使用递归(否则您将不得不编写任意数量的嵌套循环)。此函数对数组中的每一行进行递归并返回两个结果之一:

  1. 如果我们位于数组的最后一行,则返回该行;
  2. 否则,返回当前行与数组余额结果之间的叉积(使用两个嵌套的foreach 循环)。我们使用array_shift 来获取当前行,并将其从数组中删除。

代码如下:

function find_paths($array) {
    if (count($array) == 1) return $array[0];
    $output = array();
    foreach (array_shift($array) as $v1) {
        foreach (find_paths($array) as $v2) {
            $output[] = array_merge(array($v1), is_array($v2) ? $v2 : array($v2));
        }
    }
    return $output;
}

使用您的示例数据:

$a = Array( Array(1,2,3),
            Array(4,5,6),
            Array(7,8,9) );
$combinations = find_paths($a);

函数做了这一系列的操作:

  1. 将第 1 行的叉积 (1, 2, 3) 与数组平衡函数的输出 ((4, 5, 6), (7, 8, 9)) 相乘;
  2. 第一次递归:取第 2 行的叉积 (4, 5, 6) 与函数的输出,用于数组的余额 ((7, 8, 9))
  3. 第二次递归:此时我们在数组(7, 8, 9)中只剩下一行,所以我们返回它;
  4. 第一次递归:计算(4, 5, 6)(7, 8, 9) = ((4, 7), (4, 8), (4, 9), (5, 7), (5, 8), (5, 9), (6, 7), (6, 8), (6, 9)) 的叉积并返回;
  5. (1, 2, 3)((4, 7), (4, 8), (4, 9), (5, 7), (5, 8), (5, 9), (6, 7), (6, 8), (6, 9)) 相乘,然后返回。

输出:

Array (
    [0] => Array ( [0] => 1 [1] => 4 [2] => 7 )
    [1] => Array ( [0] => 1 [1] => 4 [2] => 8 )
    [2] => Array ( [0] => 1 [1] => 4 [2] => 9 )
    [3] => Array ( [0] => 1 [1] => 5 [2] => 7 )
    [4] => Array ( [0] => 1 [1] => 5 [2] => 8 )
    [5] => Array ( [0] => 1 [1] => 5 [2] => 9 )
    [6] => Array ( [0] => 1 [1] => 6 [2] => 7 )
    [7] => Array ( [0] => 1 [1] => 6 [2] => 8 )
    [8] => Array ( [0] => 1 [1] => 6 [2] => 9 )
    [9] => Array ( [0] => 2 [1] => 4 [2] => 7 )
   [10] => Array ( [0] => 2 [1] => 4 [2] => 8 )
   [11] => Array ( [0] => 2 [1] => 4 [2] => 9 )
   [12] => Array ( [0] => 2 [1] => 5 [2] => 7 )
   [13] => Array ( [0] => 2 [1] => 5 [2] => 8 )
   [14] => Array ( [0] => 2 [1] => 5 [2] => 9 )
   [15] => Array ( [0] => 2 [1] => 6 [2] => 7 )
   [16] => Array ( [0] => 2 [1] => 6 [2] => 8 )
   [17] => Array ( [0] => 2 [1] => 6 [2] => 9 )
   [18] => Array ( [0] => 3 [1] => 4 [2] => 7 )
   [19] => Array ( [0] => 3 [1] => 4 [2] => 8 )
   [20] => Array ( [0] => 3 [1] => 4 [2] => 9 )
   [21] => Array ( [0] => 3 [1] => 5 [2] => 7 )
   [22] => Array ( [0] => 3 [1] => 5 [2] => 8 )
   [23] => Array ( [0] => 3 [1] => 5 [2] => 9 )
   [24] => Array ( [0] => 3 [1] => 6 [2] => 7 )
   [25] => Array ( [0] => 3 [1] => 6 [2] => 8 )
   [26] => Array ( [0] => 3 [1] => 6 [2] => 9 )
)

如果你想得到总和,你可以简单地在数组上使用array_map,调用array_sum来得到每个元素的总和:

$sums = array_map(function ($v) { return array_sum($v);}, $combinations);

输出:

Array (
    [0] => 12
    [1] => 13
    [2] => 14
    [3] => 13
    [4] => 14
    [5] => 15
    [6] => 14
    [7] => 15
    [8] => 16
    [9] => 13
   [10] => 14
   [11] => 15
   [12] => 14
   [13] => 15
   [14] => 16
   [15] => 15
   [16] => 16
   [17] => 17
   [18] => 14
   [19] => 15
   [20] => 16
   [21] => 15
   [22] => 16
   [23] => 17
   [24] => 16
   [25] => 17
   [26] => 18 
)

Demo on 3v4l.org

【讨论】:

  • 非常感谢您指导我了解它的工作原理!
  • @svadhisthana 不用担心。我很高兴这对您有所帮助。
【解决方案2】:

在对此进行了一段时间的修改后,我发现了另一种解决方案,它比尼克或我的原始答案更快,并且使用的内存也少得多。这在使用更大和更多的阵列时变得很重要。例如,不要使用 3 x 3(27 种组合),而是尝试 9 x 9(1.34 亿种组合)。

Nick 仍然应该得到公认的答案,因为他提供的代码不仅可以按要求工作,而且解释得很好。另外,这段代码借鉴了 Nick 代码中的一些逻辑。

性能技巧是,我们只得到总和,而不是找到所有组合然后得到总和。这样一来,我们就不会创建一个潜在的庞大数组,而只是创建一个潜在的庞大数组。

编辑:通过引用传递$sums 而不是使用array_merge 可以显着节省更多时间和内存。代码已更新。

function combo_sums($arrays, $sum = 0, &$sums = []) {
    $array = array_shift($arrays);
    if (count($arrays) > 0)
        foreach ($array as $value)
            combo_sums($arrays, $sum + $value, $sums);
    else
        foreach ($array as $value)
            $sums[] = $sum + $value; # These are the final sums.
    return $sums;
}

【讨论】:

  • 如果您只想要总和,这绝对是一种更有效的方法。
  • @Nick:我需要了解如何正确遍历数组,然后才能处理总和。递归思考是一种学习经验。再次感谢!
【解决方案3】:

经过更多搜索,我在这里找到了解决方案:https://gist.github.com/cecilemuller/4688876

我用上面的示例数据对其进行了测试,它提供的正是我需要的组合。使用array_map 查找尼克答案中的总和按预期工作。

令我惊讶的是,它不依赖于递归,而是依赖于三个嵌套循环。希望我能理解它是如何工作的。

【讨论】:

    猜你喜欢
    • 2021-03-15
    • 1970-01-01
    • 2017-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-13
    • 2016-03-16
    • 2010-11-16
    相关资源
    最近更新 更多