リコウの質問応答クラスのセクション 2: 再帰

(例1) フィボナッチ数列

関数を作成し、n を入力して、フィボナッチ数列の n 番目の項を見つけます。
フィボナッチ数列は次のように定義されます:
F(0) = 0、F(1) = 1 
F(N) = F(N - 1) + F(N - 2)、N > 1。 
フィボナッチ数列は次で始まります。 0 と 1、および後続のフィボナッチ数は、
前の 2 つの数値を加算することによって計算されます。
0 1 1 2 3 5 8 13 ・・・

注: ここで出力されるのは、最初の n 項の合計ではなく、フィブナッチ数列の n 番目の項です。

def f(n): 
    if n==0: 
        return 0 
    elif n==1: 
        return 1 
    else: 
        return f(n-1)+f(n-2) 
k=f(6) 
print(k)

上記のコードの意味は、

(1) 再帰関係: すべての f(n) は、前の項目 f(n-1) と前の 2 つの項目 f(n-2) の合計に分割できます。

(2) ボトムケース: 最後で最も基本的なコンポーネント、つまり F(0) = 0、F(1) = 1 に戻ります。2 つの要素を継続的に加算することにより、以下のすべてを導き出すことができます。

派生トピック」

        フィボナッチ数列の最初の n 個の要素の合計を求めます (まだ実行しません)

(例2)階段を登る場合

あなたが階段を登っているとします。建物の屋上に着くまでにn歩かかります。一度に1段か2段ずつ登ることができます。建物の屋上まで登ることができる方法は何通りありますか? 注: n は正の整数です。以下は、n=1 ~ 5 の可能なすべての組み合わせを示します。

ステップ数 可能なパスの総数 可能なすべてのパス

n=1 ジャンプ(n)=1 ——1

n=2 ジャンプ(n)=2 ——1 1 / 2

n=3 ジャンプ(n)=3 ——1 1 1 / 1 2 / 2 1

n=4 ジャンプ(n)=5 ——1 1 1 1/ 1 1 2/ 2 1 1/ 2 2/ 1 2 1

n=5 ジャンプ(n)=8 ——1 1 1 1 1/ 1 1 1 2/ 1 1 2 1/ 1 2 1 1 / 2 1 1 1 / 1 2 2 / 2 1 2 / 2 2 1 /

注: ここで求められるのは、組み合わせが何通りあるかということです。

まずは、いじらずに答えに直接行き、答えから問題を解くための考え方やルールをまとめてみましょう。

どれだけ追い込んでも、自分で答えを書けなければ無駄になってしまいます。

基礎知識ゼロの初心者にとって、自分の考えで問題を理解し、自分の考えに従って答えを書けるようになることが最善の方法です。

専攻を出ていない場合、「アルゴリズムのデータ構造」を学んだことがない場合、答えをまったく見ずに自分の勝手な夢を描くだけだと、あなたのアイデアは非常に奇妙で非効率なものになることがよくあります。専門的なトレーニングがなければ、適切な場所に行くことは考えられないからです。

「技術的」および「アルゴリズム的データ構造」の考え方を養いたい場合、方法は 1 つしかありません。それは、より多くの回答を読むことです。まず、例と徹底的な方法を使用して、回答の設計の合理性を理解します。次に、回答のアイデアを洗練して要約します。最後に、言葉を使って質問全体のアイデアをつなぎ合わせます。次に、書いたアイデアに従って注釈を付け、最後に回答コードを書き出します。

def f(n): 
    
    # 这段if elif想表达是bottom case
    # 分别是上一个台阶有几种可能性以及上两个台阶有几种可能性
    if n==1: 
        return 1 
    # 上一个台阶,只有“走一步”这一种上去的方式
    elif n==2: 
        return 2 
    # 上两个台阶,有“走一步,再走一步”和“一次走两步”这两种走法



    else: 
        return f(n-1)+f(n-2) 
        

# f(5) =           f(4)+            f(3)
# f(5) =    f(3)+       f(2)  +   f(2)+ f(1)
# f(5) = f(2) +f(1) +   f(2)  +   f(2)+ f(1)
# f(5) = 2    +  1  +    2    +     2 +  1
# f(5) = 8

print(f(n)) 



すべての再帰はルーチンであり、再帰は 2 つの要素で構成されます。

要素(1):連続かつ「無限回」自分を呼ぶたびに自分を呼ぶ一般式を求める(現在の問題を小さな問題に分割する)、

