Python算法之旅列表的纷争之二进制编码

列表的纷争之二进制编码

小美:最近数学老师给我们玩了有趣的猜年龄游戏,他显示了6张表格,你只要观察这6张表格,然后回答“是”与“不是”就可以了。老师可以根据你的回答猜出你的年龄是多少。

阿福:真有这么厉害?我也想玩玩看。

小美:好的。请看下图的6张表格,然后回答“是”与“不是”。准备好了吗?

阿福:准备好了。

小美:第1张表格中是否包含了你的年龄?

阿福:不是。

小美:第2张表格中是否包含了你的年龄?

阿福:不是。

小美:第3张表格中是否包含了你的年龄?

阿福:不是。

小美:第4张表格中是否包含了你的年龄?

阿福:是。

小美:第5张表格中是否包含了你的年龄?

阿福:不是。

小美:第6张表格中是否包含了你的年龄?

阿福:是。

小美:你今年40岁。不对,你虚报年龄。这应该是你老爸的年龄才对。

阿福:没错,这就是我老爸的年龄。我不让你猜我的岁数,还不是因为你本来就知道我多大,我怕你作弊嘛。

小美:作弊?本小姐是这种人吗?我告诉你,这是科学!科学,你懂吗?

阿福:对对对,科学,科学。开个玩笑嘛。不过你这科学的力量挺强大的,还真猜出我心里想的数字了。能告诉我这背后有什么原理吗?

小美:这个嘛。。。。。。我先不告诉你,给你一张图,看你能不能找出规律来。

阿福:二进制数。

小美:没错,就是二进制数。你看出这些二进制数有什么规律了吗?

阿福:看出来了,图2表格中的每一个二进制数都跟图1表格中的十进制数一一对应。

小美:还有呢?

阿福:还有?暂时没看出来。

小美:这都看不出来?眼睛白长了。我再给你点提示吧,看看每个表格中所有的二进制数符号1的位置和表格编号的关系。

阿福:符号1的位置,表格编号。。。。。。它们有什么关系呢?再让我想想。。。。。。看到了!表格1中所有二进制数的第1位(从右往左数)都是1,表格2中所有二进制数的第2位(从右往左数)都是1,一直到表格6中所有二进制数的第6位(从右往左数)都是1。

小美:没错,正是这样!孺子可教也!

阿福:可是这样的规律又有什么用呢?

小美:有什么用?你个榆木脑袋。这样就可以计算你的年龄了啊。你刚才不是说了吗,第几张表格就对应二进制数的第几位,这个位上面是符号1,就代表你的年龄出现在了这张表格中啊。哪几张表格出现了你的年龄,就代表你的年龄对应的二进制数在这几个位上的符号是1,否则就代表这个位上的符号是0。

阿福:原来是这样。我明白了,刚才你是根据二进制数各个位的情况,转化成十进制数以后得到我的年龄的啊。让我算一算,0*2^0 + 0*2^1 + 0*2^2 + 1*2^3 + 0*2^4 + 1*2^5 = 40,完全正确。

小美:这回搞懂了吧?

阿福:总算明白了。没想到这里还隐藏着二进制编码的奥秘。不错,有点意思。对了,小美,我觉得这是一个编程的好素材啊。你不是擅长海龟作图吗,能不能用turtle把上面那两张图画出来啊。

小美:这有什么难的,不就是画几张表格吗?我只需要用一个二维列表table存储每一个表格中的数字就行了。例如table[0] = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63],表示table[0]存储了表1中的所有数字。我只要写一个函数依次把表格编号,表格线和数字画出来就行了。


题目1:

函数功能:根据输入的表格内容,画len(table)个4行col列的二维表格

函数名:draw_table(x0,y0, h, w, table)

参数表:x0, y0 -- 存储了第一张表格的左上角坐标

        h, w --分别表示表格的高度和宽度

        table --存储了表格内容的二维列表。

返回值:没有返回值。


