美文网首页
算法基础(II)-递归(回文、斐波那契、欧几里得算法、汉诺塔)

算法基础(II)-递归(回文、斐波那契、欧几里得算法、汉诺塔)

作者: wopen | 来源:发表于2018-12-22 16:48 被阅读0次

    什么是递归?

    递归(Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。例如,当两面镜子相互之间近似平行时,镜中嵌套的图像是以无限递归的形式出现的。也可以理解为自我复制的过程。

    递归是将一个大问题分解成小问题,任何递归函数都可以用迭代函数实现。递归一般比迭代开销更大,但是递归函数非常好理解它的意思。

    用递归解决问题要注意终止条件,没有终止条件函数一直运行下去直到报错。

    回文字符串

    回文(palindrome)字符串是一个字符串从左到右读和从右到左读完全一样,比如abcba

    用递归的方法怎么判断一个字符串是不是回文字符串?一个字符串是回文字符串那么它第一个字符就等于最后一个字符,第二个字符就等于倒数第二个字符...。那么什么是终止条件?那就是当一个字符串只有一个字符的时候(从左到右读和从右到左读完全一样)。

    def pal(s):
        if len(s) <= 1: return True
        return s[0] == s[len(s) - 1] and pal(s[1:len(s) - 1])
    

    阶乘

    阶乘(factorial)是所有小于及等于该数的正整数的积。n的阶乘计为n!0! = 1,当大于0n!=n*(n-1)

    def fac(n):
        if n == 0: return 1
        return n * fac(n - 1)
    

    可以看到用递归的方式写非常的简单,表达的意思和定义它的意思一样。

    斐波那契数列

    斐波那契(Fibonacci)数列是递归的方法来定义的:

    F_0 = 0; F_1 = 1; F_n=F_{n-1}+F_{n-2}

    0,1,1,2,3,5,7... n大于2n等于前两个数的相加。

    def fib(n):
        if n <= 1: return 0
        elif n == 2: return 1
        else: return fib(n-1) + fib(n - 2)
    

    和定义的意思一样一目了然,虽然效率不高。

    欧几里得算法

    在数学中,辗转相除法,又称欧几里得算法(Euclidean algorithm),是求最大公约数的算法。辗转相除法首次出现于欧几里得的《几何原本》(第VII卷,命题i和ii)中,而在中国则可以追溯至东汉出现的《九章算术》。

    简单来说a,b的最大公约数等于其中较小数和两数的差的最大公约数,比如100,10它们的最大公约数为100-10,1090-10,10一直持续下去。也可以看出两个数的最大公约数等于较小数和较小数除以较大数的余数的最大公约数。

    欧几里得算法将一个大问题不断缩小,直到小到我们可以轻松解决。

    def gcd(a,b):
        if b == 0:
            return a
        return gcd(b, a % b)
    

    汉诺塔

    汉诺塔(港台:河内塔)是根据一个传说形成的数学问题:

    有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:

    1. 每次只能移动一个圆盘;
    2. 大盘不能叠在小盘上面。

    传说越南河内某间寺院有三根银棒,上串 64 个金盘。寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕,世界就会灭亡。这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。但不知道是卢卡斯自创的这个传说,还是他受他人启发。

    image

    用代码怎么表示n个圆盘是怎么移动的呢?

    我们可以发现要把全部圆盘移动到目标柱就必须先把最大的圆盘移动到目标柱(因为大盘不能叠在小盘上面),然后把第二大的移动到目标柱上...这样一直持续下去直到所有圆盘放到目标柱上。

    要把大的圆盘移动到目标柱就必须把它上面小的圆盘移动到备用柱(必须要解决这个子问题才能把大圆盘放到目标柱上),移动到备用柱后再把大圆盘放到目标柱上,最后把其他小圆盘放到目标柱,这样就OK了。

    def hanoi(n, form, to, spare):
        if n == 1:
            print('从 %s 移动到 %s' % (form, to))
        else:
            hanoi(n-1, form, spare, to) # 除最底下的大圆盘把其他移动到备用柱
            hanoi(1, form, to, spare) # 把最底下的大圆盘移动到目标柱
            hanoi(n-1, spare, to, form) # 再把备用柱上的圆盘移动到目标柱
    hanoi(3, '起始柱', '目标柱', '备用柱')
    

    对于一个圆盘只需要一步(直接从从起始柱移动到目标柱)。

    对于两个圆盘就需要把它上面的一个圆盘移动到备用盘,然后把最大的圆盘放到目标柱,最后把备用柱上的放到目标柱子上。一共是3步。

    如果有三个圆盘就要把上面两个圆盘移动到备用柱子(把两个圆盘放到目标柱上需要3步),然后把最大的圆盘放到目标柱(需要1步),最后把备用柱上的放到目标柱子上(需要3步)。一共7步。

    依次类推有n个圆盘就需要两倍n-1个圆盘需要的步数再+1。对于n个圆盘的步数就为2^n-1步。可以看出来它是指数复杂度

    如果n等于64最少就需要移动2^{64}-1步,如果一秒钟能移动一块圆盘,仍将需 5845.54 亿年。目前按照宇宙大爆炸理论的推测,宇宙的年龄仅为 137 亿年。

    相关文章

      网友评论

          本文标题:算法基础(II)-递归(回文、斐波那契、欧几里得算法、汉诺塔)

          本文链接:https://www.haomeiwen.com/subject/rrzikqtx.html