python实现递归和分治

一、开发环境

开发工具:jupyter notebook 并使用vscode,cmd命令行工具协助编程测试算法,并使用codeblocks辅助编写C++程序

编程语言:python3.6

二、实验目标

1. 熟悉递归和分治算法实现的基本方法和步骤;

2. 学会分治算法的实现方法和分析方法:

三、实验内容

问题1,线性时间选择问题:

1) 在 4 59 7 23 61 55 46中找出最大值,第二大值,和第四大的值(要求不允许采用排序算法),并与第一章实现的快速排序算法进行比较。

2) 随机生成10000个数,要求找出其中第4999小的数,并与第一章实现的快速排序算法进行比较。

将4 59 7 23 61 55 46作为列表输入函数中,并且返回输出的结果

方法:直接遍历找到最大值,然后删除,再遍历,依次找到第2,3,4大的数据并且返回

每次都需要遍历一遍列表中所有的元素

快速排序的方法:将列表使用快速排序进行从大到小的排列,复杂度O(nlogn)然后直接返回对应的数,复杂度O(1),快速排序代码来自上一节。

代码如下所示:

# 直接遍历找到最大值,然后删除,再遍历,依次找到第2,3,4大的数据并且返回

def find_1_2_4(lis):

find1=lis[0];find2=lis[0];find3=lis[0];

for i in range(len(lis)):

if find1

find1 = lis[i]

lis.remove(find1)

for i in range(len(lis)):

if find2

find2=lis[i]

lis.remove(find2)

for i in range(len(lis)):

if find3

find3=lis[i]

lis.remove(find3)

find3=lis[0]

for i in range(len(lis)):

if find3

find3=lis[i]

lis.remove(find3)

return find1, find2, find3

# 快速排序,划分操作, 快速排序的辅助函数

def split(lis, first, last):

pivot = lis[first]

left = first

right = last

while left

while pivot < lis[right]:

right=right-1

while left < right and (lis[left] < pivot or lis[left] == pivot):

left=left+1

if left < right:

lis[left], lis[right] = lis[right], lis[left]

# 确定好基准位置

pos = right

lis[first] = lis[pos]

lis[pos] = pivot

return pos

# 快速排序实现

def quicksort(lis, first, last):

if first < last:

pos = split(lis, first, last)

quicksort(lis, first, pos-1)

quicksort(lis, pos+1, last)

return lis

if __name__ == '__main__':

lis1 = [4, 59, 7, 23, 61, 55, 46]

lis2 = [4, 59, 7, 23, 61, 55, 46]

import time

starttime = time.time()

print(find_1_2_4(lis1))

endtime = time.time()

print("执行时间为:",endtime-starttime)

starttime = time.time()

quicksort(lis2, 0,len(lis2)-1)

print(lis2[-1], lis2[-2], lis2[-4])

endtime = time.time()

print("执行时间为:",endtime-starttime)

实验结果如下图所示:

可见在数据量如此之小的情况下,时间相差无几,并不能反映出效率问题。

2)首先生成10000个随机数据,找出第4999小的数,如果直接使用排序算法,那么时间复杂度就是O(nlogn),然后直接输出第4999个数就行了。

但是我们可以使用分治策略对算法进行优化,使得算法的复杂度小于排序算法的O(nlogn)

算法步骤如下:

首先选取一个基点,比支点大的放支点右边,比支点小的放支点左边(到这里和快排一样),看看支点左边有多少个数,如果大于k-1说明在k在左边,左边递归,如果小于k-1说明在k右边,右边开始递归,并且新寻找的是k-pos小的数,如果相等,那么返回

代码如下:

# 寻找第k小的数的辅助函数

def k_find(lis, k):