代码1:

def draw_table(x0,y0, h, w, table):

    n = len(table)

    col = len(table[0]) // 4 #每张表均分成4行col列

    for i in range(n):#从上到下依次排列共n张4行col列的二维表格

        y0 -= h * 5

        for command in ('编号', '画线', '写字'):

            if command == '编号':

                tt.penup()

                tt.goto(x0-40, y0-h*2)

                tt.pendown()

                num = ''.join(['表', str(i+1),':'])

                tt.write(num,align="center", font=("黑体", 16,"normal"))

            elif command == '画线':

                for j in range(5):#画5条横线

                    x, y = x0, y0 - j * h

                    tt.penup()

                    tt.goto(x, y)

                    tt.pendown()

                    tt.forward(col * w)

                tt.right(90)

                for j in range(col+1):#画col+1条竖线

                    x, y = x0 + j * w, y0

                    tt.penup()

                    tt.goto(x, y)

                    tt.pendown()

                    tt.forward(4 * h)

                tt.right(-90)

            else:

                for r in range(4):

                    x, y = x0, y0 - r * h - h /2

                    for c in range(col):

                        tt.penup()

                        tt.goto(x+w/2, y-h*2/5)

                        tt.pendown()

                       tt.write(table[i][r*col+c], align="center", font=("黑体", 12,"normal"))

                        x += w


import turtle as tt

tt.TurtleScreen._RUNNING= True  #启动绘图,在IDE中运行加这句可避免报错

tt.speed(0)

tt.ht()#隐藏笔头

table = [[1, 3, 5,7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45,47, 49, 51, 53, 55, 57, 59, 61, 63],

         [2, 3, 6, 7, 10, 11, 14, 15, 18, 19,22, 23, 26, 27, 30, 31, 34, 35, 38, 39, 42, 43, 46, 47, 50, 51, 54, 55, 58, 59,62, 63],

         [4, 5, 6, 7, 12, 13, 14, 15, 20, 21,22, 23, 28, 29, 30, 31, 36, 37, 38, 39, 44, 45, 46, 47, 52, 53, 54, 55, 60, 61,62, 63],

         [8, 9, 10, 11, 12, 13, 14, 15, 24, 25,26, 27, 28, 29, 30, 31, 40, 41, 42, 43, 44, 45, 46, 47, 56, 57, 58, 59, 60, 61,62, 63],

         [16, 17, 18, 19, 20, 21, 22, 23, 24,25, 26, 27, 28, 29, 30, 31, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,61, 62, 63],

         [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,63]]

x0, y0, h, w = -400, 600, 25, 60 #分别存储了第一张表格的左上角坐标、高度和宽度

draw_table(x0, y0,h, w, table)

tt.done()

古老师:嗯,不错,小美的海龟作图是画得越来越溜了。可美中不足的是这个存储表格信息的二维列表还是手动生成的,可惜,可惜啊!

小美:可惜?不手动生成,难道还可以自动生成?

阿福:当然可以啦。难道你没有学过列表生成式吗?

小美:列表生成式?什么是列表生成式?

古老师:列表生成式是Python内置的非常简单却强大的功能,它可以用来快速创建一个列表。例如我们要生成一个列表a = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100],只需写做a = [x * x for x in range(1, 11)]。写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,非常简单方便。

阿福:for循环后面还可以加上if判断,这样我们就可以筛选出仅奇数的平方:a = [x * x for x in range(1, 11) if x % 2 == 1]。

小美:哦,原来是这样。我知道了,我们可以生成一个二维列表,该列表的每一个元素都是一个列表,我们可以使用列表生成式生成每一个列表元素。例如table[0] = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63],它是表1中的数字,其特征是转化成二进制数后第1位(从右往左数)都是1,我可以使用内置函数bin()把十进制数转换成二进制数,然后判断它的第1位是不是1,从而生成table[0]的值。以此类推可以生成所有的table[i]。

