Python分治法定义与应用实例详解


Posted in Python onJuly 28, 2017

本文实例讲述了Python分治法定义与应用。分享给大家供大家参考,具体如下:

分治法所能解决的问题一般具有以下几个特征:

1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;

第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;

第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。

第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。

题目1. 给定一个顺序表,编写一个求出其最大值的分治算法。

# 基本子算法(子问题规模小于等于 2 时)
def get_max(max_list):
  return max(max_list) # 这里偷个懒!
# 分治法 版本一
def solve(init_list):
  n = len(init_list)
  if n <= 2: # 若问题规模小于等于 2,最终解决
    return get_max(init_list)
  # 分解(子问题规模为 2,最后一个可能为 1)
  temp_list=(init_list[i:i+2] for i in range(0, n, 2))
  # 分治,合并
  max_list = list(map(get_max, temp_list))
  # 递归(树)
  solve(max_list)
# 分治法 版本二
def solve2(init_list):
  n = len(init_list)
  if n <= 2: # 若问题规模小于等于 2,解决
    return get_max(init_list)
  # 分解(子问题规模为 n/2)
  left_list, right_list = init_list[:n//2], init_list[n//2:]
  # 递归(树),分治
  left_max, right_max = solve2(left_list), solve2(right_list)
  # 合并
  return get_max([left_max, right_max])
if __name__ == "__main__":
  # 测试数据
  test_list = [12,2,23,45,67,3,2,4,45,63,24,23]
  # 求最大值
  print(solve(test_list)) # 67
  print(solve2(test_list)) # 67

题目2. 给定一个顺序表,判断某个元素是否在其中。

# 子问题算法(子问题规模为 1)
def is_in_list(init_list, el):
  return [False, True][init_list[0] == el]
# 分治法
def solve(init_list, el):
  n = len(init_list)
  if n == 1: # 若问题规模等于 1,直接解决
    return is_in_list(init_list, el)
  # 分解(子问题规模为 n/2)
  left_list, right_list = init_list[:n//2], init_list[n//2:]
  # 递归(树),分治,合并
  res = solve(left_list, el) or solve(right_list, el)
  return res
if __name__ == "__main__":
  # 测试数据
  test_list = [12,2,23,45,67,3,2,4,45,63,24,23]
  # 查找
  print(solve2(test_list, 45)) # True
  print(solve2(test_list, 5)) # False

题目3. 找出一组序列中的第 k 小的元素,要求线性时间

# 划分(基于主元 pivot),注意:非就地划分
def partition(seq):
  pi = seq[0]              # 挑选主元
  lo = [x for x in seq[1:] if x <= pi] # 所有小的元素
  hi = [x for x in seq[1:] if x > pi]  # 所有大的元素
  return lo, pi, hi
# 查找第 k 小的元素
def select(seq, k):
  # 分解
  lo, pi, hi = partition(seq)
  m = len(lo)
  if m == k:
    return pi        # 解决!
  elif m < k:
    return select(hi, k-m-1) # 递归(树),分治
  else:
    return select(lo, k)   # 递归(树),分治
if __name__ == '__main__':
  seq = [3, 4, 1, 6, 3, 7, 9, 13, 93, 0, 100, 1, 2, 2, 3, 3, 2]
  print(select(seq, 3)) #2
  print(select(seq, 5)) #2

题目4. 快速排序

# 划分(基于主元 pivot),注意:非就地划分
def partition(seq):
  pi = seq[0]              # 挑选主元
  lo = [x for x in seq[1:] if x <= pi] # 所有小的元素
  hi = [x for x in seq[1:] if x > pi]  # 所有大的元素
  return lo, pi, hi
# 快速排序
def quicksort(seq):
  # 若问题规模小于等于1,解决
  if len(seq) <= 1: return seq
  # 分解
  lo, pi, hi = partition(seq)
  # 递归(树),分治,合并
  return quicksort(lo) + [pi] + quicksort(hi)
seq = [7, 5, 0, 6, 3, 4, 1, 9, 8, 2]
print(quicksort(seq)) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

题目5. 合并排序(二分排序)

# 合并排序
def mergesort(seq):
  # 分解(基于中点)
  mid = len(seq) // 2
  left_seq, right_seq = seq[:mid], seq[mid:]
  # 递归(树),分治
  if len(left_seq) > 1: left_seq = mergesort(left_seq)
  if len(right_seq) > 1: right_seq = mergesort(right_seq)
  # 合并
  res = []
  while left_seq and right_seq:     # 只要两者皆非空
    if left_seq[-1] >= right_seq[-1]: # 两者尾部较大者,弹出
      res.append(left_seq.pop())
    else:
      res.append(right_seq.pop())
  res.reverse()             # 倒序
  return (left_seq or right_seq) + res  # 前面加上剩下的非空的seq
seq = [7, 5, 0, 6, 3, 4, 1, 9, 8, 2]
print(mergesort(seq)) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

题目6. 汉诺塔

# 汉诺塔
def move(n, a, buffer, c):
  if n == 1:
    print(a,"->",c)
    #return
  else:
    # 递归(线性)
    move(n-1, a, c, buffer)
    move(1, a, buffer, c) # 或者:print(a,"->",c)
    move(n-1, buffer, a, c)
move(3, "a", "b", "c")

问题7. 爬楼梯

假设你正在爬楼梯,需要n步你才能到达顶部。但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部?

# 爬楼梯
def climb(n=7):
  if n <= 2:
    return n
  return climb(n-1) + climb(n-2) # 等价于斐波那契数列!
print(climb(5)) # 8
print(climb(7)) # 21

问题8. 给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。(最近点对问题)

from math import sqrt
# 蛮力法
def solve(points):
  n = len(points)
  min_d = float("inf") # 最小距离:无穷大
  min_ps = None    # 最近点对
  for i in range(n-1):
    for j in range(i+1, n):
      d = sqrt((points[i][0] - points[j][0])**2 + (points[i][1] - points[j][1])**2) # 两点距离
      if d < min_d:
        min_d = d            # 修改最小距离
        min_ps = [points[i], points[j]] # 保存最近点对
  return min_ps
# 最接近点对(报错!)
def nearest_dot(seq):
  # 注意:seq事先已对x坐标排序
  n = len(seq)
  if n <= 2: return seq # 若问题规模等于 2,直接解决
  # 分解(子问题规模n/2)
  left, right = seq[0:n//2], seq[n//2:]
  print(left, right)
  mid_x = (left[-1][0] + right[0][0])/2.0
  # 递归,分治
  lmin = (left, nearest_dot(left))[len(left) > 2]  # 左侧最近点对
  rmin = (right, nearest_dot(right))[len(right) > 2] # 右侧最近点对
  # 合并
  dis_l = (float("inf"), get_distance(lmin))[len(lmin) > 1]
  dis_r = (float("inf"), get_distance(rmin))[len(rmin) > 1]
  d = min(dis_l, dis_r)  # 最近点对距离
  # 处理中线附近的带状区域(近似蛮力)
  left = list(filter(lambda p:mid_x - p[0] <= d, left))  #中间线左侧的距离<=d的点
  right = list(filter(lambda p:p[0] - mid_x <= d, right)) #中间线右侧的距离<=d的点
  mid_min = []
  for p in left:
    for q in right:
      if abs(p[0]-q[0])<=d and abs(p[1]-q[1]) <= d:   #如果右侧部分点在p点的(d,2d)之间
        td = get_distance((p,q))
        if td <= d:
          mid_min = [p,q]  # 记录p,q点对
          d = td      # 修改最小距离
  if mid_min:
    return mid_min
  elif dis_l>dis_r:
    return rmin
  else:
    return lmin
# 两点距离
def get_distance(min):
  return sqrt((min[0][0]-min[1][0])**2 + (min[0][1]-min[1][1])**2)
def divide_conquer(seq):
  seq.sort(key=lambda x:x[0])
  res = nearest_dot(seq)
  return res
# 测试
seq=[(0,1),(3,2),(4,3),(5,1),(1,2),(2,1),(6,2),(7,2),(8,3),(4,5),(9,0),(6,4)]
print(solve(seq)) # [(6, 2), (7, 2)]
#print(divide_conquer(seq)) # [(6, 2), (7, 2)]

问题9. 从数组 seq 中找出和为 s 的数值组合,有多少种可能

'''
求一个算法:N个数,用其中M个任意组合相加等于一个已知数X。得出这M个数是哪些数。
比如:
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
s = 14 # 和
全部可能的数字组合有:
5+9, 6+8
1+4+9, 1+5+8, 1+6+7, 2+3+9, 2+4+8, 2+5+7, 3+4+7, 3+5+6
1+2+5+6, 1+3+4+6, 1+2+4+7, 1+2+3+8, 2+3+4+5
共计15种
'''
# 版本一(纯计数)
def find(seq, s):
  n = len(seq)
  if n==1:
    return [0, 1][seq[0]==s]
  if seq[0]==s:
    return 1 + find(seq[1:], s)
  else:
    return find(seq[1:], s-seq[0]) + find(seq[1:], s)
# 测试
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
s = 14 # 和
print(find(seq, s)) # 15
seq = [11,23,6,31,8,9,15,20,24,14]
s = 40 # 和
print(find(seq, s)) #8
# 版本二 (打印)
def find2(seq, s, tmp=''):
  if len(seq)==0:  # 终止条件
    return
  if seq[0] == s:        # 找到一种,则
    print(tmp + str(seq[0])) # 打印
  find2(seq[1:], s, tmp)               # 尾递归 ---不含 seq[0] 的情况
  find2(seq[1:], s-seq[0], str(seq[0]) + '+' + tmp)  # 尾递归 ---含 seq[0] 的情况
# 测试
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
s = 14 # 和
find2(seq, s)
print()
seq = [11,23,6,31,8,9,15,20,24,14]
s = 40 # 和
find2(seq, s)

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python实现爬虫下载漫画示例
Feb 16 Python
python使用正则搜索字符串或文件中的浮点数代码实例
Jul 11 Python
详解Django中的权限和组以及消息
Jul 23 Python
python通过pip更新所有已安装的包实现方法
May 19 Python
python定时利用QQ邮件发送天气预报的实例
Nov 17 Python
python 判断矩阵中每行非零个数的方法
Jan 26 Python
解决Django中多条件查询的问题
Jul 18 Python
Python 获取指定文件夹下的目录和文件的实现
Aug 30 Python
Python中输入和输出(打印)数据实例方法
Oct 13 Python
如何基于Python和Flask编写Prometheus监控
Nov 25 Python
Python存储读取HDF5文件代码解析
Nov 25 Python
python利用opencv实现颜色检测
Feb 23 Python
Python更新数据库脚本两种方法及对比介绍
Jul 27 #Python
Python判断文件或文件夹是否存在的三种方法
Jul 27 #Python
Python开发SQLite3数据库相关操作详解【连接,查询,插入,更新,删除,关闭等】
Jul 27 #Python
Python基于tkinter模块实现的改名小工具示例
Jul 27 #Python
python django 增删改查操作 数据库Mysql
Jul 27 #Python
Python中Selenium模拟JQuery滑动解锁实例
Jul 26 #Python
Python列表和元组的定义与使用操作示例
Jul 26 #Python
You might like
全国FM电台频率大全 - 18 湖南省
2020/03/11 无线电
Laravel框架用户登陆身份验证实现方法详解
2017/09/14 PHP
PHP使用zlib扩展实现GZIP压缩输出的方法详解
2018/04/09 PHP
用jquery实现学校的校历(asp.net+jquery ui 1.72)
2010/01/01 Javascript
IE8 chrome中table隔行换色解决办法
2010/07/09 Javascript
关于javascript中this关键字(翻译+自我理解)
2010/10/20 Javascript
在JavaScript中获取请求的URL参数[正则]
2010/12/25 Javascript
jQuery Ajax请求状态管理器打包
2012/05/03 Javascript
jQuery简单实现中间浮窗效果
2016/09/04 Javascript
JavaScript获取URL中参数querystring的方法详解
2016/10/11 Javascript
javascript淘宝主图放大镜功能
2016/10/20 Javascript
vue 怎么创建组件及组件使用方法
2017/07/27 Javascript
深入理解Promise.all
2018/08/08 Javascript
详谈js的变量提升以及使用方法
2018/10/06 Javascript
浅析Proxy可以优化vue的数据监听机制问题及实现思路
2018/11/29 Javascript
layui实现下拉复选功能的例子(包括数据的回显与上传)
2019/09/24 Javascript
判断JavaScript中的两个变量是否相等的操作符
2019/12/21 Javascript
js this 绑定机制深入详解
2020/04/30 Javascript
vue3为什么要用proxy替代defineProperty
2020/10/19 Javascript
uniapp实现可滑动选项卡
2020/10/21 Javascript
vue 全局封装loading加载教程(全局监听)
2020/11/05 Javascript
[07:48]DOTA2上海特级锦标赛主赛事首日RECAP
2016/03/04 DOTA
Python常用随机数与随机字符串方法实例
2015/04/09 Python
python结合API实现即时天气信息
2016/01/19 Python
Python字符编码判断方法分析
2016/07/01 Python
python实现字符串连接的三种方法及其效率、适用场景详解
2017/01/13 Python
Python读取csv文件分隔符设置方法
2019/01/14 Python
Selenium结合BeautifulSoup4编写简单的python爬虫
2020/11/06 Python
Keds官方网站:购买帆布运动鞋和经典皮鞋
2016/11/12 全球购物
如何用PHP实现邮件发送
2012/12/26 面试题
2014年教师德育工作总结
2014/11/10 职场文书
2015年大学生党员承诺书
2015/04/27 职场文书
指导教师推荐意见
2015/06/05 职场文书
温馨祝福晨语:美丽的一天从我的问候开始
2019/11/28 职场文书
vue/cli 配置动态代理无需重启服务的方法
2022/05/20 Vue.js
PHP 时间处理类Carbon
2022/05/20 PHP