目录

  • 1 前言
    • 1.1  Python解释器安装
    • 1.2  PyCharm开发环境
  • 2 Python程序设计实验
    • 2.1  实验1 基本输入输出
    • 2.2  实验2 基本运算
    • 2.3  实验3 字符串处理
    • 2.4  实验4 列表与元组应用
    • 2.5  实验5 列表与元组应用
    • 2.6  实验6 字典与集合应用
    • 2.7  实验7 函数应用1
    • 2.8  实验8 函数应用2
    • 2.9  实验9 类的创建与应用
    • 2.10  实验10 模块应用
    • 2.11  实验11 文件操作
    • 2.12  实验12 异常处理
    • 2.13  实验13 使用正则表达式
    • 2.14  实验14 tkinter基础应用
  • 3 学习参考
    • 3.1  1 Python及其IDLE环境
    • 3.2  2 Python代码规范
    • 3.3  3 使用变量
    • 3.4  4 输入输出
    • 3.5  5 运算符
    • 3.6  6 字符串
    • 3.7  7 条件语句
    • 3.8  8 循环语句
    • 3.9  9 列表
    • 3.10  10 元组
    • 3.11  11 字典
    • 3.12  12 集合
    • 3.13  13 自定义函数
    • 3.14  14 内置函数
    • 3.15  15 类的创建和使用
    • 3.16  16 类的继承与多态
    • 3.17  17 time模块
    • 3.18  18 os模块
    • 3.19  19 random模块
    • 3.20  20 文件
    • 3.21  21 错误与异常处理
 实验8 函数应用2

任务1:求解汉诺塔问题

    下面代码用于求解汉诺塔问题:

def hanoi(n,a,b,c):

    if n==1:

       print(a,'->',c)

    else:

        hanoi(n-1,a,c,b)   #将a上面n-1个盘子经c移到b

        print(a,'->',c)      #将a最后1个盘子移到c

        hanoi(n-1,b,a,c)  #将b上n-1个盘子经a移到c

n=int(input("输入盘子数量:"))

hanoi(n,'A','B','C')

1、调试、理解代码,程序保存到s8A.py

2、编写程序s8B.py,根据盘子数量输出需移动的步数。

输入输出格式:

输入盘子数量:3

输出:需移动步数=7

"""

(1)递归调用(教材P72)

    函数在执行的过程中直接或间接调用自己本身,称为递归调用。其特点是:①直接或间接调用自身;②有明确的边界条件。

例如,计算n的阶乘。其数学模型为:

   fac(n)=1             (n=1)

   fac(n)=n*fac(n-1)   (n>1)

#程序代码:

def fac(n): 

  if n == 1:      #②边界条件

    return 1 

  else:

    return n * fac(n-1)  #①调用fac函数本身

#主程序

print(fac(5))

(2)变量作用域(教材P70)

    变量的作用域是指变量的作用范围。

① 局部变量

    局部变量是在函数内定义的变量,只在定义它的函数内生效。它们与函数外具有相同名称的其他变量,没有任何关系。

    所有局部变量的作用域,是它们被定义的块,从它们的名称被定义处开始。函数结束时,其局部变量被自动删除。

def fun( ):

    count = 10    #局部变量

    print(count)  #函数内访问

fun( )

print(count)    #NameError: name 'count' is not defined

② 全局变量:

    全局变量是在函数外定义的变量,它在程序中任何位置都可以被访问。

count =10            # 全局变量

def fun( ):

    print(count)     # 函数内访问全局变量

fun( )

print(count)         # 函数外访问全局变量

    函数中只能访问全局变量,但不能修改全局变量。因为在函数内部给变量赋值,默认为定义新的变量。局部变量和全局变量同名时,优先使用局部变量。

    若要在函数内部修改全局变量的值,需先在函数内使用关键字“global”进行声明。

count =10           # 全局变量

def fun( ):

    global count    #函数内声明全局变量

    count+=1        #函数内修改全局变量

fun( )

print(count)        # 函数外访问全局变量

输出结果:   11

(3)汉诺塔移动步数

    汉诺塔的问题,从小学、中学,一直到大学,老师都在讲。并且有不同的版本。一般汉诺塔问题,都是ABC三根柱子,小盘上,大盘在下

方法①:

    小学老师:1个盘子,移1步;2个盘子移3步,3个盘子移7步。n个盘子,共移动多少步??

    学生A:移动步数,等于2的n次方,再减1。

方法②:

    中学老师:将n个盘子,从A柱移动到C柱。可简化为:

    (a) n-1个盘子从A柱移动到B柱,借助C柱;

    (b) 第n个盘子,从A柱移动到C柱;

    (c) n-1个盘子从B柱移动到C柱,借助A柱;

假设,步骤(a)移动的步数为f(n-1),将n个盘子,从A柱移动到C柱,需要多少步??

    学生A:2*f(n-1)+1。因为步骤(a)和步骤(c)移动的步数,是一样的!

    学生B:那不和前面求阶乘的数学模型,是一样的!

        fun(n)=1              (n=1)

        fun(n)=2*fun(n-1)+1   (n>1)

方法③:

    大学老师:刚才s8A.py的练习,程序的功能是什么?

    学生A:输出汉诺塔问题盘子移动的动态过程。

    大学老师:n个盘子,从A柱移动到C柱,需要移动多少步?

    学生B:函数调用了多少次,就移动了多少步!

    大学老师:怎么知道函数调用了多少次?

    学生A:在函数定义中,加入变量count计数,在主程序中输出count就可以了。

    学生B:如果只想输出移动步骤,不想输出那些动态过程。怎么办?

    大学老师:将print语句屏蔽,或者用pass语句置换就可以了。


任务2:斐波那契数列

    斐波那契数列的第1项和第2项均为1,从第3项起,每项为前2项的和。

    编写程序s8C.py,要求:

1、编写函数fib(n),返回斐波那契数列的第n项

2、程序运行效果:

输入项数:120

输出:第120个斐波那契数=5358359254990966640871840

"""

