จะเขียน 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เป็นหลายขั้นตอนบางทีอาจจะตั้งค่าทีละบิตทีละคำตอบ
Peter Cordes

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 ไม่ฉันกำลังบอกว่าคุณสามารถสอนการเรียกซ้ำโดยแสดงโครงสร้างข้อมูลแบบเรียกซ้ำ ฉันไม่รู้ว่าทำไมคุณต้องดิ้นรนจับสิ่งนี้
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
เคสหลักในรหัสของ OP คือ0และใช้n - 1สำหรับปัญหาย่อย โดเมนของNatural Numbersดูเหมือนจะเป็นแบบที่ดี
ขอบคุณ

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
ประดิษฐ์พารามิเตอร์ 2 เพียงแค่เปลี่ยนนี้เป็นห่วงว่ากะซ้ายnครั้งแล้วหัก 1. (1<<n) - 1ดูเหมือนว่าแม้แต่น้อยที่สง่างามแล้วจำเป็นแม้ว่าสิ่งที่ทั้งเป็นการออกกำลังกายในการขาดประสิทธิภาพเมื่อเทียบกับ
Peter Cordes

1
@PeterCordes: การย้ายรัฐเป็นพารามิเตอร์สะสมเป็นวิธีมาตรฐานในการเปลี่ยนโทร recursive เป็นโทรหาง recursive ตอนนี้โชคไม่ดีที่งูใหญ่ไม่สนับสนุนการโทรหางที่เหมาะสมไม่ได้เหมาะสม recursion หาง แต่นั่นไม่ได้หมายความว่านี้ไม่ได้เป็นเทคนิคที่มีประโยชน์ในการเรียนรู้เพื่อให้คุณสามารถใช้มันในภาษาอื่น ๆ ที่ทำใช้โทรหางที่เหมาะสม หรืออย่างน้อยการเรียกซ้ำแบบหางที่เหมาะสม
Jörg W Mittag

1
@ JörgWMittagใช่ แต่ในกรณีนี้มันยากที่จะอำพรางความจริงที่ว่ามันจะเป็นธรรมชาติมากขึ้นในวง อาจเป็นเพราะฉันใช้เวลากับภาษาแอสเซมบลีและการแสดงมาก แต่การเขียน "ลูป" โดยใช้การเรียกซ้ำแบบหางดูเหมือนไม่มีจุดหมายในภาษาที่จำเป็นเมื่อคุณสามารถเขียนลูปได้ หรือบางทีสิ่งที่รบกวนจิตใจฉันเกี่ยวกับคำตอบนี้เป็นเพียงทางเลือกของการย่อยสลาย: เป็นการเปลี่ยนแบบครั้งต่อครั้งและการลบขั้นสุดท้ายเป็นฐาน อาจเป็นการรวมกันของทั้งสองอย่าง
Peter Cordes

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

โดยทั่วไปมันจะกำหนดค่าทั่วโลกของ n ถึง k และเรียกซ้ำผ่านการเปรียบเทียบที่เหมาะสม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.