講義 58: Python プログラミングにおける再帰関数の中心概念と応用例を理解するのが最も難しい

1. 再帰関数の概念

関数本体内では他の関数を呼び出すことができ、関数本体内で関数自体を呼び出す場合、その関数を再帰関数と呼びます。

たとえば、fun 関数を定義した場合、fun 関数が fun 関数本体で呼び出される場合、これは再帰関数です。

再帰関数には、関数自体を繰り返し実行する暗黙的なループが含まれているため、再帰関数には、再帰終了とも呼ばれる明確な再帰終了条件が必要です。

明確な再帰終了調整がない場合、再帰関数は関数本体で独自の関数を繰り返し呼び出す無限ループとなり、メモリ オーバーフローが発生する可能性があります。使用には注意が必要です。結局のところ、ループを使用して問題を解決するのは安全ではありません。解決。

再帰関数は無限ループとみなすことができ、関数本体内のコードが無限ループで実行されます。そのため、再帰に対する明確な終了条件が必要です。そうでない場合、再帰関数は常に無限ループ本体内にあり、最終的には次のような結果になります。メモリオーバーフロー。

再帰関数を使用して解決される問題は、次の 2 つの条件を満たす必要があります。

  • 問題のサイズは再帰呼び出しによって縮小でき、新しい問題は元の問題と同じ形式になります。
    • たとえば、最初は n の結果を計算したいと考えていましたが、再帰を使用した後は n-1 の結果を計算できるため、問題の規模が小さくなります。
  • 再帰が終了する単純な状況が発生する可能性があります。

再帰関数の利点は、定義が簡単で、ロジックが明確であることです。

再帰の中心的な考え方は、再帰ごとに全体的な問題は以前よりも小さくなり、再帰が特定のレベルに達したら、結果を直接与える必要があるということです。

再帰関数を使用する場合、再帰の深さがオーバーフローしないように注意する必要がありますが、Python では通常、この深さは 1000 層であり、1000 層を超えると例外がスローされます。コンピュータでは、関数の再帰呼び出しはスタックと呼ばれるデータ構造によって実装されており、再帰が入るたびにスタックは 1 層追加され、関数が返されるたびにスタックは 1 層減ります。スタックのサイズは無限ではないため、再帰呼び出しが多すぎるとスタックがオーバーフローします。

再帰的走査が終了すると、各再帰の結果が返されます。たとえば、次の図は階乗の再帰関数の使用法です。再帰関数はn * fac(n-1)毎回計算fac(x)されますfac(6)、fac(5)、fac(4)、fac(3)、fac(2)、fac(1)6 * ( 5 * ( 4 * ( 3 * ( 2 * fac(1)))))

画像-20220901094537825

再帰の中心点は再帰 + 回帰です。まず、すべての走査可能な式が再帰的にループされます。最後の再帰が終了条件を満たし、戻り戻り値が取得され、その後、最後の再帰で取得された戻り戻り値が使用されます。次に、前の再帰で回帰して最終結果を取得します。

2. 再帰関数の使用

2.1. ケース 1

2.1.1. 要件の説明

必要:

  • 1) 関数を呼び出すときに任意の文字列を渡すことができます。
  • 2) 受信文字列の長さが 5 未満かどうかを確認し、5 未満の場合は、(受信実パラメータ + 'h') で構成される文字列のセットを結合します。
  • 3) そして、連結文字列の長さが 5 未満かどうかを判定し、5 未満の場合は「h」文字を付加して連結し、連結文字列の長さが一定になるまで循環させます。 4、各サイクルは、文字列リクエストが前のループの結果であると判断します。
  • 4) 最後に、入力された実際のパラメータと結合されます. 結合するとき、各サイクルの結果は実際のパラメータと結合される必要があります。
  • 5) 最初に渡された文字列の長さが 5 より大きい場合は、直接戻ります。

たとえば、「は」という文字列を渡して、最後に「ははははははは」を返したいとします。

このうち、ha は渡される実パラメータ、hahhahahahah は連結文字列です。連結文字列の長さは 1 サイクル目で判定され、長さは 5 未満の 2 になります。その後、h 文字が連結され、結果は「hah」です。2 番目のサイクルの長さは 3 (5 未満)、h 文字を結合すると結果は「hahh」、3 番目のループの長さは 4 (5 未満)、h 文字を結合すると結果が得られます。はぁ、4 番目のループは 5 未満ではありません。ループの最後で、各ループの結果が結合され、最後に受信した実際のパラメータと結合されて、はははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははははは?

