从数组的左上角到右下角的数量

时间:2017-09-13 19:26:57

标签: arrays algorithm perl recursion language-agnostic

我正在玩弄以下问题:

  

给定一个二维数组找到我们可以从顶部移动的所有可能方式   将单元格[0][0]左侧单元格[N-1][N-1]留给我们   只能向下或向右移动?

我定义了以下内容:
对于诸如[ [ 1 ] ]之类的数组,从起始单元到目标单元只有一种方法。我们已经在那里了 否则它的总路数是我们从单元格到右边到目的地的总方式加1(从当前单元格到下一个单元格有1种方法)加上我们的总方式下面的单元格加上1(从当前单元到波纹管单元有1种方法)
所以对于像这样的数组:

[  
  [1, 2]  
  [3, 4]  
]  

答案是4(1-> 2,2-> 4,1-> 3,3-> 4)。
对于如下数组:

[   
  [1, 2, 3],  
  [3, 4, 5],   
]  

答案应该是8. 4来自右边的子阵列+ 1来取代(1) - >(2)加上1> 3 3-> 4 4-> 5总计3。
所以5 + 3 = 7 以下代码在我看来是正确的,但我搞砸了一些东西,我得到了错误的结果。

my $array = [
    [1, 2, 3],
    [3, 4, 5],
];

sub number_of_ways {
    my ( $input, $source_row, $source_col, $dest_row, $dest_col ) = @_;

    if ( $source_row == $dest_row && $source_col == $dest_col ) {
        return 1;
    }

    if ( $source_row >= scalar @$input) {
        return 0;
    }
    if ( $source_col >= scalar @{$input->[$source_row]} ) {
        return 0;
    }

    my $ways_down = number_of_ways($input, $source_row + 1, $source_col, $dest_row, $dest_col);
    my $ways_right = number_of_ways($input, $source_row, $source_col + 1, $dest_row, $dest_col); 
    my $total = $ways_right + 1 if ( $ways_right );
    $total += $ways_down + 1 if ( $ways_down );
    return $total;
}

print "Number of ways: " . number_of_ways($array, 0, 0, 1, 2). "\n";  

这给了我11.
逻辑错了吗?

更新
在@ m69的帮助下,我发现了问题 如果我们已经可以检查它是否会失败,那么在递归中进行迭代是一个坏主意。在这种情况下,即使在@ m69的注释之后更改代码也失败了,因为0之间没有区别,因为我们在一个子元素中有1个元素(源和目标是相同的)或在数组之外。
以下代码似乎是正确的。

sub number_of_ways {
    my ( $input, $source_row, $source_col, $dest_row, $dest_col ) = @_;

    if ( $source_row == $dest_row && $source_col == $dest_col ) {
        return 0;
    }

    my $total = 0;
    if ( $source_row < @$input - 1) {
        my $ways_down = number_of_ways($input, $source_row + 1, $source_col, $dest_row, $dest_col);
        $total += $ways_down + 1;
    }
    if ( $source_col < @{$input->[$source_row]} - 1 ) {
        my $ways_right = number_of_ways($input, $source_row, $source_col + 1, $dest_row, $dest_col); 
        $total += $ways_right + 1;
    }

    return $total;
}

print "Number of ways: " . number_of_ways($array, 0, 0, 1, 2). "\n";

1 个答案:

答案 0 :(得分:3)

您的算法使用此递归:

0   1   2       0---1   2       0
            =               +   |
3   4   5           4   5       3   4   5

然后继续:

1   2       1---2       1          
        =           +   |       AND
4   5           5       4   5         3   4   5   =   3---4   5

然后:

2       2
    =   |   AND                       AND
5       5         4   5   =   4---5         4   5   =   4---5

最后:

5   AND   5   AND   5

本身,这是在3x2网格中递归的有用方法,但是你添加步骤的方式是有问题的;例如在使用2x2网格[[1,2] [4,5]]进行递归后收到4之后,为它添加1,因为从位置0到2x2网格需要1步。但是,通过2x2网格有两条路径,因此您应该将两步添加两步。知道通过2x2网格有多少路径需要计算通过它的Taxicab距离,然后将步数除以该距离。您会发现这导致了许多不必要的计算,因为每个完整路径中的步数始终相同。因此,只需找到路径数量,然后将它们乘以每个路径的步数即可。

您可以使用递归来查找路径数;从分解到上面的递归步骤,你会看到你最终得到1x1网格[5]三次。这是因为有三条路径通向位置5.如果您只计算使用该1x1网格递归的次数,则可以知道路径的数量。要知道步数,可以乘以(宽度 - 1)+(高度 - 1),这是每个路径中的步数。

简单地将变量递增到递归范围之外的缺点是你不能轻易地将它变成动态编程解决方案,因为你必须经历每次递归到最后,计算你到达的次数右下角。因此,将结果传递回递归链可能更好。

如果在输入为1x1网格时返回1,并且右侧和下侧之和导致更大的网格(不向其添加任何内容),则还会为您提供路径总数。然后你可以通过存储2x1和1x2网格返回1,2x2网格返回2,3x2和2x3网格返回3,3x3网格返回6,并使用这些存储的值而不是使用相同的网格递归来存储结果再次大小。

您将看到此表格给出了通过任意大小网格的路径数量:

1  1  1  1  1 ...
1  2  3  4  5
1  3  6 10 15
1  4 10 20 35
1  5 15 35 70

其中每个值是它上面和左边的值的总和,这也指向一种简单的非递归方式来计算通过任何大小网格的路径(或步数)。

JavaScript中的这段代码片段使用代码中的递归方法来计算路径数,然后计算总步数:

&#13;
&#13;
function count_paths(grid, x, y) {
    x = x || 0; y = y || 0;
    var height = grid.length - y;
    var width = grid[0].length - x;
    if (width == 0 || height == 0) return 0;
    if (width == 1 && height == 1) return 1;
    return count_paths(grid, x + 1, y) + count_paths(grid, x, y + 1);
}

var grid = [[0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]];
var paths = count_paths(grid);
var steps = paths * (grid.length - 1 + grid[0].length - 1);
document.write("paths: " + paths + "<br>steps: " + steps);
&#13;
&#13;
&#13;

要将总步数计算整合到递归中,您必须返回路径数和步数,以便您可以将所需步数乘以向右或向下移动该步骤导致的路径数量:

&#13;
&#13;
function count_steps(grid, x, y) {
    x = x || 0; y = y || 0;
    var height = grid.length - y;
    var width = grid[0].length - x;
    if (width == 0 || height == 0) return {paths: 0, steps: 0};
    if (width == 1 && height == 1) return {paths: 1, steps: 0};
    var right = count_steps(grid, x + 1, y);
    var down = count_steps(grid, x, y + 1);
    var paths = right.paths + down.paths;
    var steps = right.steps + down.steps + paths;
    return {paths: paths, steps: steps};
}

var grid = [[0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]];
var count = count_steps(grid);
document.write("paths: " + count.paths + "<br>steps: " + count.steps);
&#13;
&#13;
&#13;