pivot = lis[len(lis)//2]

left = 0

right = len(lis)-1

lis[0],lis[len(lis)//2]=lis[len(lis)//2],lis[0]

while left

while pivot < lis[right]:

right=right-1

while left < right and (lis[left] < pivot or lis[left] == pivot):

left=left+1

if left < right:

lis[left], lis[right] = lis[right], lis[left]

# 确定好基准位置

pos = right

lis[0] = lis[pos]

lis[pos] = pivot

count=pos+1

if count==k:

return pivot

elif count>k:

return k_find(lis[0:pos], k)

else:

return k_find(lis[pos:], k-pos)

if __name__ == "__main__":

import random

import cProfile

# 产生100000个随机数组

num = 100000

# array = [random.randint(1, 1000) for i in range(num)]

array=[]

a=1

for i in range(num):

a = a+random.randint(1,20)+1

array.append(a)

# 乱序操作

random.shuffle(array)

random.shuffle(array)

import copy

# 进行一个深度拷贝,用于测试

arraycopy = copy.deepcopy(array)

# 用O(n)的算法得到第k小的数

k=4999

import time

starttime= time.time()

n = k_find(array, k)

endtime=time.time()

print("使用线性查找的时间为:",endtime-starttime)

print("查找得到的数为:",n)

starttime= time.time()

quicksort(arraycopy, 0, len(arraycopy)-1)

endtime=time.time()

print("使用快排查找的时间为:",endtime-starttime)

print("查找得到的数为:",arraycopy[k-1])

运行结果如下图所示:

查找到了相同的数字,然后发现效率竟然相差了十倍。

在这一版本的代码中,我们只要将代码中的

while pivot < lis[right]:

right=right-1

while left < right and (lis[left] < pivot or lis[left] == pivot):

left=left+1

这一部分的大于号与小于号略作修改,即可用于解决第k大的数这一问题,即:

while pivot > lis[right]:

right=right-1

while left < right and (lis[left] > pivot or lis[left] == pivot):

left=left+1

将修改后的代码用于求解leetcode上第215号题,成功通过

我实现的版本还是存在很多问题的,比如基准数的选择上,而且在出现相同数字的时候可能会出现问题,参考教科书上的解决方案还是很好的,取中间数的中间数,保证了效率,所以我照着书上敲了一遍代码(下面这个方案纯粹是照着敲上去的)

# 线性时间选择

lis1 = [4, 59, 7, 23, 61, 55, 46]

# 选择第k小的数的分治算法

def select_fct(array, k):

if len(array) <= 10: # 边界条件

array.sort()

return array[k]

pivot = get_pivot(array) # 得到数组的支点数

array_lt, array_gt, array_eq = patition_array(array, pivot) # 按照支点数划分数组

if k

return select_fct(array_lt, k)

elif k < len(array_lt)+len(array_eq): # 所求数为支点数

return array_eq[0]

else: # 所求数在支点数右边

normalized_k = k-(len(array_lt)+len(array_eq))

return select_fct(array_gt, normalized_k)

# 得到数组的支点数

def get_pivot(array):

subset_size = 5 # 每一个数组有5个元素

subsets = []

num_medians = len(array) // subset_size # 用于记录各组元素

if(len(array) % subset_size) > 0:

num_medians +=1 # 不能被5整除,组数加1

for i in range(num_medians): # 划分成若干组,每组5个元素

beg = i*subset_size

end = min(len(array), beg+subset_size)

subset = array[beg:end]

subsets.append(subset)

medians = []

for subset in subsets:

median = select_fct(subset, len(subset)//2) # 计算每一组的中间数

medians.append(median)

pivot = select_fct(medians, len(subset)//2) # 中间数的中间数

return pivot

# 按照支点数划分数组

def patition_array(array, pivot):

array_lt = []

array_gt = []

array_eq = []

for item in array:

if item < pivot:

array_lt.append(item)

elif item > pivot:

array_gt.append(item)

else:

array_eq.append(item)

return array_lt, array_gt, array_eq

import random

if __name__ == "__main__":

import cProfile

# 产生100个随机数组

num = 100000

array = [random.randint(1, 1000) for i in range(num)]

# print(array)

random.shuffle(array)

random.shuffle(array)

# 用O(n)的算法得到第k小的数

k=15000

# kval = select_fct(array, k)

cProfile.run("select_fct(array, k)")

# print(kval)

问题2,大数乘法问题。分别尝试计算9*9, 9999*9999, 9999999999*8888888888的结果

问题背景:

所谓的大数相乘,就是指两个位数比较大的数字进行相乘,一般而言相乘的结果超过了进本类型的表示范围,所以这样的数不能够直接做乘法运算。

其实乘法运算可以拆成两步,第一部,将乘数与被乘数逐位相乘,第二步,将逐位相乘得到的结果对应相加起来。

传统的大数乘法问题就是将数值的计算利用字符串来计算,从而解决位数溢出的问题。这种类型的以前曾经使用C++实现过。

然而我们现在要做的不是这个问题,我们的问题是请设计一个有效的算法,可以进行两个n位大整数的乘法运算,因为是nXn位的问题,解决方案偏向于定制化,我们需要的是将大数乘法O(n²)的复杂度降低,利用分治算法,将大的问题分解成为小的问题,先分,然而再合,从而优化解的方案。

代码实现:

前两个问题很简单,不再实现

9999999999*8888888888

对于这个问题,我们首先直接使用C++进行计算,结果肯定是有问题的

初次实验如图所示:

下面改用python语言 利用分治算法进行大数乘法的计算:

# 大数乘法

9*9

9999*9999

9999999999*8888888888

import math

# 计算符号

def sign(x):

if x<0:

return -1

else:

return 1

# 写一个计算大数乘法的函数 a和b是大数 计算他们俩相乘

def divideConquer(x, y, n):

s = sign(x)*sign(y)

x = abs(x)

y = abs(y)

if x==0 or y==0: # 如果其中有一个为0 直接返回0

return 0

elif n==1: # 递归结束条件 n=1

return x*y*s

else:

A = x // math.pow(10, n//2) # 获得第一个数的前半部分

B = x - A*math.pow(10, n//2) # 获得第一个数的后半部分

C = y // math.pow(10, n//2) # 获得第二个数的前半部分

D = y - C*math.pow(10, n//2) # 获得第二个数的后半部分

AC = divideConquer(A, C, n//2) # AC相乘的结果

BD = divideConquer(B, D, n//2) # BD相乘的结果

ABCD = divideConquer(A-B, D-C, n//2)+AC +BD # 计算中间量AD+BC的结果 实际的计算方式是(a-b)(d-c)+ac+db

return s * (AC*math.pow(10, n)+ABCD*math.pow(10, n//2)+BD)

print(A,B,C,D)

if __name__ =='__main__':

print(int(divideConquer(9999999999,8888888888,10)))

在很快的时间之内就可以完成。

测试结果:

提出问题:是不是因为python的内部机制才使得计算这么快的呢?

于是重新进行了测试

if __name__ =='__main__':

import cProfile

cProfile.run("divideConquer(123456789123456789123456789123456789123456789123456789123456789123456789,123456789123456789123456789123456789123456789123456789123456789123456789,72)")

cProfile.run("123456789123456789123456789123456789123456789123456789123456789123456789*123456789123456789123456789123456789123456789123456789123456789123456789")

这里使用了72位的数字

结果如图所示:

分治法的函数调用次数还是相当的多的

可是为什么直接用py内部的机制也是这么快呢?python到底是如何实现大数相乘的?查阅了相关资料之后发现,大整数乘法还可以采用FFT求循环卷积来实现,复杂度为O(nlogn)(网上这么说的,我也不知道对不对),然而python内部实现大整数相乘还是采用的是分治算法,理由是酱紫的:py日常计算的大整数不够大,所以还是用的是分治。

python递归算法 电影院票价问题_算法课堂实验报告(二)——python递归和分治(第k小的数,大数乘法问题)...相关推荐

  1. 算法导论:快速找出无序数组中第k小的数

    题目描述: 给定一个无序整数数组,返回这个数组中第k小的数. 解析: 最平常的思路是将数组排序,最快的排序是快排,然后返回已排序数组的第k个数,算法时间复杂度为O(nlogn),空间复杂度为O(1). ...

  2. python运动学仿真的意义_运动学仿真实验报告

    运动学仿真实验报告 一.实验目的 1 .了解 solidworks 软件建模和装配过程. 2 .运用 motion 模块进行运动学仿真. 二.实验内容 1 .完成摩托车发动机个部件的建模,掌握草图绘制 ...

  3. python实现循环赛日程表问题的算法_循环赛日程表的分治算法实现实验报告gxl.doc...

    循环赛日程表的分治算法实现实验报告gxl PAGE PAGE 2 深 圳 大 学 实 验 报 告 课程名称: 算法设计与分析 实验项目名称: 分治算法 --矩阵相乘的Strassen算法及时间复杂性分 ...

  4. python实现循环赛日程表问题的算法_循环赛日程表的分治算法实现实验报告_gxl.doc...

    循环赛日程表的分治算法实现实验报告_gxl 深 圳 大 学 实 验 报 告 课程名称: 算法设计与分析 实验项目名称: 分治算法 --矩阵相乘的Strassen算法及时间复杂性分析 或--循环赛日程表 ...

  5. 二分法python上机实验报告_数值分析上机实验报告..doc

    数值分析上机实验报告. 实验报告一 题目: (绪论) 非线性方程求解及误差估计 摘要:非线性方程的解析解通常很难给出,因此线性方程的数值解法就尤为重要.本实验采用两种常见的求解方法二分法.Newton ...

  6. 秦九韶算法matlab实验报告,数值分析上机实验报告.doc

    实验报告一 题目: (绪论) 非线性方程求解及误差估计 摘要:非线性方程的解析解通常很难给出,因此线性方程的数值解法就尤为重要.本实验采用两种常见的求解方法二分法.Newton法和改进的Newton法 ...

  7. c语言八数码A星算法代码解析,八数码问题c语言a星算法详细实验报告含代码解析...

    八数码问题c语言a星算法详细实验报告含代码解析 (13页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 一.实验内容和要求 八数码问题:在3 ...

  8. 银行家算法的实验报告

    银行家算法的实验报告 一.实验内容 银行家算法是避免死锁的一种重要方法,本实验要求编写和调试一个简单的银行家算法程序. 1.设计进程对各类资源最大申请表示及初值的确定. 2.设定系统提供资源的初始状况 ...

  9. 南京邮电大学c语言实验报告4,南京邮电大学算法设计实验报告——动态规划法...

    <南京邮电大学算法设计实验报告--动态规划法>由会员分享,可在线阅读,更多相关<南京邮电大学算法设计实验报告--动态规划法(12页珍藏版)>请在人人文库网上搜索. 1.实 验 ...

最新文章

  1. 让程序员头疼的文档问题怎么破?试试活文档
  2. Python编程基础:第一节 变量Variables
  3. python 编译函数_在Python的Django框架中编写编译函数
  4. RAID技术超详细讲解
  5. python中ndarray对象实例化_Python数据分析之Numpy学习 2——NumPy 基础 ndarray对象
  6. win7系统修复工具_205个电脑系统修复小工具, 联想工程师专用!
  7. c#中Split分割字符串的几种方法
  8. golang fmt.Print(15 == 1_5)
  9. 微服务升级_SpringCloud Alibaba工作笔记0014---Nacos简介和下载_10万微服务实例在线管理
  10. Knowledge Graph - NLP
  11. k8s问题记录与解决
  12. python找不到模块pyodbc_Python:找不到pyodbc导入模块
  13. 【英语】大学英语CET考试,口语部分1(考试介绍与备考,讲义笔记)
  14. 平面设计证书怎么考,平面设计证书有用吗:夏雨老师
  15. ASEMI代理AD9833BRMZ-REEL原装ADI车规级AD9833BRMZ-REEL
  16. 电脑计算机硬盘怎么新建,电脑如何新建一个本地磁盘
  17. 阿里云大数据组件选型
  18. base64 hash256 编码不一致问题
  19. 模板应用到多个主机 zabbix_玩转zabbix之快速入门,超全组件讲解
  20. 恩智浦刘立冬:推动虚拟化的实现,才能进一步实现NFV的部署

热门文章

  1. SCLS:拟南芥二半萜类化合物调控根系微生物组
  2. 机洗内裤容易得暗病?这个锅我们袜子不背!
  3. 学术论文模式图、统计图绘制
  4. C语言hk,C语言再学习
  5. R语言ggplot2可视化散点图、可视化两个数值变量之间的关系、使用geom_smooth函数基于loess方法拟合数据点之间的趋势关系曲线、自定义数据点的大小、色彩、添加主标题、副标题、题注信息
  6. python使用matplotlib可视化线图(line plot)、将可视化图像的图例(legend)放置在图像外部、右侧区域
  7. R语言使用coin包应用于分类变量独立性问题的置换检验(permutation tests)、使用普通卡方检验chisq.test函数和置换近似卡方检验chisq.test函数、检验分类变量的独立性
  8. R语言可视化包ggplot2绘制分组回归线实战(Regression Line by Group)
  9. python秩和检验(Kruskal-Wallis H Test)
  10. 特征选择、特征选择方法