2.1.2. 通常のループを使用して実装

実装アイデア:

  • まず、外部ループを定義して、受信する実際のパラメータの長さが 5 未満かどうかを判断します。5 未満の場合は文字列を連結し、それ以外の場合は実際のパラメータ値を直接返します。
  • 結合された文字列は非常に特徴的であるため、結合された文字列は入力実パラメータ + h 文字で構成されているため、入力実パラメータは別の変数 para2 に代入され、その後の結合は簡単です。
  • 連結文字列の長さが 5 未満かどうかも判断する必要があるため、変数 para2 を連結文字列として内側のループを再度宣言し、連結文字列が 5 未満である場合は連結文字列が 5 未満であるかどうかを判断する必要があります。 5 未満の場合は para2 の変数値を文字 h で結合し、para2 変数に代入することで、このループの結合結果が変数 para2 に定義され、次のループも判定されます前のループの結果に従って。
  • para2 変数で連結結果を定義します。この変数は渡された実パラメータと連結することもでき、para2 変数を仮パラメータ para1 と連結します。これにより、各サイクルの後、取得された結果がスティッチング後に渡された実パラメータと結合されます。 。
def join_5_str(para1):
    #首先定义一个循环,判断实参长度是否小于5,如果小于5,则进入循环
    while len(para1) < 5:
        #将形参para1的实参赋值给para2变量,拼接的字符串格式由传入的实参+'h'字符组成,因此可以将传入的实参先放在另外一个变量para2中
        para2 = para1
        #判断para2变量值的长度是否小于5,如果小于5,则进入循环
        while len(para2) < 5:
            #拼接字符串,将para2的变量值与字符'h'进行拼接,然后再赋值给para2变量
            para2 = para2 + 'h'
            #完成最终的字符串拼接,每次循环都会进行拼接,相当于是将每次的循环结果都进行了拼接
            #para1的变量值是传入的实参,para2的值是本次循环的拼接结果,然后将para1和para2拼接在一起,赋值给para1变量
            para1 = para1 + para2
    #如果实参的长度大于五,返回实参的值
    else:
            return para1

関数を呼び出して ha 文字列を渡し、戻り値を確認します。

print(join_5_str('ha'))

#输出结果:hahahhahhhahhh

画像-20220823164111398

関数の実行プロセスは次のように分析されます。

  • 実パラメータ 'ha' を仮パラメータ para1 に渡し、最初に ha の長さが 5 未満かどうかを判断し、5 未満の場合は最初の外側のループに入り、最も外側のループは 1 回だけループします。内側のサブループが完了した場合、文字列の長さは 5 より大きくなければなりません。
  • para2 変数に ha を代入し、para2 で結合された文字列の長さが 5 未満かどうかを判断します。 5 未満の場合は、最初の内側のループを開始します。
    • 初めて内部ループを開始します。para2 の値は ha、長さは 2、5 未満、条件は true、ha と h を結合して hah を取得し、それを para2 変数に代入して比較します。 hah と受信実パラメータ Ha を結合して haha​​h を取得し、para1 変数に割り当てます。
    • 最初の内側のループでは、結果が再度 para2 変数に割り当てられるため、2 番目の内側のループが開始されると、para2 の値は hah、para2 の長さは 3、長さは 5 未満で、h になります。を hah に結合して hahh を取得し、それを para2 変数に代入し、次に hahh と para1 変数を結合して para1 変数に代入すると、hahahhahh が得られます。
    • 3 番目の内側のループを開始します。考え方は同じです。para2 の値は hahh、para2 の長さは 4 で、5 未満です。h を hahh に結合し、hahaha を取得し、それを para2 変数に代入して、結合します。 haahah と para1 変数、この時点で、haahahahahahahahah が得られます。
    • 内側のループが 4 回目に始まりますが、このとき、para2 の値は haah、長さは 5 より大きく、ループは終了します。
  • 次に、2 番目の外側のループを開始します。この時点の para1 の値は haha​​hhahahaahahah で、長さは 5 より大きく、直接戻ります。

