为什么我的递归函数更新列表(计算n的斐波那契)

时间:2018-08-15 17:56:09

标签: python recursion dynamic-programming fibonacci memoization

我正在尝试学习动态编程。我查看了一个示例,该示例如何查找输入n的斐波那契数,同时在我进行操作时缓存每个“新”调用的结果。

我了解函数递归调用的顺序:fib(5)-> fib(4)-> fib(3)-> fib(2)-> fib(1)-> fib(0)-> fib(1)-> fib(2)“找到缓存”-> fib(3)“找到缓存” n = 5

我正在努力了解最终的fib(2)和fib(3)调用如何访问更新后的缓存,因为每个调用仅返回一个整数,而不返回列表,并且我认为我没有声明为全局变量的列表。

我本来希望列表在代码中表现得像整数x,所以欢迎解释如何将值传递回。

代码:

def fib(n, cache, x):
    print(n, cache)
    print("x: ", x)
    x += 1
    if n == 0 or n == 1:
        return n

    if cache[n] == 0:
        cache[n] = fib(n-1, cache, x) + fib(n-2, cache, x)
    else:
        print("Cache called on", n)

    return cache[n]


def main():
    n = 5
    x = 0
    cache = [0 for _ in range(n+1)]
    print(fib(n, cache, x))
    print(cache)
    print("x: ", x)


if __name__ == "__main__":
    main()

输出:

5 [0, 0, 0, 0, 0, 0]
x:  0
4 [0, 0, 0, 0, 0, 0]
x:  1
3 [0, 0, 0, 0, 0, 0]
x:  2
2 [0, 0, 0, 0, 0, 0]
x:  3
1 [0, 0, 0, 0, 0, 0]
x:  4
0 [0, 0, 0, 0, 0, 0]
x:  4
1 [0, 0, 1, 0, 0, 0]
x:  3
2 [0, 0, 1, 2, 0, 0]
x:  2
Cache called on 2
3 [0, 0, 1, 2, 3, 0]
x:  1
Cache called on 3
5
[0, 0, 1, 2, 3, 5]
x:  0

2 个答案:

答案 0 :(得分:1)

您传递了cache的原件,而不是副本(新建对象)。因此,fib的每个实例都与 same 对象一起工作。一个实例中的更新立即可供其他实例使用。另请参见here

答案 1 :(得分:1)

在python函数中,参数是“对象传递”(或对象引用传递)。这意味着,如果将列表(可变对象)传递给函数,则可以修改列表的元素。但是,如果您要通过新列表分配列表,则该列表在调用者的范围内不会更改。

public static void convertToExcel(string fileName, string splitter, string extension)
{
    string newFileName = fileName.Replace("." + extension, ".xls");

    string[] lines = File.ReadAllLines(fileName, Encoding.UTF8);

    int columnCounter = 0;

    foreach (string s in lines)
    {
        string[] ss = s.Trim().Split(Convert.ToChar(splitter));

        if (ss.Length > columnCounter)
            columnCounter = ss.Length;
    }           

    HSSFWorkbook workbook = new HSSFWorkbook();
    var sheet = workbook.CreateSheet("Data");
    var rowIndex = 0;
    var rowExcel = sheet.CreateRow(rowIndex);

    foreach (string s in lines)
    {
        rowExcel = sheet.CreateRow(rowIndex);

        string[] ss = s.Trim().Split(Convert.ToChar(splitter));

        for (int i = 0; i < columnCounter; i++)
        {
            string data = !String.IsNullOrEmpty("s") && i < ss.Length ? ss[i] : "";
            rowExcel.CreateCell(i).SetCellType(CellType.String);
            rowExcel.CreateCell(i).SetCellValue(data);                    
        }

        rowIndex++;
    }

    for (var i = 0; i < sheet.GetRow(0).LastCellNum; i++)
        sheet.AutoSizeColumn(i);

    using (FileStream file = new FileStream(newFileName, FileMode.Create, FileAccess.Write))
    {
        workbook.Write(file);
        file.Close();
    }
}

输出:

def list_scope(l):
    print(l, "id: ", id(l))
    l = [3, 4,5]
    print(l, "id: ", id(l))

def main():
    l = [1, 2, 3]
    print("id: ", id(l))
    list_scope(l)
    print("id: ", id(l))

main()

在分配列表id: 4510275784 [1, 2, 3] id: 4510275784 [3, 4, 5] id: 4509275592 id: 4510275784 之前llist_scope的ID与[3, 4, 5]中的ID相同。一旦分配了main,它就会更改,但在[3, 4, 5]中保持不变。