Làm thế nào để viết 2 ** n - 1 dưới dạng hàm đệ quy?


49

Tôi cần một hàm lấy n và trả về 2 n - 1 . Nghe có vẻ đơn giản, nhưng chức năng phải được đệ quy. Cho đến nay tôi chỉ có 2 n :

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

Bài tập nêu: "Bạn có thể giả sử rằng tham số n luôn là số nguyên dương và lớn hơn 0"


4
Chỉ cần cho hồ sơ, nó sẽ hiệu quả hơn rất nhiều để làm điều đó như một người bình thường với một ca và trừ. Số nguyên Python có chiều rộng tùy ý nên 1 << nkhông thể tràn. Đây dường như là một bài tập trong việc phát minh ra một cách để phân rã (1<<n) - 1thành nhiều bước, có thể thiết lập từng bit một tại một thời điểm như một số câu trả lời hiển thị.
Peter Cordes

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

3
@Voo: Không phải Carl, nhưng vui lòng liệt kê cho tôi mọi thứ được chứa trongC:\MyFolder
Flater 16/10/19

1
@Voo: Sự phụ thuộc hay không không liên quan đến một bài tập mà hoàn toàn tập trung vào việc dạy khái niệm đệ quy. Tôi có thể tạo ra một tập hợp các lớp học / phương pháp cơ bản mà học sinh có thể sử dụng. Bạn đang tập trung vào một cái gì đó hoàn toàn bên cạnh điểm của bài tập. Sử dụng điều hướng hệ thống tệp là một ví dụ điển hình vì sinh viên thường hiểu bản chất thường xuyên của các thư mục và tệp (tức là các thư mục có thể được lồng vào nhau vô thời hạn)
Flater 16/10/19

1
@Voo Không, tôi đang nói rằng bạn có thể dạy đệ quy bằng cách hiển thị cấu trúc dữ liệu đệ quy. Tôi không biết tại sao bạn đấu tranh để nắm bắt điều này.
Flater

Câu trả lời:


54

2**n -1cũng là 1 + 2 + 4 + ... + 2 n-1 có thể được tạo thành một hàm đệ quy duy nhất (không có hàm thứ hai để trừ 1 khỏi lũy thừa của 2).

Gợi ý : 1 + 2 * (1 + 2 * (...))

Giải pháp dưới đây, đừng nhìn nếu bạn muốn thử gợi ý đầu tiên.


Điều này hoạt động nếu nđược đảm bảo lớn hơn 0 (như đã thực sự hứa trong tuyên bố vấ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)

Một phiên bản mạnh mẽ hơn cũng sẽ xử lý các giá trị 0 và âm:

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)

(Thêm một kiểm tra cho các số nguyên không còn lại là một bài tập.)


4
nhưng required_steps(0)bây giờ gây ra đệ quy vô hạn
Cảm ơn bạn

7
2^0 - 1== 0. Thêm cái khác ifcho trường hợp đó.
h4z3

9
@ user633183 Vâng, tôi biết tổng số hàm là gì. Phải không Bởi vì nó sẽ không bao giờ là một chức năng tổng thể. Cũng không phải là các câu trả lời khác tổng số chức năng. Và vâng, sẽ cần nhiều mã hơn để biến chúng thành các hàm tổng. - Như tôi đã nói, chúng tôi không có tên miền. Chúng ta nên giả sử tên miền của chúng ta là gì? Ngay cả khi đó chỉ là int, chúng tôi không biết phải làm gì khi n <0. Tính? Ném một lỗi? Trả về 0? Trong trường hợp này, chúng ta chỉ có thể thực hiện một phần chức năng (xác định nó cho những thứ chúng ta biết kết quả là gì).
h4z3

4
Trường hợp cơ sở trong mã của OP là 0và sử dụng n - 1cho bài toán con. Một miền của số tự nhiên có vẻ như phù hợp.
Cảm ơn bạn