これは、以下の呼び出しコードの「return f(n-1)+f(n-2)」という文に対応します。

        f(n) = return f(n-1)+f(n-2) このコードの背後にある考え方は次のとおりです: 現在のレベルに到達するには 2 つの可能性があります。1 つの可能性は、次のレベルから到達することです。もう 1 つは 2 つ下のステップにあります。次に、これら 2 つの可能性のそれぞれの可能な歩き方の数を加算するだけで、すべての可能な歩き方、つまり可能な歩幅の組み合わせ全体を取得できます。これは f(n ) で次のように分解されます
        。 f(n-1) と f(n-2) を求める 2 つの副問題
- これら 2 つの副問題はさらに f(n-2)+ に分割できます f(n-3) と f(n-3) の 4 つの副問題f(n-3)+f(n-4) -
次に問題は、部分問題は無限世代にわたってこのように解体されるのかということです。
        - 絶対にありません!再帰が無限に続くとコンピュータはクラッシュしませんか? 私たちの問題はまだ解決されていません
        。問題が f(1)=1、f(2)=2 までドリルダウンされると、再帰ループは終了します。
        ——最後に、再帰ループの数に応じて f(1)=1 と f(2)=2 を組み合わせて、求める答え f(5) = 8 を取得します。

学生の中には、f(n-1) と f(n-2) を足したものではなく、f(n-1) と f(n-2) を掛けたものであるべきだと考える人もいるかもしれません。それが「掛け算」ではなく「足し算」であることを確認するために、以下に実演してみましょう。

——最後の行は f(n) です。これは f(5) に対する階段登りのさまざまな組み合わせの数です。

——観察すると、f(n)、ここでは f(5) が青色の f(n-1) と黄色の f(n-2) で構成されていることがわかります。

——これで、再帰ループの一般式 f(n) = f(n-1)+f(n-2) の妥当性が検証できました。

要素(2) : 自分を呼び続けて、最後まで掘り続けてそれ以上掘れなくなる状況。下のケースをこれ以上呼び出すことはできません。実際、最終的な答えを形成するのは、基本的な要素としてのこれらのボトムケースの組み立てと組み合わせです。

(2.1) まず、ボトムケースの関数値が何であるかを調べる必要があります。

まず、一番下のケースでは依然として f(n) が求められています。つまり、一番下のケースでは、n ステップ上に移動する方法が何通りあるかが求められています。

ここで、ボトムケースが n==1 または n==2 の場合、つまり f(1) と f(2) がボトムケースになります。

n=1 Jump(n)=1 - 1 一歩で上がる可能性

n=2 Jump(n)=2 ——1 1 / 2 「一歩踏み出してから一歩踏み出す」と「一度に二歩踏み出す」の 2 つの可能性

つまり、f(1)=1; f(2)=2

上の図では、ピンク色は f(1)=1 を表し、緑色は f(2)=2 を表します。

前に、f(n) が複数の f(1)=1 と複数の f(2) の合計にどのように分解できるかについて説明しました。f(1) の具体的な数と f(2) の数は再帰頻度によって異なります。 。信じられない場合のために、以下を表示してください

        まずは計算です


# f(5) = f(4)+ f(3)
# f(5) = f(3)+ f(2) + f(2)+ f(1)
# f(5) = f(2) +f(1) + f(2) + f(2)+ f(1)
# f(5) = 2 + 1 + 2 + 2 + 1
# f(5) = 8

f(5) は 3 個の f(2) と 2 個の f(1) の和で得られることがわかります。

グラフ表現は次の図です 

        つまり、 f(n)の最終値は、複数の f(2)=2 と複数の f(1)=1 を合計して結合することによって取得されます。つまり、f(2) と f(1) は f(n) の必須の要素です。f(2) と f(1) が何回あるかは、再帰の回数、つまり n の値に依存します。

(例3) 正の整数を1と3の複数の和に分割する

正の整数 N を指定すると、それを 1 と 3 の組み合わせに分解します。ここでの組み合わせとは、因数分解ではなく、加算と合計を指します。例えば、N=4の場合、1111、13、31に分解されます(再帰)


s="" 
def gene(n,s): 
    """
    n -> 既是(1)剩余待填满的字符串的长度(字符串由几个数字组成),也是(2)被求和拆分的那个数字,比如被拆成3和1的那个4
    s -> 初始化字符串的内容,一般是一个空的字符串
    """

    # 如果n(剩余待填满的字符串的长度),在减三和减一的操作后  等于0或小于0了,那就说明“该填满的字符串长度”都已经填满了,任务完成,可以退出函数了
        # 等于0,说明目前这个字符串组合可以存进去
    if n==0: 
        result.append(s) 
        return 
        # 小于0,说明没啥可存了,可以直接退出了
    elif n<0: 
        return 
    

    # ————————这个位置——————————
    # 如果前面两个条件都不满足也就是说 n>=1 函数不会终止,会继续执行后面的代码
    s1=s+str(1) 
    gene(n-1,s1) 
    s2=s+str(3) 
    gene(n-3,s2) 
