1. 矩阵:
在Python 中,我们可以使用列表的列表 (a list of lists) 来存储矩阵, 每个内部列表代表矩阵一行。例如,我们可以用
M = [ [5, 6, 7],
[0, -3, 5] ]
来存储矩阵
我们可以使用M[1]来访问矩阵的第二行(即[0, -3, 4]),也可以使用M[1][2]来访问矩阵的第二行的第三项(即 5)。
(1)编写函数 matrix_dim(M),该函数输入上面格式的矩阵 M,返回矩阵 M 的维度。例如, matrix_dim([[1,2],[3,4],[5,6] ]) 返回 [3, 2]。
通过len函数获取行数以及列数即可:
# 定义获取维度函数 def matrix_dim(M): return [len(M), len(M[0])]
测试输出:
(2)编写函数mult_M_v(M, v), 返回n×m矩阵M 和m×1向量v的乘积。
①首先通过len函数分别获取矩阵的维度和向量的维度对数据有效性进行检验
②然后通过循环遍历乘积并求和求出对应的结果矩阵的值
# 定义矩阵乘向量函数 def mult_M_v(M, v): # 判断输入合法性 if len(M[0]) == len(v): # 设置结果矩阵 res = [[0] for i in range(len(M))] # 依次循环进行计算 for i in range(len(M)): for k in range(len(v)): res[i][0] += (M[i][k] * v[k][0]) return res return ('invalid input')
测试输出:
①非法数据:
②合法数据:
(3)编写函数transpose(M), 返回矩阵的转置。
通过zip函数实现对各个行的正向收集将其转置为元组。并通过遍历将元组重新转回列表即可。
# 定义矩阵转置函数 def transose(M): temp = list(zip(*M)) res = [] for i in temp: res.append(list(i)) return res
测试输出:
(4)编写函数largest_col_sum(M), 寻找矩阵 M 中元素总和最大的列,如上面矩阵中第三列元素的总和最大,则返回 12(7+5=12)。
通过两层循环依次遍历整个矩阵,内层循环遍历每列求出每列的和,外层循环遍历每列的和求出和的最大值。
# 定义求总合最大列函数 def largest_col_sum(M): # 定义结果 res = 0 for x in range(len(M[0])): # 定义每列的结果 temp = 0 # 对每列进行遍历 for y in range(len(M)): temp += M[y][x] # 取最大值 res = max(res, temp) return res
测试输出:
(5)编写函数switch_columns(M, i, j), 交换矩阵 M 的第 i 列和第 j 列,返回新的矩阵 M。
先将矩阵转置,转置后所交换的i,j列即变为i,j行,只需交换对应列表即可。交换完毕后再转置回结果矩阵并返回。
# 定义交换列函数 def switch_columns(M, i, j): # 先对矩阵进行转置,将列变为行,方便交换 res = transose(M) # 交换对应行 res[i], res[j] = res[j], res[i] # 转置回结果矩阵 res = transose(res) return res
测试输出:
(6)把以上函数(a-e)写进模块matrix.py。编写 test_matrix.py 调用模块 matrix并测试函数(a-e)。
我们将所有函数都放在matrix.py中,并在test_matrix.py中进行调用测试。测试效果如下:
我们使用如下的矩阵和向量进行测试:
输出结果如下:
2. 信用卡号码:
我们知道信用卡的号码非常长,但是仔细研究会其中也有规律,例如
(1)American Express 使用 15 位数字,MasterCard 使用 16 位数字,Visa 使用 13 位或 16 位数字。
(2)American Express 号码以 34 或 37 开头;MasterCard 号码以 51、52、53、54 或 55 开头;Visa 号码以 4 开头。
(3)信用卡号码有一个内置的“数学关系”。
对于这个“数学关系”,大多数信用卡都使用 IBM 的 Hans-Peter Luhn 发明算法。根据 Luhn 的算法,我们可以确定信用卡号是否有效。方法如下
(1)从倒数第二个数字开始,每隔一个数字乘以 2;然后将得到的所有乘积的各个数位相加(注意不是乘积本身相加),得到结果 sum1;
(2)对于在(1)中没有乘上 2 的数字,直接相加得到结果 sum2;
(3)把(1)的结果和(2)的结果相加,即 sum3=sum1+sum2。若 sum3 的最后一位数字是 0,则信用卡号码有效。
例如有一个号码是4003600000000014,在第一步中,我们把标红色的数字乘以 2 然后相加,得到
1*2+0*2+0*2+0*2+0*2+6*2+0*2+4*2 = 2+12+8
将所得的所有乘积的各个数位相加,得到
sum1 = 2+1+2+8 = 13
在第二步中,我们把标黑色的数字直接相加,得到
sum2 = 4+0+0+0+0+0+3+0 = 7
最后第三步 sum3 = sum1+sum2 = 20,因为 20 的最后一位是 0,那么这个信用卡卡号是有效的。
任务:
编写函数 validCredit(cardNum),检查输入信用卡号码 cardNum 是否有效;若有效,则输出该卡的发行公司(American Express,Mastercard,Visa)。
例子:
validCredit(4003600000000014) # 输出:Valid,Visa
validCredit(6177292929) # 输出:Invalid
参考的有效的信用卡号码:
大致思路:
①长度与开头检验:
首先通过len函数获取每个卡号的长度,并使用startswith函数对比各个卡号的长度以及开头前几位字符。如果符合,则修改标志变量的值为对应键值。如果不符合,则直接输出“Invalid”并直接返回。
②Luhn检验:
通过for循环对整个卡号进行逆序遍历。对于sum1,可以求出十位和个位的值进行计算。对于sum2直接累加即可。当sum1和sum2累加完毕后,加和并对10取余,若余数为0则为有效卡,调用类型字典输出卡类型。如果余数不为零,则直接输出“Invalid”。
源代码:
# 定义卡号有效性检测函数 def validCredit(cardNum): # 定义信用卡种类字典 typeDic = {1: 'American Express', 2: 'MasterCard', 3: 'Visa'} # 首先对卡号长度与开头进行检验并区分卡的类型 flag = -1 if len(cardNum) == 15 and (cardNum.startswith('34') or cardNum.startswith('37')): flag = 1 # 如果是American Express elif len(cardNum) == 16 and (cardNum[0] == '5') and (1 <= int(cardNum[1]) <= 5): flag = 2 # 如果是MasterCard elif (len(cardNum) == 13 or len(cardNum) == 16) and (cardNum[0] == '4'): flag = 3 # 如果是Visa else: print('Invalid') return # 通过Luhn算法进行有效性检验 sum1 = 0 sum2 = 0 # 计算sum1 for i in range(len(cardNum)-2, -1, -2): # 获取十位 sum1 += int(int(cardNum[i])*2/10) # 获取个位 sum1 += int(cardNum[i])*2 % 10 # 计算sum2 for i in range(len(cardNum)-1, -1, -2): sum2 += int(cardNum[i]) # 最终有效性检验 if (sum1+sum2) % 10 == 0: print('Valid, '+typeDic[flag]) else: print('Invalid') # 获取输入进行测试 cardNum = input('Please input a card to check: ') print('Card Number:'+cardNum) validCredit(cardNum)
代码解释:
L5定义信用卡类型字典,便于输出信用卡类型。
L9-L17:对信用卡长度和开头字符进行检验,如果满足对应类型,则修改标志变量进行存储;如果不满足任何类型,则输出“Invalid”
L23-L27:对sum1进行求和并检验。易知,乘以二之后的值一定为两位数,则可以拆开两个数位进行求和获取sum1。
L29-L30:对sum2进行累加获取
L33-L36:最终的有效性检验并分类输出信用卡类型
L40-L42:获取输入对程序进行测试
代码测试:
①Visa卡检验:
②American Express卡检验
③MasterCard卡检验:
④非法卡号检验:
3. 分析名著:
编写程序,通过统计名著中使用的 20 个最常用单词来分析两位作家的写作风格。我们分析的名著和作家如下:
The Strange Case of Dr. Jekyl and Mr. Hyde, Robert Louis Stevenson (hyde.txt)
Treasure Island, Robert Louis Stevenson (treasure.txt)
War and Peace, Leo Tolstoy (war.txt)
我们定义单词是连续的字母序列(小写或大写)。单词紧靠前的字符,以及紧随其后的字符是一个非字母。比如,缩写 we’re,我们把它看成两个单词 we 和 re。本次作业无需考虑诸如 we’re,other’的单词。
任务和步骤:
(1)编写函数 parse(string),接受字符串参数 string,并返回一个列表。列表包含 string 中所有长度至少为 4 个字母的单词,且所有单词都为小写。例如 string 为
“Shenzhen’s big, beautiful and rich place”
则返回
[“Shenzhen”, “beautiful”, “rich”, “place”]
(2)编写函数 mostFrequentWords(filenames),接受参数 filenames,表示从文件filenames 中读取数据,函数返回类型不限制。
(3)统计两位作家最常用的 20 个单词。注意三部名著中有两部是 Robert Louis Stevenson 的,需要综合考虑此作家的两部名著,给出他最常用的 20 个单词。
大致思路:
①单词切分:
首先利用str.replace()函数将所有非法符号(标点符号,换行以及数字)转换成空格,便于接下来进行处理。完成空格替换后,使用str.split()函数以空格为界对单词进行切分。完成切分后遍历整个单词列表,丢弃换行,空单词以及长度不足4的单词。对于每个合法单词,将其转换成对应小写。
②频率统计:
首先按行获取文件读入到EOF结束,当读入是空行时直接忽略直到合法输入。对于非空行首先调用单词切分函数并获取返回的结果列表。遍历结果列表,如果在字典中可以找到,则对应值加一;若不能找到,则创建以单词字符串为键值的键值对,并赋值为1。遍历完全部输入流文件后即完成读入操作并关闭输入流。
③频率排序:
完成单词频率统计后,获取单词频率字典。为了便于以频率进行排序,首先遍历整个字典,然后以值为第一个元素,字典键值为第二个元素,构建元组,并以“单词——频率”元组构建列表。调用库函数sort()对列表进行升序后调用reverse()进行翻转成降序列表。此时列表即为以单词出现频率为降序的列表。
④输出结果:
完成前面的操作后,已经获得了有序的频率列表。只需遍历列表前20个元素,并进行依次输出即可。
源代码:
本题下,将分块依次展示源代码并进行讲解:
①引入string库:
import string
②定义切分单词函数:
# 定义切分单词函数 def parse(sentence): # 去除标点符号换行以及数字 for ch in sentence: if ch in string.punctuation or ch == '\n' or '0' <= ch <= '9': sentence = sentence.replace(ch, " ") # 利用空格完成切分 temp = sentence.split(' ') # 倒序查找空列表或仅有换行列表并丢弃 for i in range(len(temp)-1, -1, -1): # 丢弃空单词以及和长度不足4的单词 if len(temp[i]) < 4: del temp[i] continue # 判换行 if temp[i] == '\n': del temp[i] continue temp[i] = temp[i].lower() return temp
L4-L6:遍历整个字符串,将非法字符替换为空格便于切分单词
L8:利用库函数split以空格为分界将单词切分存入列表中
L10-L20:遍历单词列表,首先剔除长度小于4的单词(L12-L14),其次去除空行(L16-L18)。最后将单词全部转换为小写(L19)。处理完后返回结果列表。