Як записати 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: Чи не Карл, але , будь ласка , список мені все , що міститься вC:\MyFolder
Flater

1
@Voo: залежність чи ні - не має значення для вправи, яка суто орієнтована на викладання поняття рекурсії. Я міг би зробити базовий глузливий набір класів / методів, якими учні могли скористатися. Ви зосереджуєтесь на чомусь, що є абсолютно окрім точки вправи. Використання файлової системи навігації є хорошим прикладом , тому що студенти в цілому розуміють , невід'ємно рецидивний характер папок і файлів (тобто папки можуть бути вкладені один в одному Най на невизначений термін)
Flater

1
@Voo Ні Я говорю, що ви можете навчити рекурсії, показуючи рекурсивну структуру даних. Я поняття не маю, чому ти борешся, щоб це зрозуміти.
Flater

Відповіді:


54

2**n -1також є 1 + 2 + 4 + ... + 2 n-1, який може перетворитись на єдину рекурсивну функцію (без другої, щоб відняти 1 від сили 2).

Підказка : 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
Базовий випадок у коді ОП є 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 * 2на r << 1те, що це "зовсім не читабельно"? 😂
Дякую

2
Винайдення другого параметра просто перетворює це на цикл, який зміщує ліві nчаси, а потім віднімає 1. Здається, навіть менш елегантним, ніж необхідним, хоча вся справа вправі в неефективності (1<<n) - 1.
Пітер Кордес

1
@PeterCordes: Переведення стану в параметр акумулятора - це стандартний спосіб перетворення рекурсивного виклику в хвостово-рекурсивний виклик. Зараз, на жаль, Python не підтримує належні виклики Tail, навіть не належну рекурсу хвоста, але це не означає, що це не корисна методика для вивчення, щоб ви могли застосовувати її на інших мовах, які реалізують належні дзвінки Tail. або принаймні належної рекурсії хвоста.
Йорг 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" - це застосувати його у відповідь з першого виклику функції, використовуючи аргумент зі значенням за замовчуванням, а потім явно встановити аргумент зміщення на нуль під час рекурсивних викликів.

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))

В основному, він присвоює глобальне значення n до k і повторюється через нього за допомогою відповідних порівнянь.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.