介绍归纳、递归

本文深入探讨了归纳法和递归在解决问题中的应用,通过实例展示了如何将复杂问题简化为已知的算法。文章指出,归纳法是从一个基础情况开始,通过递归步骤解决整个问题,而递归则是将问题分解,从整体开始逐步分解为子问题。文中还对比了归纳和递归在解决问题时的不同视角,并提供了具体的代码示例,如找到序列中最接近的两个数字、排序算法的递归实现等,强调了理解和运用这两种方法的重要性。
摘要由CSDN通过智能技术生成

简单地说,将一个问题A简化为另一个问题B涉及到某种形式的转换,然后解决方案              给B(直接或通过一些按摩)给A一个解决方案。一旦你学会了一系列标准算法              (在这本书中你会遇到很多),这是你遇到新问题时通常会做的事情。你能              以某种方式改变它,以便用你知道的方法之一来解决它?在许多方面,这是核心              解决所有问题的过程。              让我们举个例子。你有一个数字列表,你想找到两个不相同的数字              彼此最接近(即绝对差最小的两个): 

from random import rangdrange
seq = [randrange(10**10) for i in range(100)]
dd = float("inf")
for x in seq:
 for y in seq:
   if x == y:continue
   d = abs(x-y)
   if d < dd:
     xx,yy,dd = x,y,d

xx, yy
(15743, 15774)
两个嵌套循环,都在seq上;很明显这是二次循环,这通常不是一件好事。              假设你已经研究过一些算法,你知道如果序列              排序。您还知道排序通常是对数线性的,或q(n lg n)。明白了吗?这里的见解是              在排序顺序中,两个最近的数字必须相邻: 

seq.sort()
dd = float("inf")
for i in range(len(seq)-1):
    x, y  = seq[i],seq[i-1]
    if x == y:continue
    d = abs(x-y)
    if d < dd:
     xx,yy,dd=x,y,d      

更快的算法,同样的解决方案。新的运行时间是对数线性的,以排序为主。我们的原创              问题是“在一个序列中找到两个最接近的数字”,我们将其简化为“在              排序序列,”通过排序序列在这种情况下,我们的减少(排序)不会影响我们得到的答案。              一般来说,我们可能需要改变答案,使其符合原始问题。 

注意,在某种程度上,我们只是将问题分为两部分,对排序的序列进行排序和扫描。你也可以              说扫描是一种减少原始问题到排序序列的问题的方法。一切都是关于              观点。 

把A减到B有点像说“你想解A吗?”哦,这很容易,只要你能解出b。”见图4-1              为了说明减少是如何工作的。 

图4-1。使用从A到B的约简,用B的算法求解A。B的算法(中心,内部              circle)可以转换输入b吗?输出B!,而还原包含两个转换(较小的              圆圈)从A开始?到B?从B!A!,一起形成主算法,哪个转换输入A?到              输出A! 

一,二,很多              我已经用归纳法解决了第3章中的一些问题,但是让我们回顾一下,并通过以下几个步骤来解决              例子。在抽象地描述归纳法时,我们说我们有一个命题或陈述,p(n),并且我们              想证明对任何自然数n都是正确的。例如,假设我们正在研究第一个n的和              奇数;p(n)可以是以下语句: 

这是非常熟悉的,几乎和我们在上一章中讨论过的握手和一样。你              通过调整握手公式可以很容易地得到这个新结果,但是让我们看看我们如何通过归纳法证明它。              相反。归纳法的思想是让我们的证明“扫过”所有自然数,有点像一排多米诺骨牌。              坠落。我们首先建立P(1),这在本例中非常明显,然后我们需要展示每个Domino,              如果它掉下来,下一个就会倒掉。换句话说,我们必须证明,如果p(n-1)是真的,那么p(n)就是              也是如此。              如果我们能证明这一含义,即p(n–1)p(n),结果将扫过n的所有值,从              P(1),用P(1)P(2)建立P(2),然后转到P(3)、P(4)等。换句话说,关键是              以确定让我们更进一步的含义。我们称之为归纳步骤。在我们的示例中,这意味着              我们假设如下(p(n-1)): 


            