2.1.3. 再帰関数を使用した実装

私たちの要件は非常にユニークです。入力された文字列が 5 未満かどうかを判断し、5 未満の場合は文字列を連結できます。文字列を連結するだけのほうが良いと思いませんか? 連結された文字列は、入力される実際のパラメータ 連結文字列も 5 未満の場合は、ループ判定を入力し、文字 h を結合する必要があるため、ループを 2 つ使用していますが、要求がさらに嫌な場合は、スプライシングでスプライシング文字列を確認するには、長さが5未満であると判断されるので、ループ本体を大量に記述する必要はありません。

このような状況を踏まえて、次のように while ループを大量に記述することを避けるために、再帰関数を使用して実装することをお勧めします。

画像-20220823165200325

#运用递归函数
def join_5_str(para1):
    #判断实参的长度是否小于5,这也是递归函数的结束条件,只要不小于5了,递归循环就会结束
    if len(para1) < 5:
        #那么就为其拼接字符串,para1实参+递归函数join_5_str(para1 + 'h')的结果
        return para1 + join_5_str(para1 + 'h')
    else:
        return para1

#调用函数传入字符串ha
print(join_5_str('ha'))
#输出结果:hahahhahhhahhh

'''
return para1 + join_5_str(para1 + 'h')代码解释:
在这行代码中调用了join_5_str自身函数,相当于使用递归函数了,递归函数是一个隐式的循环体。
join_5_str(para1 + 'h')是递归函数的本体,递归函数的特点就是隐式循环,使用自身函数test2去处理(para1 + 'h'),第一次循环para1的值为调用join_5_str函数时传递的位置实参ha,每次都会将循环后的结果赋值给递归函数中的para1形参,依次循环,直到if len(para1) < 5条件不满足时结束递归函数的循环,拿到递归函数的返回结果,最后和函数体中的para1进行拼接。
    para1 + 'h'相当于para1 = para1 + 'h'。
    递归函数的循环过程;
        1)递归函数第一次循环:para1形参的值为ha,与h拼接后得到:para1 = ha + h,结果为hah,字符串的长度为3,满足if len(para1) < 5条件,继续进入下一次循环。
        2)递归函数第二次循环:para1形参的值为上次循环的结果为hah,与h拼接后得到:para1 = hah + h,结果为hahh,字符串的长度为4,满足if len(para1) < 5条件,继续进入下一次循环。
        3)递归函数第三次循环:para1形参的值为上次循环的结果为hahh,与h拼接后得到:para1 = hahh +h,结果为hahhh,字符串的长度为5,此时就不满足if len(para1) < 5这个条件了,递归函数循环结束。
    由于递归函数前的运算符是加法+,因此递归函数循环结束后,会将每次循环的结果相加,循环结果返回的如果是字符串就相当于字符串拼接,如果是数字就会累加求和,最终递归函数的结果就是:hah+hahh+hahhh=hahhahhhahhh。
    此时函数体中的para1的值就会和递归函数进行拼接,ha+hahhahhhahhh,得到hahahhahhhahhh。
    如果一开始para1形参接收的实参字符长度超过5了,就不会再使用递归函数拼接其他字符串,从而直接返回原字符串。
'''

上記の説明は特に分かりやすい説明です。再帰の原理がよく分からない方は、まず上の説明を読んでください。再帰関数に慣れてから、 で説明することもできます。再帰 + 再帰関数の回帰の方法:

1) 最初に再帰を開始します。

  • 関数呼び出し時に para1 に渡される実際のパラメータは ha です。
  • 最初の再帰を開始します: ha + h
  • 2 番目の再帰を開始します: ha + h + h
  • 3 回目の再帰を開始します: ha + h +h +h この時点では、再帰条件が満たされないため、ループは終了します。

2) 回帰を開始します。

  • 最後の再帰値から順に上向きに回帰します。
  • 最初の回帰 (2 回目の再帰の値) ハッハッハ + (3 回目の回帰の値) ハッハッハ
  • 2 回目の回帰 (1 回目の再帰の値) はぁ+ (2 回目の回帰の値) はぁはぁ
  • 実際のパラメータ ha + 再帰関数 haahahahah の戻り値が関数呼び出し時に渡され、haahahahahahah が取得されます。

2.2. ケース 2

これはケース 1 と非常に似ていますが、デモンストレーションのために文字列が数値に置き換えられる点が異なります。再帰関数ループが終了した後、各ループの結果が追加され、ループ結果が文字列を返す場合は同等です。 string splicing に、それが数値の場合、累積されて合計されます。

2.2.1. 要件の説明

必要:

  • 1) 関数を呼び出すときに任意の数値を渡すことができます。
  • 2) 受信した数値が 5 未満かどうかを判断し、5 未満の場合は数値を追加します。この数値は (受信した実際のパラメーター + 1) で構成されます。
  • 3) そして、加算した数が 5 未満かどうかも判断し、5 未満の場合は 1 を加算し、加算した数が 4 になるまでループメソッドを実行します。ループが判断されるたびに、その数は加算されます。は 2 番目のサイクルの結果である必要があります。
  • 4) 最後に、渡された実パラメータに加算されます。加算するときは、各サイクルの結果を実パラメータに加算する必要があります。
  • 5) 最初に渡された数値引数が 5 より大きい場合は、直接戻ります。

たとえば、数値 1 を渡すと、返される結果は 15 です。

このうち、1 は実際に渡されるパラメータ、14 は最後に返される結果です。最初のサイクルで実際に参加した回数を判断する必要があります。数値が 1 以上 5 未満の場合は、に 1 を加えます。結果は 1+1=2 、2 番目のサイクルの数は 2 で、5 より小さいのでそれに 1 を加えます。結果は 2+1=3 、3 番目のサイクルの数は 3 になります。 5 未満の場合、それに 1 を加算すると、結果 3+1=4 が得られます。4 番目のサイクルの数は 4、5 未満の場合、それに 1 を加算すると、結果は 4+1=5 になります。 5 番目のサイクルの値が 5 (5 以上) の場合、サイクルは終了し、各ループの結果を 2+3+4+5 で加算し、最後に 1+2+3+4+5 を受信する実際のパラメータに加算して取得します。 15.

2.2.2. 通常のループを使用して実装

実装アイデア:

  • まず、渡された実際のパラメータが 5 未満かどうかを判断する外側のループを定義します。5 未満の場合はそれに数値を追加します。それ以外の場合は、実際のパラメータ値を直接返します。
  • スプライスされた数値は非常に特徴的であるため、スプライスされた数値は入力される実際のパラメータ + 1 で構成されます。そのため、入力される実際のパラメータは、後で追加できる別の変数 para2 に割り当てられます。
  • スプライス数が5未満かどうかも判断する必要があるので、再度内側のループを宣言し、変数para2を追加した数にして、スプライス数が5未満かどうかを判断する必要があります。が5未満の場合は、para2の変数値と1Addを比較し、para2変数に代入することで、このループの加算結果が変数para2に定義され、次のループも判定されます。前のループの結果に従って。
  • 加算結果は para2 変数で定義され、渡された実パラメータに加算することもできます。また、para2 変数は仮パラメータ para1 に加算され、各サイクル後に取得された結果が渡された実パラメータと比較されます。追加で。
def sum_5_int(para1):
    #首先定义一个循环,判断实参是否小于5,如果小于5,则进入循环
    while para1 < 5:
        #将形参para1的实参赋值给para2变量,相加的数字格式由传入的实参 + 1组成,因此可以将传入的实参先放在另外一个变量para2中
        para2 = para1
        #判断para2变量值的是否小于5,如果小于5,则进入循环
        while para2 < 5:
            #相加数字,将para2的变量值与1进行相加,然后再赋值给para2变量
            para2 = para2 + 1
            #完成最终的数字相加,每次循环都会进行相加,相当于是将每次的循环结果都进行了相加
            #para1的变量值是传入的实参,para2的值是本次循环的相加结果,然后将para1和para2相加,赋值给para1变量
            para1 = para1 + para2
    else:
        return para1

数値 1 を渡し、戻り値を確認します。

print(sum_5_int(1))

#输出结果:15

画像-20220823220043279

関数の実行プロセスは次のように分析されます。

  • 実パラメータ 1 を仮パラメータ para1 に渡し、まず 1 が 5 より小さいかどうかを判断し、5 より小さい場合は、最初の外側のループに入ります。内側のサブループの後は、最も外側のループは 1 回だけループします。完了するには、数値は 5 より大きい必要があります。
  • para2 変数に 1 を代入し、para2 で加算した数値が 5 未満かどうかを判断します。5 未満の場合は、最初の内側のループを開始します。
    • 内側のループを初めて開始します。para2 の値は 1 (5 未満)、条件は true、1 と 1 を加算して 1+1=2 を取得し、それを para2 変数に代入し、2 を比較します。受信した実際のスプライシング用の 1 を参照して 3 を取得し、それを para1 変数に割り当てます。
    • 最初の内側のループでは、結果が再度 para2 変数に割り当てられるため、2 番目の内側のループが開始されるとき、para2 の値は 2 (5 未満) となり、2 + 1 を取得するには 2 に 1 を加えます。 =3 を指定し、値を代入します。 para2 変数に 3 を加算し、para1 変数に 3 を加算して、para1 変数に代入すると、6 が得られます。
    • 3 番目の内側ループを開始します。考え方は同じです。para2 の値は 3 (5 未満)、3 に 1 を加算して 3+1=4 を取得し、それを para2 変数に代入し、次に para1 に 3 を加算します。変数の場合は 10 が得られます。
    • 4 回目の内側ループを開始します。para2 の値は 4 です。5 未満の場合は、4 に 1 を加算し、4+1=5 になるまで待機してから、その値を para2 変数に代入し、その後、para2 変数に 5 を加算します。 para1 変数を指定すると、15 が得られます。
    • 内側のループが 5 回目に開始され、para2 の値が 5 以上の 5 になり、ループが終了します。
  • 次に、2 番目の外側のループを開始します。このときの para1 の値は 15、長さは 5 より大きく、直接戻ります。

2.2.3. 再帰関数を使用した実装

def sum_5_int(para1):
    #判断实参的是否小于5,这也是递归函数的结束条件,只要不小于5了,递归循环就会结束
    if para1 < 5:
        #那么就为其相加一个数字,para1实参+递归函数sum_5_int(para1 + 1)的结果
        return para1 + sum_5_int(para1 + 1)
        #sum_5_int(para1 + 1)递归函数的循环结果依次为:(1+1)+(2+1)+(3+1)+(4+1)
    else:
        return para1

print(sum_5_int(1))

'''
return para1 + sum_5_int(para1 + 1)代码解释:
在这行代码中调用了sum_5_int自身函数,相当于使用递归函数了,递归函数是一个隐式的循环体。
sum_5_int(para1 + 1)是递归函数的本体,递归函数的特点就是隐式循环,使用自身函数sum_5_int去处理(para1 + 1),第一次循环para1的值为调用sum_5_int函数时传递的位置实参1,每次都会将循环后的结果赋值给递归函数中的para1形参,依次循环,直到if para1 < 5条件不满足时结束递归函数的循环,拿到递归函数的返回结果,递归函数每次循环的结果都会累加在一起,最后和函数体中的para1进行相加,并返回。
    para1 + 1相当于para1 = para1 + 1。
    递归函数的循环过程;
        1)递归函数第一次循环:para1形参的值为1,与1相加后得到:para1 = 1 + 1,结果为1+1=2,数字为2,满足if len(para1) < 5条件,继续进入下一次循环。
        2)递归函数第二次循环:para1形参的值为上次循环的结果为2,与1相加后得到:para1 = 2 + 1,结果为2+1=3,数字为3,满足if len(para1) < 5条件,继续进入下一次循环。
        3)递归函数第三次循环:para1形参的值为上次循环的结果为3,与1相加后得到:para1 = 3 + 1,结果为3+1=4,数字为4,满足if len(para1) < 5条件,继续进入下一次循环。
        4)递归函数第四次循环:para1形参的值为上次循环的结果为4,与1相加后得到:para1 = 4 + 1,结果为4+1=5,数字为5,此时就不满足if len(para1) < 5这个条件了,此时递归函数循环结束。
    由于递归函数前的运算符是加法+,因此递归函数循环结束后,会将每次循环的结果相加,循环结果返回的如果是字符串就相当于字符串拼接,如果是数字就会累加求和,最终递归函数的结果就是:1+(1+1)+(2+1)+(3+1)+(4+1)=15
    如果一开始para1形参接收的实参字符长度超过5了,就不会再使用递归函数拼接其他字符串,从而直接返回原字符串。
'''

上記の説明は特にわかりやすい説明ですが、再帰関数の再帰+回帰を使って以下のような説明もできます。

1) 最初に再帰を開始します。

  • 関数呼び出し時に para1 に渡される実際のパラメータは 1 です。
  • 最初の再帰を開始します: 1 + 1 = 2
  • 2 番目の再帰を開始します: 2 + 1 = 3
  • 3 回目の再帰を開始します: 3 + 1 = 4
  • 4 番目の再帰を開始します: 4 + 1 = 5、この時点では再帰条件が満たされていないため、ループを終了します。

2) 回帰を開始します。

  • 最後の再帰値から順に上向きに回帰します。
  • 最初の回帰 (3 回目の再帰の値) 4 + (4 回目の回帰の値) 5 = 9
  • 2 回目の回帰 (2 回目の再帰の値) 3 + (3 回目の回帰の値) 9 = 12
  • 3 回目の回帰 (1 回目の再帰の値) 2 + (2 回目の回帰の値) 12 = 14
  • 関数呼び出し時に渡される実引数1+再帰関数の戻り値14、15を取得します。

3. 再帰関数を使用して階乗を計算する

3.1. 階乗の概念

正の整数の階乗は数学の概念であり、開発者が知っておくべき数学の概念でもあります。

階乗とは、この数値より小さいすべての正の整数の積を指します。たとえば、正の整数 2 の階乗が必要です。2 の階乗は、2 より小さいすべての正の整数の積です。2! = 1 * 2 = 2 。または、再帰的に (2-1)*2=2 を実行しても、結果は同じになります。

したがって、階乗の公式は次のようになります。n! = 1 * 2 * 3 * 4 * ··· * n

再帰階乗の公式は次のとおりです。(n-1)! * nまたはn * (n -1)!

6的阶乘:
普通阶乘的运算:
     6! = 1 * 2 * 3 * 4 * 5 *6 
        = 720
递归阶乘的运算:
	6! = (6-1)! * 6
    相当于用5的阶乘去乘以6

再帰階乗の結果は通常の階乗と同じですが、再帰階乗は積の範囲を狭めることができ、演算の結果も階乗になる点が異なります。

0 の階乗は 0、1 の階乗は 1 です。

3.2. 再帰関数を利用して階乗を実現するアルゴリズム

階乗アルゴリズムを実装するための再帰関数を定義しましょう。

def factorial(num):
    #0和1的阶乘都是本身,因此可以作为递归函数的循环结束条件
    if num == 1:
        return 1
    #采用递归阶乘的算法来实现需求:n * (n -1)!
    #定义阶乘的算法,传入的num形参值就是阶乘公式中的n,factorial(num - 1)就是(n -1)!
    #factorial(num - 1)是递归函数,会循环执行factorial函数体,直到不满足条件位置,循环结束,也就相当于是(n -1)!阶乘的运算过程
    return num * factorial(num - 1)


'''
代码解释:
	首先定义一个递归函数的循环结束体,当num的变量值等于1时,结束循环,返回1。
	如果不满足if num == 1条件,则执行num * factorial(num - 1)代码,计算传入的实参的阶乘。
	计算实参的阶乘我们通过公式n * (n -1)!作为依据,例如传入2,得到的式子就是2*(2-1)! = 2*1! = 2
	其中:n通过num形参传入,(n -1)!是计算阶乘的部分,所以通过递归函数循环去计算,然后将每次循环的结果累加在一起,最终就是传入实参的阶乘值。
'''

次に、factorial 関数を呼び出して、正の整数の階乗を計算します。

print(factorial(6))
print(factorial(600))

画像-20220824115159452

