D&C与快速排序|Python实现

1. D&C:分而治之

我们曾经在证明欧几里得算法的正确性时提出过一个可以抽象并用欧几里得算法解决的问题——分土地的问题。现在,我们从一个更加直观的角度来看看如何解决这个问题。

在拿到一块1680×640的土地时,我们想要把它平均划分成正方形,并且试图保证正方形面积最大。我们首先将土地划分出最大的正方形,可以画出两块640×640的土地,剩下一块400×640的土地。之后我们对剩下的小长方形土地继续应用这个思路、画出一块400×400的正方形土地、一块400×240的小长方形土地……分到最后,我们将得到一块160×80的土地,恰好可以完全分成两块80×80的正方形土地,而80就是我们要找的答案——问题就这样被解决了。

仔细思考这个过程,其实这就是欧几里得算法的直观展现,我们的证明也保证了这一过程所得结果得正确性。现在,我们抛开欧几里得算法,来看看这个过程得其它特点:

  1. 问题的规模被不断缩小——小长方形土地的面积不断变小。
  2. 有明显的递归特征——存在基线条件、递归条件与一个反复使用的方法:划出正方形土地与小长方形土地。

这个过程就是我们要讨论的一种解决问题的方法:分而治之(divide and conquer, D&C)

分而治之的工作原理就是

  1. 找出简单的基线条件。
  2. 确定缩小问题规模的方法,使问题最终符合基线条件。

这个算法并不像我们前面介绍的欧几里得算法那样明确,但这种算法为我们提供了解决问题的思路。下面,我们先来看看欧几里得算法的具体实现,载举一个例子看看D&C如何进行,最后我们讨论一种优雅快速的算法——快速排序,来加深对分而治之的理解。


2. 两个简单的例子

a. 欧几里得算法:

关于这个问题我们已经讨论了很多了,所以直接给出代码:

def euclidean_algorithm(numx,numy):
	'欧几里得算法'

	num_max = max(numx,numy)
	num_min = min(numx,numy)

	if num_max % num_min == 0:
		#基线条件。
		return num_min
	else:
		return euclidean_algorithm(num_min,num_max%num_min)

经过测试,代码工作良好。

b. 加数字:

假设我们有一个数字列表,我们想求出这个列表里所有元素的和。我们可以使用Python内置的sum()函数,也可以自己使用一个简单的循环求和。但是,这里我们运用分而治之与递归的思想。

分而治之要求我们缩小问题规模,递归要求我们找到基线条件——什么样的列表最好求和?当然是空列表,因为这个和值一定是0。那么,我们可以减小列表的长度来缩小问题规模,直到达到基线条件。

就像这样:

def my_sum(lt):
	'应用分而治之'

	if len(lt) == 0:
		return 0
	else:
		return lt.pop() + my_sum(lt)

经过测试,代码工作良好。

一些语言的编程方式称为函数式编程——这意味你无法使用循环。现在你应该好好想想递归了,否则使用这类语言时你可能连求和都无法完成。


3. 快速排序

下面我们将再次见识D&C,或者说是递归的神奇之处:

我们曾经编写过选择排序的代码,使用了循环对列表一次又一次的检查——让人感觉思路并不优雅。我们能不能在排序的时候运用我们学到的快速排序呢?

让我们来想想基线条件——什么是最简单的排序呢?当然是只有一个数字或者没有数字的列表了,那样的话我们只需返回这个列表就可以了。

如果不是这种简单的列表呢?我们需要想一个简单的办法:我们可以选择第一个元素作为基准,把所有比基准大的数放到后面,把比基准大的放到后面,然后组合起来就可以了——但是前面和后面的列表是无序的!没关系,我们把这个问题交给递归——我们也使用这种方法对这些无序的列表进行排序就可以了。

这种简单优雅的思路让我爱上了递归。代码也很简单:

def quick_sort(lt):
	'快速排序'

	if len(lt) <= 1:
		return lt
	else:
		pivot = lt[0]
		less = [i for i in lt[1:] if i < pivot]
		greater = [i for i in lt[1:] if i > pivot]
		return quick_sort(less) + [pivot] + quick_sort(greater)

我们似乎把一切工作都交给了这个函数自己,但出乎意料的是工作的很完美:

lt = [3,8,9,5,4,6,7,1,2]
print(quick_sort(lt))

结果是

[1, 2, 3, 4, 5, 6, 7, 8, 9]

这就像我们用简陋的几行递归代码解决复杂的无与伦比的汉诺塔问题时所感到的惊讶与惊喜!


之后我们还会讨论快速排序——我们将讨论它的运行时间。但这篇文章中我们的主题是递归,所有就到此结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值