Bagaimana cara menulis 2 ** n - 1 sebagai fungsi rekursif?

49

Saya membutuhkan fungsi yang mengambil n dan mengembalikan 2 n - 1 . Kedengarannya cukup sederhana, tetapi fungsinya harus bersifat rekursif. Sejauh ini saya hanya punya 2 n :

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

Latihan menyatakan: "Anda dapat mengasumsikan bahwa parameter n selalu bilangan bulat positif dan lebih besar dari 0"

Kajice
sumber
4
Sebagai catatan, seharusnya jauh lebih efisien untuk melakukannya seperti orang normal dengan perubahan dan pengurangan. Bilangan bulat python lebar sembarang jadi 1 << ntidak bisa meluap. Ini tampaknya merupakan latihan dalam menemukan cara untuk membusuk (1<<n) - 1menjadi beberapa langkah, mungkin mengatur setiap bit satu per satu seperti yang ditunjukkan oleh beberapa jawaban.
Peter Cordes
8
def fn(n): if n == 0: return 1; return (2 << n) - fn(0); # technically recursive
MooseBoys
3
@ Voo: Bukan Carl, tapi tolong sebutkan semua yang ada di dalam sayaC:\MyFolder
Flater
1
@ Vo: Ketergantungan atau tidak tidak relevan untuk latihan yang murni berfokus pada pengajaran konsep rekursi. Saya bisa membuat satu set dasar kelas / metode yang dapat digunakan siswa. Anda fokus pada sesuatu yang sepenuhnya selain inti dari latihan. Menggunakan navigasi sistem file adalah contoh yang baik karena siswa umumnya memahami sifat berulang folder dan file (yaitu folder dapat bersarang di satu sama lain tanpa batas waktu)
Flater
1
@Vo Tidak, saya mengatakan bahwa Anda dapat mengajarkan rekursi dengan menunjukkan struktur data rekursif. Saya tidak tahu mengapa Anda berjuang untuk memahami ini.
Flater

Jawaban:

54

2**n -1juga 1 + 2 + 4 + ... + 2 n-1 yang dapat dibuat menjadi fungsi rekursif tunggal (tanpa yang kedua untuk mengurangi 1 dari kekuatan 2).

Petunjuk : 1 + 2 * (1 + 2 * (...))

Solusi di bawah, jangan lihat jika Anda ingin mencoba petunjuk itu terlebih dahulu.


Ini berfungsi jika ndijamin lebih besar dari nol (seperti yang dijanjikan dalam pernyataan masalah):

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

Versi yang lebih kuat akan menangani nilai nol dan negatif juga:

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)

(Menambahkan cek untuk non-integer dibiarkan sebagai latihan.)

h4z3
sumber
4
tetapi required_steps(0)sekarang menyebabkan rekursi yang tak terbatas
Terima kasih
7
2^0 - 1== 0. Tambahkan yang lain ifuntuk kasing itu.
h4z3
9
@ user633183 Ya, saya tahu apa fungsi total itu. Apakah kamu? Karena itu tidak akan pernah menjadi fungsi total. Juga tidak ada fungsi jawaban total lainnya. Dan ya, lebih banyak kode diperlukan untuk menjadikannya fungsi total. - Seperti yang saya katakan, kami tidak memiliki domain. Apa yang harus kita asumsikan domain kita? Bahkan jika itu hanya int, kita tidak tahu apa yang harus dilakukan ketika n <0. Menghitung? Melempar kesalahan? Kembali 0? Dalam hal ini, kita hanya dapat melakukan fungsi parsial (mendefinisikannya untuk hal-hal yang kita tahu apa hasilnya).
h4z3
4
Kasing dasar dalam kode OP adalah 0dan digunakan n - 1untuk submasalah. Domain Bilangan Alami sepertinya cocok.
Terima kasih
4
Terima kasih banyak! Menurut pendapat saya yang sederhana, ini adalah solusi terbaik untuk masalah spesifik saya. Saya tidak menyatakan nilai yang mungkin untuk n, sangat menyesal! Saya tahu itu agak penting ... latihan ini menyatakan: "Anda dapat mengasumsikan bahwa parameter n selalu bilangan bulat positif dan lebih besar dari 0"
Kajice
37

