我应该如何在没有反复试验的情况下解决这个递归

Posted

技术标签:

【中文标题】我应该如何在没有反复试验的情况下解决这个递归【英文标题】:How should I go about solving this recursion without trial and error 【发布时间】:2015-10-13 06:54:36 【问题描述】:
int sum_down(int x)

    if (x >= 0)
    
        x = x - 1;
        int y = x + sum_down(x);
        return y + sum_down(x);
    
    else
    
        return 1;
    

参数x的这个最小整数值是多少,所以返回值大于1.000.000?

现在我只是通过反复试验来做到这一点,因为这个问题是通过纸质格式提出的。我认为我没有足够的时间进行反复试验。问题是,你们如何快速将其可视化以便可以轻松解决。谢谢大家,我是编程新手,所以提前感谢!

【问题讨论】:

答案是 19。而且,对我来说,这个问题应该问到Maths Stack... 我真的不明白这个问题。而且我不明白赞成票......这是一个如何解决重复问题的数学问题吗?你只是想要答案吗?你想知道其他人会如何处理它吗?就我个人而言,我会将其包装在一个循环中并在结果为 >1'000'000 时中断。 我会首先摆脱突变:if (x >= 0) return x - 1 + 2 * sum_down(x - 1); . 您好,我不是在寻找答案。我只需要帮助弄清楚如何解决这些问题,比如我应该采取的方向。但是谢谢大家。 :) 这是上课的吗?你在研究递归关系吗? 【参考方案1】:

递归逻辑:

x = x - 1;
int y = x + sum_down(x);
return y + sum_down(x);

可以简化为:

x = x - 1;
int y = x + sum_down(x) + sum_down(x);
return y;

可以简化为:

int y = (x-1) + sum_down(x-1) + sum_down(x-1);
return y;

可以简化为:

return (x-1) + 2*sum_down(x-1);

以数学形式表示,

f(N) = (N-1) + 2*f(N-1)

N-1 时递归终止。 f(-1) = 1.

因此,

f(0) = -1 + 2*1 = 1
f(1) =  0 + 2*1 = 2
f(2) =  1 + 2*2 = 5

...

f(18) = 17 + 2*f(17) = 524269
f(19) = 18 + 2*524269 = 1048556

【讨论】:

哇,谢谢!当你解释它时,它似乎很容易。你是怎么找到终止条件的? @Blake_Lead,终止条件在函数的else部分。【参考方案2】:

你的程序可以这样写(对不起 c#):

public static void Main()

    int i = 0;
    int j = 0;
    do
    
        i++;
        j = sum_down(i);
        Console.Out.WriteLine("j:" + j);
     while (j < 1000000);
    Console.Out.WriteLine("i:" + i);

static int sum_down(int x)

    if (x >= 0)
    
        return x - 1 + 2 * sum_down(x - 1);
    
    else
    
        return 1;
    

所以在第一次迭代中,你会得到 2,然后是 5,然后是 12...所以你可以忽略 x-1 部分,因为它与乘法相比会保持很小。

所以我们有:

i = 1 => sum_down ~= 4 (real is 2)
i = 2 => sum_down ~= 8 (real is 5)
i = 3 => sum_down ~= 16 (real is 12)
i = 4 => sum_down ~= 32 (real is 27)
i = 5 => sum_down ~= 64 (real is 58)

所以我们可以说 sum_down(x) ~= 2^x+1。那么这只是 2^x+1

【讨论】:

很有道理!谢谢! 这是一个很好的答案。感谢您的小数学解释,我找不到这样说的方法。【参考方案3】:

有点晚了,但要得到一个精确的非递归公式并不难。

如已在其他答案中解释的那样,以数学方式写出来:

f(-1) = 1
f(x) = 2*f(x-1) + x-1 

这个和

一样
f(-1) = 1
f(x+1) = 2*f(x) + x

(只是从 x 和 x-1 切换到 x+1 和 x,两种情况下差异 1)

前几个x和f(x)是:

x:    -1  0  1  2  3  4
f(x): 1   1  2  5  12 27

虽然有许多任意复杂的方法可以将其转换为非递归公式,但使用简单的方法通常有助于写出每两个元素之间的区别:

x:    -1  0  1  2  3  4  
f(x): 1   1  2  5  12 27  
        0  1  3  7  15  

所以,对于某些 x

f(x+1) - f(x) = 2^(x+1) - 1  
f(x+2) - f(x) = (f(x+2) - f(x+1)) + (f(x+1) - f(x)) = 2^(x+2) + 2^(x+1) - 2  
f(x+n) - f(x) = sum[0<=i<n](2^(x+1+i)) - n 

例如。插入x=0,使f(x+n) 变为f(n)

f(x+n) - f(x) = sum[0<=i<n](2^(x+1+i)) - n  
f(0+n) - f(0) = sum[0<=i<n](2^(0+1+i)) - n  
f(n) - 1 = sum[0<=i<n](2^(i+1)) - n   
f(n) = sum[0<=i<n](2^(i+1)) - n + 1   
f(n) = sum[0<i<=n](2^i) - n + 1   
f(n) = (2^(n+1) - 2) - n + 1
f(n) = 2^(n+1) - n - 1  

不再递归。

【讨论】:

【参考方案4】:

这个怎么样:

int x = 0;
while (sum_down(x) <= 1000000)

    x++;

循环递增 x 直到 sum_down(x) 的结果优于 1.000.000。

编辑:结果是 19。

虽然试图理解和简化 sum_down() 函数背后的递归逻辑具有启发性和信息性,但这个 sn-p 往往是合乎逻辑和实用的,因为它不会尝试根据上下文来解决问题,而是在结果。

【讨论】:

@Blake_Lead,你的逻辑有问题。您正在检查x 增加后的结果。 好了,伙计。我删除了我的反对意见和粗鲁的评论。对此感到抱歉....尽管我仍然不会赞成... 嗨,你们怎么知道它是否是 20。我不是在寻找答案,而是在寻找我应该应用于这类问题的方向或逻辑。因为现在我所做的只是反复试验,我知道这不是解决这类问题的最理想方法。非常感谢大家! 它看起来更好...如果您现在删除res 变量,它将更具可读性。 while(sum_down(x)&lt;=1000000)x++; 抱歉先粗鲁了。问题是,一开始这个答案看起来好像没有任何努力和/或思想投入......但是,是的,你完全正确,我的语气不合适......再次抱歉......如果您想进一步改进您的答案,我建议您提到这是解决问题的一种务实的方法,即使重现问题不像这种情况那样容易解决。 IE。这是一种通用方法 - 其他答案不是,一旦复发变得太复杂就会失败......如果你这样做,我会投票赞成......干杯!。【参考方案5】:

两行 Python 代码回答你的问题:

>>> from itertools import *   # no code but needed for dropwhile() and count()

定义递归函数(见 R Sahu 的回答)

>>> f = lambda x: 1 if x<0 else (x-1) + 2*f(x-1)

然后使用dropwhile() 函数从列表[0, 1, 2, 3, ....] 中删除f(x)&lt;=1000000 的元素,得到f(x) &gt; 1000000 的整数列表。注意:count() 返回 [0, 1, 2, ....] 的无限“列表”

dropwhile() 函数返回一个 Python 生成器,因此我们使用 next() 来获取列表的第一个值:

>>> next(dropwhile(lambda x: f(x)<=1000000, count()))
19

【讨论】:

以上是关于我应该如何在没有反复试验的情况下解决这个递归的主要内容,如果未能解决你的问题,请参考以下文章

如何通过限制运动递归解决河内塔?

如何在不使用递归或`/`运算符的情况下进行除法?

如何遍历文件夹下上亿文件而不栈溢出

如何遍历文件夹下上亿文件而不溢出(转载)

如何在没有转义序列的情况下停止递归

如何在没有外部递归函数的情况下解析多个嵌套的 JSON 键?