Python学习笔记-算法设计与实现

Python学习笔记-算法设计与实现

查找问题

查找是指在一个集合里找到特定值的过程。

  1. 线性查找(在列表里一个一个查找)

算法:

#search_liinear.py  线性查找
def search_linear(x,nums):#输入需查找的数和列表
    for i in range(len(nums)):#列表中的所有数据都查找一遍
        if nums[i]==x:#找到目标值
            return i#返回 值的索引
        return -1#未找到,返回-1

测试代码:

from time import *#引入库time-使用函数clock
from random import *#引入库random-使用函数random和randint

#测试线性查找的运行时间
N=1024*1024#测试集规模
T=100#循环次数
nums=[range(N)]#建立一个规模为N的列表
total_time=0.0#设初始时间为0.0
for i in range(T):#测试100次
    start_time=clock()#测试开始时间
    search_linear(randint(0,N),nums)#查找1-N的随机数
    end_time=clock()#测试结束时间
    total_time=total_time+end_time-start_time
    #测试结束时间-测试开始时间=本次测试经过的时间
avg_time=total_time/T#平均测试时间
print "Searching time in linear search is %f"%avg_time
#输出结果
  1. 二分查找(每次猜可能取值范围中间的数字)

算法:

#search_binary.py  二分查找

def search_binary(x,nums):#输入需查找的数和被查找的列表
    low=0
    high=len(nums)-1#将low设为最低的数,high为最高的数
    while low<=high:#一直循环到没有查找空间(low>high)
        mid=(low+high)//2#中间项的位置(用//得到结果必为整数)
        item=nums[mid]#取出中间项的具体数值
        if x==item:#找到目标值
            return mid #跳出循环,返回索引位置
        elif x<item:#目标值可能在低半部分
            high=mid-1#将搜索范围缩小到低半部分
        else:#目标值可能在高半部分
            low=mid+1#将搜索范围缩小到高半部分
    return -1#未找到目标值,返回-1

from time import *
from random import *

#测试二分查找的运行时间
N=1024*1024#测试集规模
T=100#循环次数
nums=[range(N)]#建立一个规模为N的列表
total_time=0.0#设初始时间为0.0
for i in range(T):#测试100次
    start_time=clock()#测试开始时间
    search_binary(randint(0,N),nums)#查找1-N的随机数
    end_time=clock()#测试结束时间
    total_time=total_time+end_time-start_time
    #测试结束时间-测试开始时间=本次测试经过的时间
avg_time=total_time/T#平均测试时间
print "Searching time in binary search is %f"%avg_time
#输出结果

通过抽象算法的速学特性可知:

  • 线性查找算法的复杂度为n
  • 二分查找算法的复杂度为log2n

算法复杂度即为解决问题所需时间正比于问题规模的对数
虽然二分查找算法效率更高,但他需要列表是有序的列表,缺乏普适性,故Python内部的查找函数采用的是线性查找算法

排序问题

排序指对一个列表中的元素按照升序(或降序)进行重新排列的过程

  1. 选择排序:

每次循环过程都选择剩下的最小元素并将其放到合适的位置

算法:

#selSorrt.py  选择排序
def selSort(nums):#输入乱序的列表
    n=len(nums)
    for bottom in range(n-1):#每选择一个数,循环一次
        mi=bottom#将第一个数设为最低值
        for i in range(bottom+1,n):#找出剩下数据里的最低值
            if nums[i]<nums[mi]:
                mi=i
       nums[bottom],nums[mi]=nums[mi],nums[bottom]#将选出的最低值放在最前面的位置
    return nums#返回排好序的列表

测试代码:

#test_selSort.py   #测试排序函数的代码
from random import *#导入random库,使用相关函数
numbers=range(10)#生成一个长度为10的列表
shuffle(numbers)#将列表的顺序随机打乱     
print numbers#输出乱序的列表
print selSort(numbers)#输出排序后的列表
  1. 归并排序

排序两叠更小的卡片及将两叠排序后的卡片合并-分而治之
步骤为:

  1. 将列表nums分成两个子列表
  2. 排序第1个子列表
  3. 排序第2个子列表
  4. 将两个排序后对的子列表进行合并

归并算法:

#merge.py 归并算法
def merge(list1,list2):#输入两个排好序的列表
    i1,i2=0,0
    n1,n2=len(list1),len(list2)
    list3=[]#数据初始化
    while i1<n1 and i2<n2:#循环一直进行到两个列表(或有一个)没有数据为止
        if list1[i1]<list2[i2]:#若列表1的数据较小
            list3.append(list1[i1])#将列表1的数据存入list3
            i1+=1
        else:#若列表2的数据较小
            list3.append(list2[i2])#将列表2的数据存入list3
            i2+=1

    #将列表1中剩余的数据(如当列表2中数据存完后还有剩余有)增加到新列表中
    while i1<n1:
        list3.append(list1[i1])
        i1+=1

    #将列表2中剩余的数据(如当列表1中数据存完后还有剩余有)增加到新列表中
    while i2<n2:
        list3.append(list2[i1])
        i2+=1
    return list3

递归分解列表-将每个长度为1的列表进行排序:

#mergeSort.py  归并排序算法
def mergeSort(nums):#输入一个需要排序的表
    n=len(nums)#列表中数据的个数
    if n>1:#若列表长度不为1,不断分解列表
        m=n//2
        nums1,nums2=nums[:m],nums[m:]#将列表平均分为两个部分
        nums1=mergeSort(nums1)
        nums2=mergeSort(nums2)#不断递归,直至列表的长度为1
        nums=merge(nums1,nums2)#通过将长度为1的列表不断归并,也就是乱序列表排序的过程
    return nums

测试代码:

#test
from random import *#导入random库,使用相关函数
numbers=range(10)#生成一个长度为10的列表
shuffle(numbers)#将列表的顺序随机打乱     
print numbers#输出乱序的列表
print mergeSort(numbers)#输出排序后的列表

选择排序为n^2算法
归并排序为nlog2n算法
如果输入规模小,选择排序可能会更快(代码更简单且开销更小)
随着输入规模的扩大,nlog2n增长很缓慢(nlog2n16000000=24),而
n^2的增长极快,所需的运行时间将变得不可容忍。

汉诺塔问题

相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
在这里插入图片描述
解决步骤:

  1. 圆盘1到n-1组成的他从A移动到B
  2. 圆盘n移动到C
  3. 圆盘1到n-1组成的塔从B移动到C

解决代码如下:

#hanoi.py  汉诺塔问题
counts=[]
def moveTower(n,source,dest,temp):
#输入须以移动的盘子数以及本次移动的原来的杆、目的地、中转站
    if n==1:#如果只移动一个盘子时,移动并输出移动方向(基例)
        counts.append(str(source+"to"+dest))#将每一步移动的方向存入列表counts
    else:
        moveTower(n-1,source,temp,dest)#将n-1个盘子从A移动到B(中转)
        moveTower(1,source,dest,temp)#将最大的盘子从A直接移动到C
        moveTower(n-1,temp,dest,source)#将n-1个排好的盘子从B移动到C
    return counts
def main():
    n=eval(raw_input("The scale of Hanover problem is:"))
    #输入需要解决的汉诺塔问题的规模
    count=[]#创建空列表
    count=moveTower(n,"A","B","C")#调用相应规模的函数
    print "The number of steps required to solve the",n," scale Hanover problem is",len(count),"."
    #输出此规模汉诺塔问题所需要的步数
main()

代码复杂度为2^n-1
即64个盘子移动完需要2^64-1次需要超过5800亿年

科赫曲线

LS文法构图算法是仿照语言学中语法生成方法来构造图形的一种算法。任何一种语言都由一个字母集和一些构成有意义语句的文法规则组成的。给定一个或几个初始字母和一组”生成规则“,将生成规则反复作用到初始字母和新生成的字母上,可以生产出整个语言。
LS文法用字母表和符号串来表达生成对象的初始形式,然后根据一组产生式重写规则,将初始形式的每个字段依次替换为新的字符形式,以此过程反复替换重写,最后生成图形。
代码如下:

#kochLS.py   科赫曲线(LS文法)
from Tkinter import *#导入Tkinter图形库
from math import *#导入math库

#建立Koch类,将建立科赫曲线的代码封装起来
class Koch(Frame):
    #建立一个Frame(窗口控件)的派生类Koch类,通过Frame向里面添加控件
    def __init__(self,master=None):
    #构造函数,master=None--这个派生类所属的控件没有上层控件,是顶层的

        #设置Koch类的内置属性
        self.statement="F++F++F"#创建statement属性,包含上述字符
        self.replacement="F-F++F-F"#repacement属性
        self.direction=0#初始角度
        self.winkle=pi/3#每次转角的角度--60度
        self.endX=100
        self.endY=150#结束点/起始点位置
        self.level=eval(raw_input("Please enter the the number of iterations required:"))
        #人机互动,输入迭代次数level
        self.step=eval(raw_input("Please enter the step(486/(3^self.level):"))
        #根据迭代次数设置每步的长度
        #内置调用函数
        self.calcuStatement()#调用计算函数,并通过self传入上述已定义好一部分属性
        #调用父类Frame的__init__函数初始化Koch类的Frame类部分
        #即初始化Frame(窗口)对象,并传入self(自身对象),master(管理者)参数
        Frame.__init__(self,master)
        self.grid()#显示窗口并使用grid布局,grid方法是从Frame继承来的
        #布局--控件的排列方式
        self.createWidgets()#调用创建控件的函数,并通过self传入上述已定义好一部分属性
    def calcuStatement(self):
    #计算函数,计算出迭代level次后需要进行的画图步骤
        for currentLevel in range(0,self.level):#循环level次
            newStatement=""#创建一个空字符串
            for index in range(0,len(self.statement)):#就最初的画图步骤循环

                if self.statement[index]=="F":#若index是F(直线),则舍弃原来的直线,用replacement替代
                    newStatement=newStatement+self.replacement
                else:#若index不是直线,则不变仍然转角
                    newStatement+=self.statement[index]
            self.statement=newStatement#将计算好的赋给statement,再次替换(迭代)
            print "The drawing steps after iteration are",self.statement

    def createWidgets(self):
    #创建控件函数,创建窗口
        self.draw=Canvas(self,width=800,height=600)
        #通过父类Frame的Canvas建立一个属性--宽为800,高为600的画布,(通过self传入标题)
        self.draw.pack(side=LEFT)#此画布的布局方式为LEFT
        self.drawCanvas()#调用画图函数,画出计算函数迭代出的画图步骤
    def drawCanvas(self):
    #画图函数,在画布上画出需要的图形
        for index in self.statement:#传入计算函数算好的画图步骤
            if index =="F":#若index是F(直线),沿着目前转好的方向划线
                #先设置好划线的起止点
                startX=self.endX
                startY=self.endY#起点的XY坐标
                self.endX=startX+self.step*(cos(self.direction))
                self.endY=startY+self.step*(sin(self.direction))
                #通过简单的数学计算找出终止点的位置
            elif index=="-":#若index为-,则逆时针旋转60度
                self.direction-=self.winkle
            elif index=="+":#若index为+,则顺时针旋转60度
                self.direction+=self.winkle
app=Koch()#为Koch类创建一个对象app
app.master.title("Koch(LS)")#通过master的内置函数title设置窗口标题为'First Tkinter'
app.mainloop()#进入主循环
  

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值