一:基本概念
分治算法从字面上理解就是分而治之,通常将一个很大、很复杂的问题化分为许多的子问题求解,子问题又可以继续分下去…直到可以很轻松的将问题解决,最后将各个子问题的解合并,合并之后的答案就是原问题的答案。一般来说,子问题的规模、复杂度等都比原问题简单不少,相应的求解速度和难度也就会好很多。
二:应用场景
- 原问题可以分解成许多子问题。 通常这些子问题只是规模有所降低,其求解方法与原问题相同或相似。
- 原问题在求解的过程中,递归的求解子问题。 在将问题分解的时候,要注意递归的终止条件,通常这也是问题无法再分解的条件。
- 原问题解的合并。 在完成一系列子问题的求解后,能够用某种方法合并出原问题的解。
三:例子
下面我们通过两个例子进一步加深对分治算法的理解
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()
运行结果: