昨天晚上,一个朋友给我发了一张图,据说是江苏网警微博出的题目,叫“2018年刑侦科试题”。当时已经很晚了,所以今天早上才解决这道题目。
试卷如图所示:
如果你没有接触过数理逻辑中的布尔代数,对真命题、假命题、恒真式等概念不了解,对题目可能会感到云里雾里。这道题目,我以前就见过类似的,不知道这一次是不是又把那道题目搬了出来,还是江苏网警仿造的一道题目。记得当时,我觉得这道题目很难,我没有做出来。但是,今天不一样了,我有计算机,我有python(2017年世界上最受欢迎的语言)。
科普一下真命题和假命题。一般的,在数学中把用语言、符号或式子表达的,可以判断真假的陈述句叫做命题。命题真值只能取两个值:真或假。真对应判断正确,假对应判断错误。任何命题的真值都是唯一的,称真值为真的命题为真命题。例如“15是5的倍数”,就是真命题。而“2010年是闰年”,则是假命题。在程序里,真命题的值是True,假命题的值为False。
再讲几个运算符:not,and和or。
not就是取反运算符,真的反面就是假,也就是说 not True == False(在python语言里,=被用来赋值,==则被用来判断是否相等)。同样地,not False == True。
and是与运算符。A and B如果是真的话,要求A和B都是真。比如"15能被5整除“并且”15能被3整除“,这个命题就是真命题。如果我们记命题 ”15能被5整除“为A,记命题B”15能被3整除“为B,那么就有 A and B == True。如果A和B中间有一个是假命题,则A and B == False。
or是或运算符。A or B如果是真的话,只要A和B中有一个是真即可。比如"15能被5整除“或者”15能被2整除“,这个命题就是真命题。
搞清这些概念之后(如果还是不太清楚,可以查阅一下相关数学书),我们再回过头来看这张试卷。
很显然,这张试卷是浑然一体的,题目之间有着千丝万缕的联系,你单做一道题是不行的。如果你第二题选了A,那么根据第二题的题意,第五题你必须选C,否则就矛盾了。因此,你需要找到的答案就是有顺序的十个字母(都在ABCD之中),并且这十个字母必须满足题目中给出的十个逻辑关系。
对于普通人来说,常规的方法就是先尝试正常分析解答,如果解答不出来就可能去”撞大运“——随便填几个答案,看看有没有矛盾,有矛盾的话进行修改,直到获得一个没有矛盾的答案。
其实,这也是我的思路,我也要随机产生答案去碰运气。但是,不同的是,我要利用计算机,产生上百万个答案去试探。由于现代计算机速度很快,不需要很长时间,就能尝试出正确答案(本题答案最多有4^10=1048576个)。
对于每一个生成的答案,都要对其中的十个选项逐一进行测试,看看是否满足要求。我们把每个答案保存在一个数组(数组变量名叫answer)里,由于python里面的数组下标从0开始,为了阅读方便,我们使用一个长度为11的数组,将答案存放在数组第二项到第十一项里。这样answer[1]就是数组的第二项,但同时也是答案的第一项。我们先构造几个函数,对数组的那些项分别进行处理,并根据判断结果返回True或False。
先看第一题,很显然这一题不管答案是什么,都是对的,因为这一题就好比说的是“我是我”。
python是脚本语言,大部分的语句阅读起来很直白。为了测试第二题的选项,我们定义一个函数judge2(a)。这里的a是输入值,在后面的主程序里面,可以用答案数组answer取代a。程序语言里的函数,跟初中学的函数一样,除了自变量(相当于输入值),还有因变量(也就是输出值)。只不过,程序的函数输出值可能超过一个。在这里,我们就把输出值限制为True和False,用以标明第二题的答案是否满足题目要求。函数如下:
def judge2(a):
if a[2] == 1:
if a[5] == 3:
return True
else:
return False
elif a[2] == 2:
if a[5] == 4:
return True
else:
return False
elif a[2] == 3:
if a[5] == 1:
return True
else:
return False
elif a[2] == 4:
if a[5] == 2:
return True
else:
return False
上面函数里的if就是假如的意思。if a[2] == 1,意思就是如果第二题答案是A的话,后面紧接一个if a[5] == 3,意思就是如果第五题答案是C的话。后面的return True就是要让该函数返回真值,表明第二题答案满足要求。else:否则的意思。return False就是说,如果第五题答案不是C,则返回错误值,表明第二题答案不满足要求。elif a[2] == 2,则判断当第二题答案是B时,是否产生矛盾。
第二个函数写完后,我们再依法炮制第三题至第十题的函数,代码如下:
def judge3(a):
if a[3] == 1:
if a[3] != a[6] and a[3] != a[2] and a[3] != a[4]:
return True
else:
return False
elif a[3] == 2:
if a[6] != a[3] and a[6] != a[2] and a[3] != a[4]:
return True
else:
return False
elif a[3] == 3:
if a[2] != a[3] and a[2] != a[6] and a[3] != a[4]:
return True
else:
return False
elif a[3] == 4:
if a[4] != a[3] and a[4] != a[6] and a[4] != a[2]:
return True
else:
return False
def judge4(a):
if a[4] == 1:
if a[1] == a[5]:
return True
else:
return False
elif a[4] == 2:
if a[2] == a[7]:
return True
else:
return False
elif a[4] == 3:
if a[1] == a[9]:
return True
else:
return False
elif a[4] == 4:
if a[6] == a[10]:
return True
else:
return False
def judge5(a):
if a[5] == 1:
if a[5] == a[8]:
return True
else:
return False
elif a[5] == 2:
if a[5] == a[4]:
return True
else:
return False
elif a[5] == 3:
if a[5] == a[9]:
return True
else:
return False
elif a[5] == 4:
if a[5] == a[7]:
return True
else:
return False
def judge6(a):
if a[6] == 1:
if a[8] == a[2] and a[8] == a[4]:
return True
else:
return False
elif a[6] == 2:
if a[8] == a[1] and a[8] == a[6]:
return True
else:
return False
elif a[6] == 3:
if a[8] == a[3] and a[8] == a[10]:
return True
else:
return False
elif a[6] == 4:
if a[8] == a[5] and a[8] == a[9]:
return True
else:
return False
def judge7(a):
count1 = 0
count2 = 0
count3 = 0
count4 = 0
for i in range(1,11):
if a[i] == 1:
count1 += 1
elif a[i] == 2:
count2 += 1
elif a[i] == 3:
count3 += 1
elif a[i] == 4:
count4 += 1
smallest = min(count1,count2,count3,count4)
if a[7] == 1:
if count3 == smallest:
return True
else:
return False
elif a[7] == 2:
if count2 == smallest:
return True
else:
return False
elif a[7] == 3:
if count1 == smallest:
return True
else:
return False
elif a[7] == 4:
if count4 == smallest:
return True
else:
return False
def judge8(a):
if a[8] == 1:
if abs(a[1] - a[7]) != 1:
return True
else:
return False
elif a[8] == 2:
if abs(a[1] - a[5]) != 1:
return True
else:
return False
elif a[8] == 3:
if abs(a[1] - a[2]) != 1:
return True
else:
return False
elif a[8] == 4:
if abs(a[1] - a[10]) != 1:
return True
else:
return False
def judge9(a):
if a[9] == 1:
if (not (a[1] == a[6])) == (a[6] == a[5]):
return True
else:
return False
elif a[9] == 2:
if (not (a[1] == a[6])) == (a[10] == a[5]):
return True
else:
return False
elif a[9] == 3:
if (not (a[1] == a[6])) == (a[2] == a[5]):
return True
else:
return False
elif a[9] == 4:
if (not (a[1] == a[6])) == (a[9] == a[5]):
return True
else:
return False
def judge10(a):
count1 = 0
count2 = 0
count3 = 0
count4 = 0
for i in range(1,11):
if a[i] == 1:
count1 += 1
elif a[i] == 2:
count2 += 1
elif a[i] == 3:
count3 += 1
elif a[i] == 4:
count4 += 1
smallest = min(count1,count2,count3,count4)
biggest = max(count1,count2,count3,count4)
diff = biggest - smallest
if a[10] == 1:
if diff == 3:
return True
else:
return False
elif a[10] == 2:
if diff == 2:
return True
else:
return False
elif a[10] == 3:
if diff == 4:
return True
else:
return False
elif a[10] == 4:
if diff == 1:
return True
else:
return False
上述函数,阅读起来难度不大,需要说明几个函数。abs是绝对值函数,在第八题中出现过,用来判断答案选项是否相邻。由于我们用1234来代替ABCD,因此,只要两个选项相邻,它们的绝对值之差就是1。min函数为求最小值函数,例如min(1,2,3)= 1。相对的,max函数则是求最大值函数。
好了,下面编写主函数。由于我们需要使用随机函数,所以在程序里加入这样一句话:
import random
这样就加载了random模块,可以使用random自带的各种函数。
def main():
found = False
while (found == False):
answer = []
for i in range(11):
answer.append(random.randrange(1,5))
if (judge2(answer) == True) and (judge3(answer) == True) and (judge4(answer) == True)\
and (judge5(answer) == True) and (judge6(answer) == True) and (judge7(answer) == True)\
and (judge8(answer) == True) and (judge9(answer) == True) and (judge10(answer) == True):
print(answer[1:11])
found = True
我们首先设置一个标识found,用来标识是否找到了正确答案,将其初始值设置为假(False)。while语句则是一个循环,如果没有找到的话,也就是说found == False,我们就不断循环。
随后,我们定义一个空数组answer,再用随机函数random.randrange(1,5)随机生成1-4之间的整数(也就是各个题目的答案)。使用for语句(这也是一个循环体)调用11次,生成11个选项(我们只需要用到后面10个)。
下面,我们将生成的答案,代入上面编写的9个函数里,进行检验。如果九个函数都满足条件(都返回True),我们的程序就结束了,并打印结果:print(answer[1:11]),同时把found标识为True,结束循环。由于答案总是存在的,所以循环一定会结束,这个方法跟暴力破解法差不多。只不过,使用暴力破解的话,需要对每一个可能的答案都进行测试,这样就需要写十重循环,看起来很繁琐。对于只需要找出一个答案的题目来说,使用随机法也足够了。
最后,我们再加上一句,完成程序:
main( )
这个语句调用主函数,主函数再调用相应的子函数,开始计算。由于现在计算机一秒钟可以进行十亿次计算,运算起来还是很快的。虽然python写的程序速度不如其他语言写的程序,但是等待了几秒后,还是出来了答案:
最后一行的[2,3,1,3,1,3,4,1,2,1]即是算出的结果,对应答案为BCACACDABA。
整图如下:
仔细查看整图,你可以体会出题人满满的恶意。为了应对未来的恶意挑战,你是不是应该感觉买本python书学起来呢。另外,我在网上查了查,有人提供了一份手写的答案,好几页,估计没几个人能看懂。还是用计算机解决重复劳动更安逸。
PS:最近有三个省市的小学课本已经加入了python课程,python编程席卷全国,也只是迟早的事。赶紧学吧,这样可以给孩子辅导作业。不学的话,小心被小学生鄙视噢。
领取专属 10元无门槛券
私享最新 技术干货