昨天在checkio上做了一道挺有意思的题。这道题中同时用上了列表推导、filter、int、join、map、bin和数字异或的特性。将解题思路记下来,也算是对自己近期学习Python的一个总结吧~~
题目(译)
原题地址:Mono Captcha 让我们来教教机器人Stephan识别简单的数字。机器人使用低分辨率的等宽字体。你可以看看下面图片中的字体感受一下。这个字体具备一个像素错误的抗噪能力。 你的程序应该能够读取以二进制数字矩阵图像显示的数字。每一个数字最多只允许包含一个错误的像素。每个数字之间是一个像素的空间(例如"1"虽然比其他数字窄,但是还是具有3个像素的宽度)。
输入
一个图片,该图片用拥有取值为1或0的列表组成的列表 ## 输出 整型数字
例子
1 | checkio([[0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0], |
前提条件
矩阵行数 == 5 5 ≤ 矩阵列数 < 30 ∀ x ∈ 矩阵 : x == 0 or x == 1 数字宽度 == 3 每一个数字至多包含一个像素错误。 数字间宽度 == 1
题解
由于每个数字的宽度和数字间的间隔是固定的,因此可以分割输入矩阵为一个一个的数字块,然后比较该数字块与哪一个数字块最接近即可。因此,思路梳理为:分割 -> 查找 -> 拼接
分割
由于只能使用Python自带模块,因此不能考虑使用例如第三方模块numpy来进行简单快速的矩阵操作进行矩阵分割。此时,可以使用列表推导代替。 另外,由于每个数字的宽度是3, 数字间宽度为1,因此,第i个数字为矩阵的第1+i4列到第3+i4列。 此时,可以写出类似下面的代码来分割: 1
2def slice(array, index=0):
return [item[1+index*4:4+index*4] for item in array]1
2
3digits = {0b010110010010010:'1',0b111001011100111:'2',0b111001010001111:'3',
0b101101111001001:'4',0b111100110001110:'5',0b011100111101011:'6',
0b111001010100100:'7',0b111101111101111:'8',0b011101111001110:'9',0b110101101101011:'0'}1
2def transfer(array, index=0):
return int("".join(["".join(map(lambda i:str(i),item[1+index*4:4+index*4])) for item in array]),2)1
func = lambda value, real=transfer(array,i): 1 == bin(value^real).count("1") or '0b0' == bin(value^real)
filter(func,digits)
由于digits是字典,因此实际上func中的value取的是digits的键,这就是为什么digits长得这么怪异。另外,我们也可以利用digits的键值查找到对应的数字。 1
digits[filter(func,digits)[0]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14def transfer(array, index=0):
return int("".join(["".join(map(lambda i:str(i),item[1+index*4:4+index*4])) for item in array]),2)
def checkio(array):
digits = {0b010110010010010:'1',0b111001011100111:'2',0b111001010001111:'3',
0b101101111001001:'4',0b111100110001110:'5',0b011100111101011:'6',
0b111001010100100:'7',0b111101111101111:'8',0b011101111001110:'9',0b110101101101011:'0'}
res = ""
#数字的个数可以用矩阵的列数len(array[0])/4得到,当然,python3k在这里需要修改一下
for i in xrange(0,len(array[0])/4):
func = lambda value, real=transfer(array,i): 1 == bin(value^real).count("1") or '0b0' == bin(value^real)
res+=digits[filter(func,digits)[0]]
return int(res)