2.3

问题描述

有 \(n\) 个人围成一圈,顺序排号。从第一个人开始报数(从 \(1\) 到 \(3\) 报数),凡报到 \(3\)

思路分析

实际上这是个约瑟夫环问题。但是题目没有要求复杂度,我们简单的实现就好了。

根据关键词退出圈子,不难想到:

  • del
  • x.remove()

于是我们很容易写出这样一个错误的代码

n = int(input('输入数字:')) # 输入数字
arr = list(range(1, n + 1, 1)) # 建立一个列表,存放的是号码数
cnt = 0 # 记录当前报的号是多少
while len(arr) > 1:
    for i in range(len(arr)):
        cnt += 1
        if (cnt % 3 == 0):
            del arr[i]
print(arr[0])

但是这个代码会报错,出现:

IndexError: list assignment index out of range
也就是,索引越界。

为什么会出现这个问题呢?答案是:当你del or remove 一个元素之后,列表的长度会发生变化!!!

举个例子:

arr = [1, 2, 3]
# 这时候我们访问 arr[2]
print(arr[2]) 
# 会输出 3
del arr[1]
# 在删除 arr[1] 之后我们再进行访问 arr[3] 会怎么样呢?
print(arr[3])
# IndexError: list index out of range 会出现越界的错误
# 这是因为,del 之后,list 的长度发生了变化

因此,为了避免这个问题,我们可以设置两个列表 arrbrr 。在每次操作开始前,我们保证 brr = arr

我们寻找 brr 中的每个元素,假如发现他所报号正好 %3 == 0 ,我们就在arr里面删除它。为啥不在 brr 里面删除而在 arr 里面删呢? 因为在 arr 里面删除不会改变 brr 的长度,这样就不会出现,索引越界的情况

代码

"""
Problem:
有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),
凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
Solution:
实际上这个是个约瑟夫环问题,但是我们简单的implementation就好了
"""
n = int(input('输入数字:')) # 输入数字
arr = list(range(1, n + 1, 1)) # 建立一个列表,存放的是号码数
cnt = 0 # 记录当前报的号是多少

# 循环直到列表只剩一个元素
while len(arr) > 1:
    brr = arr[:] # 列表切片,复制列表,为下一步删除做准备
    # brr 中的每个元素进行报数,符合条件的在 arr 中删除
    for i in range(len(brr)):
        cnt += 1 # 进行报号
        if cnt % 3 == 0: # 如果报3,则去除a中的这一位
            arr.remove(brr[i])
print(arr[0])

2.4

问题描述

输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。

思路分析

这题非常简单,我们提取关键词:1.最大值2.最小值3.交换

关于 1.2,我们需要做的是:

  1. 找到最大最小值 mxmn
  2. 找到极值对应的索引 mx_idxmn_idx

关于 3交换,我们运用 Python 里面的元组只是:

  1. a, b = b, a 就可以实现交换

因此整个流程:

  • 通过 max min 函数找到最大最小值
  • 通过 for 循环找到最大最小值对应的索引
  • 交换即可

那么聪明的 yzy 能不能不看代码实现呢!

代码

"""
Problem:
输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。
Solution: 
implementation 简单实现即可
"""
# 简单的接收列表
n = int(input("Please input a number as the length of list"))
arr = [None] * n
for i in range(n):
    x = int(input("输入元素")); arr[i] = x
print(arr)
# 首先找到最大的元素,最大用 mx 而不是 max 表示
# 注意,这里找到最大的元素不管用,需要找到最大元素的索引(因为要实现交换),最小值同理
mx, mn = max(arr), min(arr)
mx_idx, mn_idx = -1, -1 # 初始化 最大值的下标和最小值的下标
# 通过以下 for 循环,我们可以找到对应极值的索引
for i in range(n):
    if arr[i] == mx and mx_idx == -1: mx_idx = i
    if arr[i] == mn and mn_idx == -1: mn_idx = i
# 实现交换即可, 在 python 中 a, b = b, a 即可实现交换
arr[mx_idx], arr[0] = arr[0], arr[mx_idx]
arr[mn_idx], arr[n - 1] = arr[n - 1], arr[mn_idx]
print(arr)
# 结果
Please input a number as the length of list7
输入元素5
输入元素3
输入元素10
输入元素3
输入元素1
输入元素9
输入元素5
[5, 3, 10, 3, 1, 9, 5]
[10, 3, 5, 3, 5, 9, 1]

3.1

问题描述

求一个 \(3\times3\)

思路分析

过于简单,可能难点在于 二维数组 如何建立吧,然后简单计算即可!!

代码

"""
Problem: 
求一个3*3矩阵对角线元素之和。
Solution: 
implementation 然后 tot = arr[1][1] + arr[2][2] + arr[3][3]
"""
arr = [ [None] * 3 ] * 3
for i in range(3):
    for j in range(3):
        x = int(input("请输入(%d, %d)的元素" % (i, j))); arr[i][j] = x
print(arr)
# 假设计算的是 (0, 0) (1, 1) (2, 2) 的对角线之和
tot = 0
for i in range(3):
    tot += arr[i][i]
print(tot)

3.2

问题描述

一个 \(5\) 位数,判断它是不是回文数。即 \(12321\)

思路分析

这里提供一个思路,就是把数字转换为字符串,再判断是否为回文串

例如: \(num = 12321\) ,我们首先转化,ss = str(num)

然后我们可以这样表示: \(ss=1 2 3 2 1\),我们设两个索引:

  • left = 0 ,指向最开始,用于从左边扫描
  • right = len(ss) - 1 ,指向最后,用于从右边扫描

为了更加清晰的作图,我们做一下变化l = left, r = right(这里你可能会看不懂,没关系,看下面!!)

\[\begin{array}{l} ss=\overset{l=0}{\overbrace{1}}\;2\;3\;2\overset{r=4}{\overbrace{1}} \\ ss=1\;\overset{l=1}{\overbrace{2}}\;3\;\overset{r=3}{\overbrace{2}}\;1 \\ ss=1\;2\;\overset{l=r=2}{\overbrace{3}}\;2\;1 \end{array} \]

我们 l 从左往右扫描,r 从右往左扫描:

  • 如果 ss[l] == ss[r] 则他们相互靠近一格。
  • 如果不同则直接 break
  • 如果 l >= r 时,说明该字符串(数字)为一个回文串。

代码

"""
Problem: 
一个5位数,判断它是不是回文数。即12321是回文数,
个位与万位相同,十位与千位相同。
Solution: 
可以转为 str 之后用两个下标循环处理!
"""
import random
# 随机生成一个 5 位数
num = random.randint(10000, 99999)
print("生成的五位数是: %d" % num)
# 我们把它转为一个字符串,这样我们就能用下标访问了!
ss = str(num)
# 设置两个元素,从左到右,从右到左进行扫描
left, right = 0, len(ss) - 1
# 设置 flag ,假如 flag 为 True 则为回文,否则则不为回文
flag = True
while (left < right):
    # 发现有个元素不同,则肯定不回文,设置 flag = False 然后退出循环
    if (ss[left] != ss[right]):
        flag = False
        break
    left += 1 # 左边的往右
    right -= 1 # 右边的往左
if (flag):
    print("是回文串")
else:
    print("不是回文串")

3.3

问题描述

求 \(1+2!+3!+...+20!\)

思路分析

首先阶乘的定义是啥?

阶乘: \(x! = 1 \times 2 \times \cdots\times x\)

假设 \(fac(x) := x!\) ,我们容易得到一个推导式: \(fac(x) = fac(x-1) * x\)

要计算 \(1+2!+3!+...+20!\)

代码

"""
Problem: 
求1+2!+3!+...+20!的和。
Solution: 
x! 代表阶乘, x! = x * (x - 1)!
因此,我们设一个数组 fac[n],其中 fac[i] := i!, fac[i + 1] = (i + 1) * fac[i]
"""
# 因为要计算的范围是 1 -- 20 所以设最大为 21
fac = [None] * 21
# 设置初值
fac[0] = 1
# tot --> total 代表总答案
tot = 0
for i in range(1, 21, 1):
    fac[i] = i * fac[i - 1]
    tot += fac[i]
print(tot)
# 2561327494111820313

3.4

问题描述

有一分数序列:\(\dfrac{2}{1},\dfrac{3}{2},\dfrac{5}{3},\dfrac{8}{5},\dfrac{13}{8},\dfrac{21}{13} \cdots\)求出这个数列的前 \(20\)

思路分析

这里的难点在于发现序列分子,分母对应的规律。

思考一下发现,上下分子分母都是一个类似斐波那契数列的规律:

  • 2, 3, 5(2+3),8(3+5)
  • 1, 2, 3(1+2),5(2+3)

因此,我们可以先推导出分子分母各为什么,再计算答案:

不妨设:

  • upper(i) := 第 i 个元素的分母
  • lower(i) := 第 i 个元素的分子

显然,他们都拥有相同的规律:

  • upper(i) = upper(i - 1) + upper(i - 2)
  • lower(i) = lower(i - 1) + lower(i - 2)

因此,我们可以先计算出上下分母分子再用一个 for 循环计算答案!

代码

"""
Problem:
有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。
Solution:
这题有点难度,因为上面下面实际上都是一个斐波那契数列,
不妨设上面的第 i 个为 upper(i)
不妨设下面的第 i 个为 lower(i)
则: upper(i) = upper(i - 1) + upper(i - 2)
同: lower(i) = lower(i - 1) + lower(i - 2)
通过递推式计算即可!
"""
upper = [None] * 20
lower = [None] * 20
upper[0], upper[1] = 2, 3
lower[0], lower[1] = 1, 2
for i in range(2, 20, 1):
    upper[i] = upper[i - 1] + upper[i - 2]
    lower[i] = lower[i - 1] + lower[i - 2]
ans = 0.0
for i in range(20):
    ans += (upper[i] / lower[i])
print("答案为 %.4f" % ans)
# 答案为 32.6603

函数章节的作业

1

"""
Q:
依次从控制台输入多个整数,以整数-1作为输入结束标记,编写程序找出其中的最大数和最小数
"""

x = int(input("请输入一个整数"))
mx, mn = x, x # mx --> max, mn --> min
while x != -1:
    mx = max(mx, x)
    mn = min(mn, x)
    # 思考下为啥放最后(因为不放最后会把 -1 也给考虑进来)
    x = int(input("请输入一个整数")) 
print("最大值为 %d, 最小值为 %d" % (mx, mn))

2

"""
Q:
利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来。
Solution:
稍微有点难度,没搞懂没关系
"""
n = 5
def rec_print(cnt):
    """
    cnt: 代表当前是第几层递归
    """
    if (cnt == 5): return
    char = input("请输入一个字符") # 接收输入
    rec_print(cnt + 1) # 直接递归到下一个元素
    print(char) # 之前的元素全部处理好之后输出
rec_print(0)

3

"""
Q:
编写程序将一个不确定位数的正整数进行三位分节后输出,如输入1234567,输出1,234,567。
"""
# 法一,字符串模拟
x = int(input("请输入一个数字"))
if (len(str(x)) < 3): # 假如小于 3 直接切片就会越界! 意识到了嘛!
    print(x)
else:
    print(str(x)[-3:])

# 法二,数学方法
ans = ""
x = int(input("请输入一个数字"))
for i in range(3):
    ans += str(x % 10) # 直接 %10 取低位
    x = x // 10 # 往右移一位, 记得整除!
print(ans[::-1]) # 翻转

4

"""
Q:
请分别用递归技术和迭代技术,将一个十进制正整数,以七进制形式打印在屏幕上。
"""
# 迭代版本
num = int(input("请输入一个数字"))
res = 0 # 存储结果
base = 1 # 存储基数,也就是当前第几位了 eg: 205 这个2就是 2 * 100
while num > 0:
    t = num % 7 # 当前位的数组
    num = num // 7 # 修改num数字大小
    res += (t * base) # 更新答案
    base *= 10 # base 增加一位
print(res)

# 递归版本
# 看不懂没关系,我到时候和猪猪讲!!!! 
def ten2seven(cur, base):
    if (cur <= 0): return 0
    res = (cur % 7) * base
    return res + ten2seven(cur // 7, base * 10)
num = int(input("请输入数字"))
print(ten2seven(num, 1))