Algorithm 在递归函数中,我应该在哪里放置计数器?
不确定是否要发布到PSE,但我的问题是关于递归的 假设我有一个递归运行的函数MergeSort。如果我想计算它拆分数组前半部分的次数,我会将计数器放在哪里?(我知道我可以计算它,但我正在努力更好地理解递归) 比如说Algorithm 在递归函数中,我应该在哪里放置计数器?,algorithm,matlab,Algorithm,Matlab,不确定是否要发布到PSE,但我的问题是关于递归的 假设我有一个递归运行的函数MergeSort。如果我想计算它拆分数组前半部分的次数,我会将计数器放在哪里?(我知道我可以计算它,但我正在努力更好地理解递归) 比如说 function u = MergeSort(Array) %% Initializations A = Array; B = zeros(1,n2); %to store first half of A C = zeros(1,n1-n2); %to store sec
function u = MergeSort(Array)
%% Initializations
A = Array;
B = zeros(1,n2); %to store first half of A
C = zeros(1,n1-n2); %to store second half of A
D = zeros(1,n1); %to store sorted array
na = length(A);
nb = floor(0.5*na);
count1 = 0;
count2 = 0;
%% recursive part
if n1 == 1
D = A;
A1 = mergeSort(A(1:nb));
A2 = mergeSort(A(nb+1:n));
最简单的方法是将计数器作为参数传递给递归函数本身,如下所示:
func foo(bar, acc) {
print bar;
print acc;
foo(bar+1, acc+1);
}
然后,当您最初调用该函数时,可以按如下方式执行:
foo(bar, 0);
很明显,它的功能远不止此(您可能希望函数做一些事情,而不仅仅是打印当前变量,并且您可能还需要确保它有一个最终用例),但希望基本思想能够被理解。我建议阅读有关模式匹配、折叠和尾部递归的知识,我认为这对理解计数器和累加非常有帮助。为了有趣地解释它是如何有帮助的,我最近花了很多时间研究它,它大量使用递归和尾部调用(函数中可能没有直接使用的计数器,但通过使用所述尾部调用到列表为空的点,可以有效地执行相同的操作。最简单的方法是将计数器作为参数传递给递归函数本身,如下所示:
func foo(bar, acc) {
print bar;
print acc;
foo(bar+1, acc+1);
}
function (sorted_array, total) = mergesort(array) {
if(length(array) == 1)
then return (array, 0); // this is not a split, don't count it
(left, left_sum) = mergesort(lefthalf);
(right, right_sum) = mergesort(righthalf);
result_array = merge(left, right);
return (result_array, 1 + left_sum + right_sum); // this is a split, add 1
}
然后,当您最初调用该函数时,可以按如下方式执行:
foo(bar, 0);
很明显,它的功能远不止此(您可能希望函数做一些事情,而不仅仅是打印当前变量,并且您可能还需要确保它有一个结束用例),但希望基本思想能被理解。我建议阅读有关模式匹配、折叠和尾部递归的知识,我认为这对理解计数器和累加非常有帮助。为了轶事般地解释它是如何有帮助的,我最近花了很多时间在这方面,它大量使用递归和尾部调用(函数中可能没有直接使用的计数器,但通过使用所述尾部调用到列表为空的点,可以有效地执行相同的操作
function (sorted_array, total) = mergesort(array) {
if(length(array) == 1)
then return (array, 0); // this is not a split, don't count it
(left, left_sum) = mergesort(lefthalf);
(right, right_sum) = mergesort(righthalf);
result_array = merge(left, right);
return (result_array, 1 + left_sum + right_sum); // this is a split, add 1
}
这不仅会计算前半部分的拆分,而且您只能在前半部分调用它。通过将0更改为1,您可以将其更改为计算所有函数调用
关于这一点(以及一般的递归)最酷的事情这实际上只是重新定义了你想要计算的东西。如果我们从一个分支返回,那么从我们开始向下的分支总数的定义是什么?一个对我们来说,加上左边和右边的分支数。如果我在底部返回,从我们开始向下的分支数是多少?零。
这不仅会计算前半部分的拆分,而且您只能在前半部分调用它。通过将0更改为1,您可以将其更改为计算所有函数调用
关于这一点(以及一般的递归)最酷的事情这实际上只是重新定义了你想要计算的东西。如果我们从一个分支返回,那么从我们开始向下的分支总数的定义是什么?一个对我们来说,加上左边和右边的分支数。如果我在底部返回,从我们开始向下的分支数是多少?零。“问题不能是自身的复制品”…该死的“问题不能是自身的复制品”…damnAlso,如果调用代码不需要了解计数器,那么保持干净很简单:
func foo_public(bar){foo(bar,0);}
此外,如果调用代码不需要知道计数器的信息,那么保持干净也很简单:func foo_public(bar){foo(bar,0);}