実際のパラメータの 6 ビットを渡して、関数の実行プロセスを推論してみましょう。

  • 1) まず実パラメータ 6 が 1 に等しいかどうかを判断し、1 に等しい場合は 1 を返し、以下のコードは実行しません。
  • 2) 実際のパラメータ 6 が 1 に等しくない場合は、階乗アルゴリズム num * fastial(num - 1) を入力します。num は 6、factorial(num - 1) は再帰関数です。再帰関数は次のようにループする必要があります。条件が満たされなくなるまで関数本体内のコードを実行し、各サイクルの結果に num を乗算し、最後に num の階乗を取得します。
  • 3) 階乗 (数値 - 1) 再帰関数ループ処理:
    • 1. 最初の再帰関数のループ: num パラメータは 6、1 を引いた後、num = 6 - 1、結果は 6-1=5、num は 5、if num == 1 の条件が満たされない場合満足したら、次の時間サイクルに入ります。
    • 2. 再帰関数の 2 番目のループ: num 仮パラメータは 5、1 を引いた後、num = 5 - 1 が得られ、結果は 5-1=4、num は 4、if num == 1 の条件の場合が満たされていない場合は、次の時間サイクルに入ります。
    • 3. 再帰関数の 3 番目のサイクル: num パラメータは 4、1 を引いた後、num = 4 - 1 が得られ、結果は 4-1=4、num は 3、if num == 1 の条件の場合が満たされていない場合は、次の時間サイクルに入ります。
    • 4. 再帰関数の 4 番目のループ: num パラメーターは 3、1 を引いた後、num = 3 - 1、結果は 3-1=2、num は 2 (if num == 1 の条件が満たされない場合)満足したら、次の時間サイクルに入ります。
    • 5. 再帰関数の 3 番目のループ: num 仮パラメータは 2、1 を引いた後、num = 2 - 1 が得られ、結果は 2-1 = 1、num は 1、および if num == の条件が得られます。 1 が満たされると、再帰関数ループは終了します。
  • 4) 再帰関数の前の演算子は乗算であるため*、再帰関数のループ終了後、各ループの結果が乗算され、再帰関数の最終結果は になります5*4*3*2*1
  • 5) 最後に、渡された実パラメータ 6 と乗算する6*5*4*3*2*1=720と、実パラメータ 6 の階乗は 720 になります。

上記の説明は特にわかりやすい説明ですが、再帰関数の再帰+回帰を使って以下のような説明もできます。

1) 最初に再帰を開始します。

  • 関数呼び出し時に para1 に渡される実際のパラメータは 6 です。
  • 最初の再帰を開始します:階乗(6 - 1) =階乗(5)
  • 2 番目の再帰を開始します:階乗(5 - 1) =階乗(4)
  • 3 番目の再帰を開始します:階乗(4 - 1) =階乗(3)
  • 4 番目の再帰を開始します:階乗(3 - 1) =階乗(2)
  • 5 番目の再帰を開始します:階乗(2 - 1) =階乗(1)。この時点では再帰条件が満たされていないため、ループを終了します。

2) 回帰を開始します。

  • 最後の再帰で得られた戻り値は 1 であり、最後の再帰で返された値が順番に返されますが、この再帰で使用される演算子は乗算であるため、戻り値も乗算になります。
  • 最初の回帰: (5 回目の再帰の値) 1 * (戻り値) 1 = 1
  • 2 番目の回帰: (4 番目の再帰の値) 2 * (5 番目の回帰の値) 1 = 2
  • 3 回目の回帰: (3 回目の再帰の値) 3 * (4 回目の回帰の値) 2 = 6
  • 4 番目の回帰: (2 回目の再帰の値) 4 * (3 回目の回帰の値) 6 = 24
  • 5 番目の回帰: (最初の再帰の値) 5 * (2 番目の回帰の値) 24 = 120
  • この時点で、再帰関数は戻り値 120 を取得し、これに受信する実際のパラメータを乗算します (6*120=600)。
  • または、次のような回帰として見ることもできますreturn 6*120*24*6*2*1

ここに画像の説明を挿入

3.3. 階乗アルゴリズムを規則的なループで実現する

def factorial(num):
    #判断传入的参数是否不等于1
    while num != 1:
        #将传入的实参赋值给numfac变量,用于做阶乘(n-1)!公式
        numfac = num
        #如果numfac的值不为1,则开始循环
        while numfac != 1:
            #每次循环都将numfac的值减1,然后再赋值给numfac变量,直到numfac=1时,退出循环,这里对应的阶乘公式为(n-1)!
            numfac = numfac - 1
            #每次循环时都将numfac与num形参相乘,这里对应阶乘的公式n*(n-1)!
            num = num * numfac
        #循环完后,实参的阶乘就计算完毕了,返回
        return num
    #如果等于1,则直接返回1
    else:
         return 1

print(factorial(6))
print(factorial(600))

画像-20220824115159452

実際のパラメータの 6 ビットを渡して、関数の実行プロセスを推論してみましょう。

  • 1) まず、実パラメータ 6 が 1 に等しいかどうかを判断し、1 に等しくない場合は外側のループ本体を実行します。そうでない場合は、直接 1 を返します。

  • 2) 外側のループは最初のループを開始し、受信した実パラメータ 6 を numfac 変数に割り当ててから、内側のループ本体の実行を開始します。

    • 1. 内側のループを初めて開始し、numfac の値が 6 で 1 に等しくなく、条件が満たされ、numfac の値が 1 減ります (つまり、6-1=5)。時刻では、numfac の値は 5 で、num パラメータは「実際のパラメータは 6」に対応し、実際のパラメータ 6 に numfac の値 5 が乗算されて、num 変数に代入されます。

    • 2. 2 番目の内側ループを開始します。このときの numfac の値は 5 であり、1 ではありません。条件が満たされると、numfac の値は 1 減ります。つまり、5-1=4 です。今回は、numfac の値は 4 で、変数 num の値は最後のカウント後の 30 です。その後、num 変数の値 30 に numfac の値 4 を乗算して、num 変数に代入します。

    • 3. 3 番目の内側ループを開始します。このときの numfac の値は 4 であり、1 ではありません。条件が満たされると、numfac の値は 1 減ります。つまり、4-1=3 です。今回は、numfac の値は 3 で、変数 num の値は最後のカウント後の 120 です。その後、num 変数の値 120 に numfac の値 3 を乗算して、num 変数に代入します。

    • 4. 4 番目の内側ループを開始します。このときの numfac の値は 3 であり、1 ではありません。条件が満たされると、numfac の値は 1 減ります (3-1=2)。今回は、numfac の値が 2 で、変数 num の値は最後の計算後の 360 です。その後、num 変数の値 360 に numfac の値 2 を乗算して、num 変数に代入します。

    • 5. 5 番目の内側ループを開始します。このときの numfac の値は 2 であり、1 ではありません。条件が満たされると、numfac の値は 1 減ります (2-1=1)。今回は、numfac の値が 1 で、変数 num の最後の計算後の値は 720 です。その後、num 変数の値 720 に numfac の値 1 を乗算して、num 変数に代入します。

    • 6. 6 番目の内側ループを開始します。numfac アドレスの値は 1、1 は 1 に等しく、条件は true ではなく、内側ループは終了します。

  • 3) 内側のループ本体を実行した後、この時点で num 変数の値を直接返します。この値は実パラメータの階乗の結果です。外側のループを一度再循環することはできません。そうしないと、アルゴリズムが間違ってしまいます。

4. 再帰関数を使用してフィボナッチ数列を計算する

黄金分割数列とも呼ばれるフィボナッチ数列(Fibonacci sequence)は、数学者のレオナルド・フィボナッチ(Leonardoda Fibonacci)がウサギの飼育を例にして導入したものであるため、「ウサギ数列」とも呼ばれるこのような数列です。 : 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 数学では、フィボナッチ数列は次のように再帰的に定義されます: F(0 )=0、F(1)=1、現代物理学、準結晶構造、化学およびその他の分野における F(n)=F(n - 1)+F(n - 2) (n ≥ 2、n ∈ N*)

次のアルゴリズムは、再帰関数を使用してフィボナッチ数列を実装します。

def fibonacci(n):
    """使用递归函数计算斐波那契数列"""
    if n == 0:
        return 0
    if n == 1:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)

fibonacci() 関数を使用して、整数 6 のフィボナッチ数列をクエリします。

print(fibonacci(6))

#输出结果8。

なぜ8が出力されるのでしょうか?入力した整数は 6 なので、フィボナッチ数列の最初の 6 つの数値を見つけ、最後の 2 つの数値を加算して 8 を求めます。

次のメソッドを使用して、対応するフィボナッチ数列を出力できます。

num = int(input("请输入一个数字:"))
if num <= 0:
    print('请输入一个正数')
else:
    for i in range(num):
        print(fibonacci(i))
#print(fibonacci(6))

画像-20220824142334268

フィボナッチ数列の再帰フローチャート:

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_44953658/article/details/131181838