Python实现算命老先生算姓氏问题
程序员文章站
2022-07-03 21:44:06
...
老先生算姓氏
街边一帮人围观着一位算命的老先生,老先生指着一张张写有姓氏的卡纸说:您只要告诉我哪几张卡纸上有您的姓氏,我就能算出您的姓。围观的人试了一次又一次,还真邪乎,每次老先生都算的非常准。那么老先生这到底是啥门道呢?
算姓氏的思路
先把百家姓的按照顺序给编号,如图中的1,2,3,4分别代表了 ‘赵’ ‘钱’ ‘孙’ ‘李’,然后把这个编号转化成二进制数据(如图中绿色一行一行的数字所示),然后每一列就作为一个卡纸,每列中的数字1就代表着对应的姓氏需要写到这个卡纸当中去,0则不写(比如 ‘孙’ 字就需要写到卡纸1和卡纸2中去),比方当我们告诉老先生自己的姓氏出现在了卡纸1和卡纸2,而其他卡纸里面没有时,老先生就知道了1 1 0 0 0 0 0,倒过来就是0 0 0 0 0 1 1转换成十进制就是3,对应的就是 ‘孙’ 字。
代码中出现的二进制和十进制转换的思路:
import math
NAME = '赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶' # 取老百家姓前31个姓氏
def get_pages(names):
pages_num = int(math.log(len(names), 2)) + 1 # 根据姓氏的个数,计算出需要多少张卡纸,是由最大数的二进制最左边的1决定的
# 这里也可以写 pages_list = [''] * pages_num
pages_list = ['' for _ in range(pages_num)] # 根据算出来的卡纸数,用空字符串定义空白卡纸,再用列表装起来方便遍历给观众展示
for i in range(len(names)): # 遍历所有姓氏的索引
code = i + 1 # 姓氏的编号不能从0开始,0的二进制全是0,没法写到卡纸上去,所以姓氏的编号等于索引+1
for j in range(pages_num): # 遍历的次数等于卡纸的张数,因为一个姓氏需要在所有卡纸上判断是否需要写上去
if code % 2 != 0: # 把编码的十进制转为二进制过程,余数不等于0,即等于1,则根据当前的索引把姓氏写到空白卡纸上去并用逗号隔开
pages_list[j] += names[i] + ','
code //= 2 # 把编码的十进制转为二进制过程中得到的商作为下一步的二进制转化
return pages_list
def get_name(answers, name):
result = 0
for answer in reversed(answers): # 把观众的回答组成的二进制,正好是反的,所以需要倒过来
# 下面这步是二进制转十进制,十进制转二进制是一直除以2得到商和余数,余数组成了二进制的结果,而反向得到十进制就是商一直乘以2再加上余数
result = result * 2 + answer # 得到姓氏的编号
return name[result-1] # 编号-1就是姓氏的索引
if __name__ == '__main__':
answers_list = [] # 定义一个空列表容器,来装观众的回答
pages_list = get_pages(NAME) # get_pages函数会返回所有写有姓氏的卡纸组成的列表
for page in pages_list: # 把每一个卡纸拿出来,依次问观众
print(page) # 呈现卡纸给观众看
answer = input('Is you name in this pages? (y, n)\n') # 问观众姓氏是否在这张卡纸上,输入y或n
if answer.lower() == 'y': # 可能输入的会是大写,所以同意转成小写,如果回答y,则说明有姓氏在上面
answers_list.append(1) # 有姓氏在上面,就在列表中添加元素1
else:
answers_list.append(0) # 没有姓氏在上面,就在列表中添加元素0
name = get_name(answers_list, NAME) # get_name函数会根据观众的回答返回最终的姓氏
print('Your name is', name)
推荐阅读