Untuk memecahkan masalah dengan pendekatan rekursif Anda harus mengetahui bagaimana Anda dapat mendefinisikan fungsi dengan input yang diberikan dalam hal fungsi yang sama dengan input yang berbeda. Dalam hal ini, karena f(n) = 2 * f(n - 1) + 1, Anda dapat melakukan:

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

maka:

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

output:

0
1
3
7
15
blhsing
sumber
9

Anda dapat mengekstrak bagian yang sangat rekursif ke fungsi lain

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

Atau Anda dapat mengatur bendera dan menentukan kapan harus mengurangi

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

>>> print(required_steps(10))
1023
rafaelc
sumber
0

Menggunakan parameter tambahan untuk hasilnya, 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

Anda juga dapat menulisnya menggunakan shift kiri bitwise, <<-

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

Outputnya sama

Terima kasih
sumber
2
Tidak perlu melibatkan operasi bitwise untuk latihan multiplikasi sederhana .. tidak dapat dibaca sama sekali. Juga, tidak perlu untuk elseklausa di salah satu fungsi
rafaelc
Satu-satunya perbedaan berubah r * 2menjadi r << 1dan itu "tidak dapat dibaca sama sekali"? 😂
Terima kasih
2
Menciptakan sebuah 2 parameter hanya berubah ini menjadi lingkaran yang bergeser meninggalkan nkali dan kemudian mengurangi 1. Sepertinya bahkan kurang elegan maka perlu, meskipun semuanya adalah latihan dalam inefisiensi vs (1<<n) - 1.
Peter Cordes
1
@PeterCordes: Pindah negara menjadi parameter akumulator adalah dengan cara standar transformasi panggilan rekursif menjadi panggilan ekor-rekursif. Sekarang, sayangnya, Python tidak mendukung Panggilan Tail tepat, bahkan tidak tepat Tail Rekursi, tetapi itu tidak berarti bahwa ini bukan teknik yang berguna untuk belajar sehingga Anda dapat menerapkannya dalam bahasa lain yang melakukan menerapkan Panggilan Tail Proper atau setidaknya Rekursi Ekor yang Benar.
Jörg W Mittag
1
@ JörgWMittag Ya, tetapi dalam kasus ini sulit untuk menyembunyikan fakta bahwa itu akan lebih alami sebagai sebuah loop. Mungkin hanya karena saya menghabiskan begitu banyak waktu pada bahasa dan kinerja perakitan, tetapi menulis "loop" dengan menggunakan rekursi ekor tampaknya tidak ada gunanya dalam bahasa imperatif ketika Anda bisa menulis loop. Atau mungkin yang menggangguku tentang jawaban ini hanyalah pilihan bagaimana menguraikan: menjadi satu per satu perubahan dan kemudian mengurangi terakhir sebagai basis data. Mungkin kombinasi keduanya.
Peter Cordes
0

Mintalah pengganti untuk mengingat nilai asli dari n dan kemudian untuk langkah pertama yaitu n == N, kembali2^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)
emad
sumber
-1

Salah satu cara untuk mendapatkan offset "-1" adalah menerapkannya sebagai imbalan dari panggilan fungsi pertama menggunakan argumen dengan nilai default, lalu secara eksplisit mengatur argumen offset ke nol selama panggilan rekursif.

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

Di atas semua jawaban luar biasa yang diberikan sebelumnya, di bawah ini akan menunjukkan implementasinya dengan fungsi internal.

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

Pada dasarnya, ini memberikan nilai global n ke k dan mengulanginya dengan perbandingan yang sesuai.

Kamu mengendarai
sumber