阿福:没错,就是这样。还可以不使用bin()函数,而是利用按位与运算来判断某个二进制数的第i位是不是1。

古老师:阿福思维很开阔,我们确实可以用多种方法来判断二进制数的某个位是1还是0。这样吧,我来分配一下任务,小美用bin()函数做,阿福用位运算做,最后看看效果是否相同。


知识小贴士:

Python bin() 函数:bin() 返回一个整数的二进制表示,其语法为bin(x),其中x是int或者long int数字,返回值是一个字符串。例如bin(10)返回'0b1010',bin(-6)返回'-0b110'。


代码2:

n = 6

table = [[x for xin range(2**i, 2**n+1) if x & 2**i != 0] for i in range(n)]

代码3:

n = 6

table = [[x for xin range(2**i, 2**n+1) if bin(x)[-i-1] == '1'] for i in range(n)]

table2 =[[bin(x)[2:] for x in range(2**i, 2**n+1) if bin(x)[-i-1] == '1'] for i inrange(n)]

古老师:非常好!都抓住了列表生成式的精髓,其中小美还把十进制和二进制数两个列表都生成了,很有创造性。阿福的位运算代码也很简洁,值得表扬!

对了,上次我在公园里看到一个猜姓氏的游戏,界面如下图所示。你们看看它背后的原理是什么?

小美:这不就是刚才我们玩的猜年龄游戏吗?只不过它把数字改成了姓氏,道理是一样一样的。

阿福:没错,又是一个二进制编码的游戏。

古老师:眼睛够亮的啊!既然你们都看出来了,那就用turtle把它画出来吧。

这是要用到的百家姓前128种姓氏:李王张刘陈杨赵黄周吴徐孙胡朱高林何郭马罗梁宋郑谢韩唐冯于董萧程曹袁邓许傅沈曾彭吕苏卢蒋蔡贾丁魏薛叶阎余潘杜戴夏钟汪田任姜范方石姚谭廖邹熊金陆郝孔白崔康毛邱秦江史顾侯邵孟龙万段漕钱汤尹黎易常武乔贺赖龚文庞樊兰殷施陶洪翟安颜倪严牛温芦季俞章鲁葛伍韦申尤毕聂丛焦。

我还有点事,就先走了,回头见啊。



彩蛋:

阿福:这个好像可以直接调用你前面写的draw_table()函数,只要修改一下table列表的值就行了。

小美:那当然了,这就叫做函数的通用性。还不错吧?

阿福:看你嘚瑟的,这不应该是编写函数的基本要求吗?对了,代码写好了吗?

小美:早就写好了。因为可以直接调用draw_table()函数,所以我只写了主函数部分。


代码4:

import turtle as tt

tt.TurtleScreen._RUNNING= True  #启动绘图,在IDE中运行加这句可避免报错

tt.speed(0)

tt.ht()#隐藏笔头

xing = '李王张刘陈杨赵黄周吴徐孙胡朱高林何郭马罗梁宋郑谢韩唐冯于董萧程曹袁邓许傅沈曾彭吕苏卢蒋蔡贾丁魏薛叶阎余潘杜戴夏钟汪田任姜范方石姚谭廖邹熊金陆郝孔白崔康毛邱秦江史顾侯邵孟龙万段漕钱汤尹黎易常武乔贺赖龚文庞樊兰殷施陶洪翟安颜倪严牛温芦季俞章鲁葛伍韦申尤毕聂丛焦'

n = 7

table =[[xing[x-1] for x in range(2**i, 2**n+1) if x & 2**i != 0] for i inrange(n)]

x0, y0, h, w = -400, 600, 25, 60 #分别存储了第一张表格的左上角坐标、高度和宽度

draw_table(x0, y0,h, w, table)

num = input('输入二进制编码:')

print(f'{xing[int(num[::-1],2)-1]}先生/女士,你好!')

tt.done()

你可能感兴趣的:(Python算法之旅列表的纷争之二进制编码)