递归方法与非递归方法比较:

    以教材P72为例,计算500阶乘。

import time

def fac(n):    #递归方法

    if n == 1:

        return 1

    else:

        return n * fac(n - 1)

def fun(n):    #非递归方法

    result = 1

    for i in range(1, n + 1):

        result *= i

    return result

#递归方法优点: 代码少,逻辑清晰,可读性强,易于维护。   

#主程序(以下代码,参考教材P98):

before = time.perf_counter( )  

x = fac(500)        #调用函数fac( ),递归方法

after = time.perf_counter( )

interval = after - before

print(f" 递归方法,运行时间为{interval:.6f}秒")


before = time.perf_counter( )

x = fun(500)        #调用函数fun( ),非递归方法

after = time.perf_counter( )

interval = after - before

print(f"非递归方法,运行时间为{interval:.6f}秒")

#程序运行结果:

  递归方法,运行时间为0.000711秒

非递归方法,运行时间为0.000168秒

"""

#递归方法缺点:耗费时间更多,递归深度越大越明显。因为递归需要不断地回溯与递推。

注意,不能重复的无限制的对自身进行调用,这会引发异常。Python中默认递归次数为1000次。调用fac(1000)会出错,而调用fun(1000)不会出错。


任务3:质数搜索

    编写程序s8D.py,要求:

1、定义函数isp(n),用于判断n是否为质数。

2、输出[m,n]上质数的个数。m,n从键盘输入。

3、程序运行效果:

输入m,n的值:3,100

输出:[3,100]上共有24个质数

"""

函数方法与非函数方法比较 

      参考实验四任务4,s4D.py。输出【m,n】范围内质数个数。

#以下为非函数方法

s = input("输入m,n的值:")

s = s.split(",")

m, n = map(int, s)

count = 0

for k in range(m, n + 1):    # 遍历【m,n】

    flag = True     #变量flag=True,假设k是质数

    for j in range(2, k):      # 遍历【2,k-1】

        if k % j == 0:

            flag = False

            break

    if flag == True:

        count += 1

print(f"[{m},{n}]上共有{count}个质数")


#以下为函数方法

def isp(n):    #函数定义(要考虑形参n为1或0,甚至负数的情况)

    ....

#主程序

s = input("输入m,n的值:")

s = s.split(",")

m, n = map(int, s)

count = 0

for k in range(m, n + 1):  

    if isp(k) == True:    #调用函数isp(k),返回值为True,则k为质数

        count += 1

print(f"[{m},{n}]上共有{count}个质数")

"""

   ① 函数主要作用是将复杂问题简单化。方法2和方法1相比,主程序更加简单,程序结构更清晰。前提是,已经有人替你把isp( )函数编好了(这里可没人替你编写)。

   ② 函数的特点是模块化。如果isp( )函数已经有人替你编好。你不需要关心函数具体是如何编写的。但你得知道怎么用,也就是函数的入口和出口!怎么调用,函数返回值是什么。

   ③ isp( )函数返回值,可以设为“True/Flase”,可以设为“1/0”,也可以设为“Y/N”,还可以是其他值!!主程序 if 调用语句,作相应修改就可以了。

   上面的“if isp(k) == True:”语句, 修改为 “if  isp(k):”,可不可以??可以!前面实验4的s4D.py中已经讲了,“if  isp(k) == True”和“if  isp(k)”,都可用来作为条件判断。老师这里明确规定,不要用后者。


附加练习:

编写一个函数,求满足以下条件的最大的n值:

    1^2+2^2+3^2+4^2+…+n^2<1000  (这里n^2,表示n的2次方

输出结果:

   13