分治算法

一:基本概念

分治算法从字面上理解就是分而治之,通常将一个很大、很复杂的问题化分为许多的子问题求解,子问题又可以继续分下去…直到可以很轻松的将问题解决,最后将各个子问题的解合并,合并之后的答案就是原问题的答案。一般来说,子问题的规模、复杂度等都比原问题简单不少,相应的求解速度和难度也就会好很多。

二:应用场景
  • 原问题可以分解成许多子问题。 通常这些子问题只是规模有所降低,其求解方法与原问题相同或相似。
  • 原问题在求解的过程中,递归的求解子问题。 在将问题分解的时候,要注意递归的终止条件,通常这也是问题无法再分解的条件。
  • 原问题解的合并。 在完成一系列子问题的求解后,能够用某种方法合并出原问题的解。
三:例子

下面我们通过两个例子进一步加深对分治算法的理解

3.1:假币问题

问题描述:
有8个大小、形状完全相同的硬币,但是其中有一个假币,如何尽快将假币找出来?已知:假币的重量比真币轻。
求解思路:
对于这个问题,我们可以一个一个找,将每一个硬币都称量一下,选出最轻的那一个就是假币。这种方法就是靠运气的了,在硬币数量较少的时候尚可一试;如果有1000个硬币就不可行了。我们可以换一种思路:每次将硬币分成两组,分别称量两组硬币的重量,重量较轻的一组一定包含假币,再将重量较轻的这组继续分,重复上述过程,直到找出假币为止。
代码示例:

import random

def er_fen(coin):
    half = int(len(coin)/2)
    if half == 1:
        if coin[0] < coin[1]:
            print('假币为:'+str(coin[0]))
            return
        else:
            print('假币为:'+str(coin[1]))
            return
    before = coin[:half:]
    after = coin[half::]
    before_sum = 0
    after_sum = 0
    for i in range(int(len(before))):
        before_sum += before[i]
    for j in range(int(len(after))):
        after_sum += after[j]

    if before_sum < after_sum:
        er_fen(before)
    else:
        er_fen(after)

def main():
    Coin = [1] * 8
    FalseCoin = random.randint(0, 8)
    Coin[FalseCoin] = random.random()  # 生成0~1之间的小数,代表假币
    er_fen(Coin)

if __name__ == '__main__':
    main()
3.2:循环比赛问题

问题描述:
设有N个选手的循环比赛。其中N=2^k,要求每名选手要与其他N-1名选手都赛一次。每名选手每天比赛一次,循环赛共进行N-1天,要求每天没有选手轮空。
求解思路:
对于这样一个问题,我们可能会束手无策,因为原问题实在是太复杂了,所以我们考虑将其分解为一系列小问题。考虑到N满足2的幂次方,我们不妨先考虑有2支球队比赛的情况:
在这里插入图片描述
2支球队的情况比较简单,下面我们再看4支球队的情况,看他们之间有什么关系?
在这里插入图片描述
可以看出,这个矩阵的左上部分是2支球队的情况,3、4列的值是由1、2列对应元素分别+2得到的,整个矩阵又是一个对阵矩阵,进而,我们就可以推断出整个矩阵的情况了。

接下来,我们来看看8支球队是否可以由4支球队推出来?
在这里插入图片描述
可以看出,8x8矩阵的左上部分与4x4矩阵完全相同,5、6、7、8列是由1、2、3、4列对应元素分别+4得到的。所以,我们可以按同样的方法得到8x8矩阵。

从上面的分析我们可以看出,一个N(N满足2的幂次方)支球队的比赛安排表,我们最终可以分解到2支球队的比赛安排表,再由2支球队的解合成4支球队的解,4支球队的解合成8支球队的解…直到我们得到原问题的解。这就是一个典型的分治算法的例子。

代码示例

import numpy as np

def scheduleTable(array,N):
    if N == 1:                      # 如果只有一支球队
        array[0][0] = 1
    else:
        # 填充左上区域
        M = int(N / 2)
        scheduleTable(array,M)

        # 根据已填充好的左上区域,填充右上区域
        for i in range(M):
            for j in range(M,N):
                array[i][j] = array[i][j-M] + M

        # 填充左下区域
        for i in range(M,N):
            for j in range(M):
                array[i][j] = array[j][i]

        # 填充右下区域
        for i in range(M,N):
            for j in range(M,N):
                array[i][j] = array[i-M][j-M]

def PrintTable(table,N):
    for i in range(N):
        for j in range(N):
            print(' %3d ' % (int(table[i][j])),end='')
        print()


def main():
    print('==================================')
    print('队伍数量要求为2的幂次方')
    print('第一列代表本队,其余列代表对手')
    print('==================================')
    N = int(input('请输入队伍数量:'))
    table = np.zeros((N,N))
    scheduleTable(table,N)
    PrintTable(table,N)

if __name__ == '__main__':
    main()

运行结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

steven_moyu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值