4
Cảm ơn bạn rất nhiều! Theo ý kiến ​​khiêm tốn của tôi, đây là giải pháp tốt nhất cho vấn đề cụ thể của tôi. Tôi đã không nêu giá trị có thể cho n, thực sự xin lỗi! Tôi biết điều đó khá quan trọng ... bài tập nêu rõ: "Bạn có thể giả sử rằng tham số n luôn là số nguyên dương và lớn hơn 0"
Kajice

37

Để giải quyết vấn đề bằng cách tiếp cận đệ quy, bạn sẽ phải tìm hiểu làm thế nào bạn có thể xác định hàm với một đầu vào đã cho theo cùng một hàm với một đầu vào khác. Trong trường hợp này, kể từ đó f(n) = 2 * f(n - 1) + 1, bạn có thể làm:

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

vậy nên:

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

đầu ra:

0
1
3
7
15

9

Bạn có thể trích xuất phần đệ quy thực sự cho hàm khác

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

Hoặc bạn có thể đặt cờ và xác định khi nào nên trừ

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

Sử dụng một tham số bổ sung cho kết quả, 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

Bạn cũng có thể viết nó bằng cách sử dụng dịch chuyển trái bit, <<-

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

Đầu ra là như nhau


2
Không cần thiết phải liên quan đến các thao tác bitwise cho một bài tập nhân đơn giản .. hoàn toàn không thể đọc được. Ngoài ra, không cần elsemệnh đề trong một trong hai chức năng
rafaelc

Sự khác biệt duy nhất là thay đổi r * 2thành r << 1và đó là "không thể đọc được"? 😂
Cảm ơn bạn

2
Phát minh ra một tham số thứ 2 chỉ biến điều này thành một vòng lặp dịch chuyển nthời gian còn lại và sau đó trừ đi 1. Có vẻ ít thanh lịch hơn khi cần thiết, mặc dù toàn bộ điều này là một bài tập không hiệu quả so với (1<<n) - 1.
Peter Cordes

1
@PeterCordes: Di chuyển nhà nước vào một tham số cho ác là những cách tiêu chuẩn chuyển đổi cuộc gọi đệ quy thành một cuộc gọi đuôi-đệ quy. Bây giờ, không may, Python không hỗ trợ cuộc gọi Tail đúng, thậm chí không đúng Tail Đệ quy, nhưng điều đó không có nghĩa rằng đây không phải là một kỹ thuật hữu ích để học hỏi để bạn có thể áp dụng nó trong các ngôn ngữ khác mà làm thực hiện cuộc gọi Tail đúng hoặc ít nhất là đệ quy đuôi thích hợp.
Jörg W Mittag

1
@ JörgWMittag Có, nhưng trong trường hợp này thật khó để ngụy trang sự thật rằng nó sẽ tự nhiên hơn như một vòng lặp. Có lẽ chỉ là tôi dành quá nhiều thời gian cho ngôn ngữ lắp ráp và hiệu suất, nhưng viết một "vòng lặp" bằng cách sử dụng đệ quy đuôi có vẻ vô nghĩa trong một ngôn ngữ bắt buộc khi bạn chỉ có thể viết một vòng lặp. Hoặc có lẽ điều khiến tôi băn khoăn về câu trả lời này chỉ là sự lựa chọn về cách phân tách: thành các ca một lần và sau đó là phép trừ cuối cùng làm cơ sở. Có lẽ là sự kết hợp của cả hai.
Peter Cordes

0

Có một trình giữ chỗ để nhớ giá trị ban đầu của n và sau đó cho bước đầu tiên n == N, tức là trả lại2^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

Một cách để lấy phần bù của "-1" là áp dụng nó trong phần trả về từ lệnh gọi hàm đầu tiên bằng cách sử dụng một đối số có giá trị mặc định, sau đó đặt rõ ràng đối số offset thành 0 trong các lệnh gọi đệ quy.

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

-1

Trên tất cả các câu trả lời tuyệt vời được đưa ra trước đó, bên dưới sẽ cho thấy việc thực hiện với các chức năng bên trong.

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

Về cơ bản, nó đang gán một giá trị toàn cầu từ n đến k và đệ quy thông qua nó với các so sánh thích hợp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.