n=4 

# n表示被拆分的数字,这里是4
# s表示??暂时被拆分的东西?

result=[] 
gene(n=4,s="") 
print(result)

疑い

この数字はどのようにして 3 と 1 の組み合わせに分割されましたか?

(例4)ブラケット生成

数値 n は括弧生成の対数を表します。すべての有効な括弧の組み合わせを生成できる関数を設計してください。

• 例 1: 入力: n = 3 出力: ["((()))","(()())","(())()","()(())"," ( )()()"]。出力は 3 つのブラケット ペアの可能な組み合わせです。

• 例 2: 入力: n = 1 出力: ["()"]

result=[] 
s="" 
def gene(n,l,r,s): 
    """
    l记录左边的括号数量
    r记录右边的括号数量
    """

    # 当左右两边的括号数,都同时到达n,将s这个位置的数据存进result里面
    if l==n and r==n: 
        result.append(s) 
        return 

    # 如果左边的括号数量少,就在左边加括号
    if l<n: 
        s1=s+"(" 
        # 左边的括号多一个,表示左边括号个数的l也要加一
        gene(n,l+1,r,s1) 
    
    # 如果右边括号数量小,就往右边加括号
    if l>r: 
        s2=s+")" 
        # 右边的括号多一个,表示右边括号个数的l也要加一
        gene(n,l,r+1,s2) 
n=3 
gene(n,0,0,s) 
print(result)

(例5) 繰り返し番号を除いたフルアレンジメント

繰り返される数値のない配列 nums を指定すると、考えられるすべての順列を返します。回答は任意の順序で返すことができます。

リートコード-46

入力: nums = [1,2,3] 出力: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3, 1 ,2],[3,2,1]]。

入力: nums = [0,1] 出力: [[0,1],[1,0]

result=[] 
def gene(nums,l): 
    if len(l)==len(nums): 
        result.append(l) 
        return 
    for a in nums: 
        if a not in l: 
            l1=l+[a] 
            gene(nums,l1) 
nums=[1,2,3] 
gene(nums,[]) 
print(result) 

よくわかりません

(例6)フルアレンジ – 繰り返し番号を含む

繰り返しの数値を含む配列 nums を指定すると、考えられるすべての順列を返します。(出力配列の長さは入力の長さと同じである必要があります)。回答は任意の順序で返すことができます。

入力: 数値 = [2,2,3]

出力: [[2, 2, 3], [2, 3, 2], [3, 2, 2]]

result=[] 
def gene(nums,l,m): 
    if len(l)==len(nums) and l not in result: 
        result.append(l) 
        return 
    for i in range(len(nums)): 
        if i not in m: 
            l1=l+[nums[i]] 
            m1=m+[i] 
            gene(nums,l1,m1) 
nums=[2,2,3] 
gene(nums,[],[]) 
print(result)

本当にわかりません

(例7) 組み合わせの数を求める

2 つの整数 n と k が与えられると、1 ... n 内の数値の考えられる k 個の組み合わせをすべて返します。

入力: n = 4、k = 2。1 ~ 4 の 2 つの数字の可能なすべての組み合わせを返します。

出力: [ [2,4]、[3,4]、[2,3]、[1,2]、[1,3]、[1,4]、]

res=[] 
def gene(n,k,l,start): # start 开始取数的位置
    """
    n表示数字的可能性是1到n
    k表示一个组合的数字的数量
    l表示初始的数组,一般是空列表
    start表示从哪个数字开始,一般是1
    """
    # 如果l这个列表的长度和要求的组合长度k相等,则说明组合完毕了,把最后一个结果放进result,就结束即可
    if len(l)==k: 
        res.append(l) 
        return 

    # 如果还没到达就继续组合
        # ????这段没看懂???
    for i in range(start,n+1): 
        gene(n, k, l+[i], i+1) 
n=4 
k=2 
gene(n,k,[],1)

res # [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

理解できなかった部分がありました 

アルゴリズムデータ構造のトピックを学ぶにはどうすればよいですか?

(1) 上記の質問例については、白い紙に答えを書き、自分で考えて書き出すことができます。自分で考えても書けないときは、自分の思考回路の切れた部分を先生に持っていって、なぜ思いつくのか、なぜ思いつかないのかを聞いてください、それがあなたと先生の違いです。ギャップ、これはあなたの知識の不足です

(2) Niuke.com または Likou.com にアクセスし、このトピック (再帰トピックなど) に関する最も簡単な演習を見つけてください。もう一度言いますが、先生に聞いてください。

おすすめ

転載: blog.csdn.net/Albert233333/article/details/132900266