如何写2 ** n-1作为递归函数?


49

我需要一个接受n并返回2 n -1的函数。听起来很简单,但是该函数必须是递归的。到目前为止,我只有2 n

def required_steps(n):
    if n == 0:
        return 1
    return 2 * req_steps(n-1)

练习指出:“您可以假设参数n始终为正整数且大于0”


4
仅作记录,像平常人一样通过移位和减法来执行此操作应大大提高效率。Python整数是任意宽度,因此1 << n不会溢出。这似乎是在尝试发明一种分解(1<<n) - 1为多个步骤的方法,这可以像某些答案所示那样一次将每个位设置为一个。
彼得·科德斯

8
def fn(n): if n == 0: return 1; return (2 << n) - fn(0); # technically recursive
MooseBoys

3
@Voo:不是Carl,但是请列出我所包含的所有内容C:\MyFolder
Flater

1
@Voo:依赖与否与纯粹侧重于讲授递归概念的练习无关。我可以制作一套可供学生使用的基本模拟类/方法。您正在专注于完全超出练习重点的事情。使用文件系统导航是一个很好的例子,因为学生通常了解文件夹和文件的固有重复性质(即文件夹可以无限地嵌套在彼此之间)
Flater

1
@Voo不,我是说您可以通过显示递归数据结构来教授递归。我不知道你为什么要努力抓住这一点。
平坦:

Answers:


54

2**n -1也是1 + 2 + 4 + ... + 2 n-1,可以将其设为单个递归函数(没有第二个递归函数需要从2的幂中减去1)。

提示:1 + 2 *(1 + 2 *(...))

下面的解决方案,不要看是否要先尝试提示。


如果n保证大于零(如问题声明中实际承诺的那样),则此方法有效:

def required_steps(n):
    if n == 1: # changed because we need one less going down
        return 1
    return 1 + 2 * required_steps(n-1)

一个更强大的版本也将处理零和负值:

def required_steps(n):
    if n < 0:
        raise ValueError("n must be non-negative")
    if n == 0:
        return 0
    return 1 + 2 * required_steps(n-1)

(剩下的是对非整数的检查作为练习。)


4
required_steps(0)现在会导致无限递归
谢谢您,

7
2^0 - 1==0 if。为该情况添加另一个。
h4z3

9
@ user633183是的,我知道总功能是什么。你做?因为它永远不会是一个全部功能。其他答案也不是全部功能。是的,将需要更多代码才能使它们具有全部功能。-正如我所说,我们没有域名。我们应该假设我们的域名是什么?即使只是int,当n <0时,我们也不知道该怎么办。计算?抛出错误?返回0?在这种情况下,我们只能执行部分函数(将其定义为我们知道结果是什么的函数)。
h4z3

4
OP的代码中的基本情况是0并且n - 1用于子问题。自然数域似乎很合适。
谢谢您,

4
非常感谢!以我的拙见,这是针对我的特定问题的最佳解决方案。我没有说明n的可能值,真的很抱歉!我知道这很重要...练习指出:“您可以假设参数n始终为正整数且大于0”
Kajice

37

为了使用递归方法解决问题,您将必须找出如何根据给定输入的功能定义相同输入和不同输入的功能。在这种情况下,由于f(n) = 2 * f(n - 1) + 1,您可以执行以下操作:

def required_steps(n):
    return n and 2 * required_steps(n - 1) + 1

以便:

for i in range(5):
    print(required_steps(i))

输出:

0
1
3
7
15

9

您可以将真正的递归部分提取到另一个函数中

def f(n):
    return required_steps(n) - 1

或者您可以设置一个标志并定义何时减去

def required_steps(n, sub=True):
    if n == 0: return 1
    return 2 * required_steps(n-1, False) - sub

>>> print(required_steps(10))
1023

0

为结果使用附加参数r-

def required_steps (n = 0, r = 1):
  if n == 0:
    return r - 1
  else:
    return required_steps(n - 1, r * 2)

for x in range(6):
  print(f"f({x}) = {required_steps(x)}")

# f(0) = 0
# f(1) = 1
# f(2) = 3
# f(3) = 7
# f(4) = 15
# f(5) = 31

您也可以使用按位左移<<-

def required_steps (n = 0, r = 1):
  if n == 0:
    return r - 1
  else:
    return required_steps(n - 1, r << 1)

输出是一样的


2
对于简单的乘法练习,不必涉及按位运算。根本不可读。另外,else在任何一个函数中都不需要该子句
rafaelc

唯一的区别是更改r * 2r << 1并且“根本不可读”?😂
感谢您

2
发明第二个参数只会使它变成一个循环,向左移动n一次,然后减去1。看起来似乎还不够优雅,而没有必要,尽管整个过程都是效率低下(1<<n) - 1
彼得·科德斯

1
@PeterCordes:将状态移动到累加器参数是递归调用转换为尾递归调用标准方法。现在,不幸的是,Python不支持适当的尾调用,甚至没有适当的尾递归,但这并不意味着这是不学,让你可以在其他语言的应用成为一个有用的技术实施适当的尾调用或至少是正确的尾递归。
约尔格W¯¯米塔格

1
@JörgWMittag是的,但在这种情况下,很难掩盖事实,这将是更多的作为循环自然。也许只是我花了很多时间在汇编语言和性能上,但是当您可以编写循环时,在命令式语言中使用尾部递归来编写“循环”似乎毫无意义。也许让我困扰的是这个答案只是如何分解的选择:一次转换一次,然后以最终的减法为基数。可能两者兼而有之。
彼得·科德斯

0

让一个占位符记住n的原始值,然后第一步,即n == N返回2^n-1

n = 10
# constant to hold initial value of n
N = n
def required_steps(n, N):
    if n == 0:
        return 1
    elif n == N:
        return 2 * required_steps(n-1, N) - 1
    return 2 * required_steps(n-1, N)

required_steps(n, N)

-1

获得“ -1”偏移量的一种方法是,使用具有默认值的参数在第一个函数调用的返回值中应用它,然后在递归调用期间将offset参数显式设置为零。

def required_steps(n, offset = -1):
    if n == 0:
        return 1
    return offset + 2 * required_steps(n-1,0)

-1

在前面给出的所有令人敬畏的答案之上,下面将展示其内部功能的实现。

def outer(n):
    k=n
    def p(n):
        if n==1:
            return 2
        if n==k:
            return 2*p(n-1)-1
        return 2*p(n-1)
    return p(n)

n=5
print(outer(n))

基本上,它将为k分配一个全局值n,并通过适当的比较对其进行递归。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.