镜子,镜子              在他出色的网络视频节目中,泽弗兰克曾经说过:“你知道没有什么可以害怕的,但是              恐惧本身。“是的,这叫做递归,这会导致无限的恐惧,所以谢谢你。”4              建议是,“为了理解递归,我们必须首先理解递归。”              的确。尽管无限递归是一种相当病态的行为,但递归很难让人信服。              在某种程度上,递归实际上只有作为归纳的镜像才有意义(见图4-3)。在归纳法中,我们              (概念上)从一个基本情况开始,展示归纳步骤是如何使我们更进一步,直至完整的问题。              大小,n.对于弱诱导,6我们假设(诱导假设)我们的解适用于n-1,由此,我们              推断它适用于n。递归通常更像是分解事物。从一个完整的问题开始,              大小为n。将大小为n–1的子问题委托给递归调用,等待结果,然后扩展子解决方案              找到一个完整的解决方案。我相信你能明白这是怎么回事。在某种程度上,归纳法告诉我们              为什么递归有效,递归给了我们一个简单的方法(直接)实现归纳思想。 

图4-3。归纳(在左边)和递归(在右边),作为彼此的镜像 

例如,以前一节中的跳板问题为例。制定解决方案的最简单方法              对此(至少在我看来)是递归的。你放置一个L形件,这样你得到四个等价的子问题,和              然后你递归地解决它们。通过归纳,解决方案是正确的。 

实现棋盘式覆盖 

尽管棋盘覆盖问题在概念上有一个非常简单的递归解决方案,但是实现它可以              需要一些思考。实现的细节对示例的要点并不重要,所以感觉              如果你愿意,可以跳过这个侧边栏。实现解决方案的一种方法如下所示: 

def cover(board,lab=1,top=0,left=0,side=None):
    if side is None: side = len(board)
    ## side length of subboard:
    s = side//2
    ## offsets foe outer/inner squares of subboards:
    offstes = (0,-1),(side-1,0)
    for dy_outer,dy_inner in offstes:
        for dx_outer,dx_inner in offsets:
            # if the outer corner is not set..
            ## ... label the inner corner:
            board[top+s+dy_inner][left+s_dx-inner] = lab
        # next label:
        lab +=1
        if s > 1:
            for dy in [0,s]:
                #  
                for dx in [0,s]:
                    lab = cover(board,lab,top+dy,left+dx,s)
        return lab

##

board = [[0]*8 for i in range(8)]  ## Eight by eight checkerboard
board[7][7] = -1
cover(board)
for row in board:
    print("%si*8) % tuple(row))
    
    
 def ins_sort_rec(seq,i):
     if i ==0:return 
     ins_sort_rec(seq,i-1)
     j = i
     while j > 0 and seq[j-1] >seq[j]:
         seq[j-1],seq[j] =seq[j],seq[j-1]
         j -=1

这些算法并没有那么有用,但它们通常是被教授的,因为它们是很好的例子。而且,他们是              经典,所以任何一个算法师都应该知道它们是如何工作的。 

再一次,你可以看到这两者非常相似。递归实现显式表示              归纳假设(作为递归调用),而迭代版本显式表示重复执行              归纳步骤。两者都是通过找到最大的元素(for循环查找max_j)并将其交换到              正在考虑的序列前缀的结尾。注意,您也可以在              此节从开始,而不是从结束(在插入排序中,将所有对象排序到右侧,或查找              选择排序中的最小元素) 

def sel_sort_rec(seq,i):
    if i == 0:return
    max_j = i 
    for j in range(i):
      if seq[j] > seq[max_j]:max_j = j
    seq[i],seq[max_j] = seq[max_j],seq[i]
    sel_sort_rec(seq,i-1)

# Selection Sort
def sel_sort(seq):
    for i in range(len(seq)-1,0,-1):
        max_j = i
        for j in range(i):

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值