一类有趣的枚举问题

四个嫌疑犯

ABCD4个嫌疑犯,A说是B干的,B说是D干的,C说不是我干的,D说B在说谎.
其中只有一人说的是真话( )是罪犯

4个人中只有一个人干了坏事,所以只有4种情况。对于答案1000,0100,0010,0001,只需要判断每个答案中有几个真话、几个假话,真话为1的那种情况就是答案。

10道刑侦科推理试题

在这个问题中,最重要的信息是:单选题,如果忽略了这个条件,就会得到多个答案
10个问题,每个问题4种答案,总共有$4{10}=2{20}$种备选答案,验证每个答案是否自冾即可。

def get_ans_of(ans, i):
    return (ans >> ((i - 1) * 2)) & 3


def only_one(ans_list, my_ans):
    return len(ans_list) == 1 and ans_list[0] == my_ans


def one(ans):
    return True


def two(ans):
    five_ans = get_ans_of(ans, 5)
    second_ans = get_ans_of(ans, 2)
    return five_ans == (2, 3, 0, 1)[second_ans]


def three(ans):
    def diff_from_other(ans_list, i):
        for j in range(len(ans_list)):
            if j != i:
                if ans_list[i] == ans_list[j]:
                    return False
        return True

    ans_group = [get_ans_of(ans, i) for i in (3, 6, 2, 4)]
    real_ans = []
    for i in range(4):
        if diff_from_other(ans_group, i):
            real_ans.append(i)
    return only_one(real_ans, get_ans_of(ans, 3))


def four(ans):
    same_group = ((1, 5), (2, 7), (1, 9,), (6, 10))
    real_ans = []
    for i in range(4):
        if get_ans_of(ans, same_group[i][0]) == get_ans_of(ans, same_group[i][1]):
            real_ans.append(i)
    return only_one(real_ans, get_ans_of(ans, 4))


def five(ans):
    options = (8, 4, 9, 7)
    real_ans = []
    for i in range(4):
        if get_ans_of(ans, options[i]) == get_ans_of(ans, 5):
            real_ans.append(i)
    return only_one(real_ans, get_ans_of(ans, 5))


def six(ans):
    group = ((2, 4,), (1, 6), (3, 10), (5, 9))
    real_ans = []
    for i in range(len(group)):
        x, y = group[i]
        x_ans = get_ans_of(ans, x)
        y_ans = get_ans_of(ans, y)
        if x_ans == get_ans_of(ans, 8) and y_ans == get_ans_of(ans, 8):
            real_ans.append(i)
    return only_one(real_ans, get_ans_of(ans, 6))


def seven(ans):
    cnt = [0] * 4
    for i in range(1, 11):
        cnt[get_ans_of(ans, i)] += 1
    mi = 0
    for i in range(4):
        if cnt[i] < cnt[mi]:
            mi = i
    mi_count = 0
    for i in range(4):
        if cnt[i] == cnt[mi]:
            mi_count += 1
    if mi_count != 1:
        return False
    return mi == (2, 1, 0, 3)[get_ans_of(ans, 7)]


def eight(ans):
    options = (7, 5, 2, 10)
    real_ans = []
    for i in range(4):
        if abs(get_ans_of(ans, options[i]) - get_ans_of(ans, 1)) != 1:
            real_ans.append(i)
    return only_one(real_ans, get_ans_of(ans, 8))


def nine(ans):
    one_six_same = get_ans_of(ans, 1) == get_ans_of(ans, 6)
    options = [6, 10, 2, 9]
    real_ans = []
    for i in range(4):
        x5_same = get_ans_of(ans, options[i]) == get_ans_of(ans, 5)
        if x5_same != one_six_same:
            real_ans.append(i)
    return only_one(real_ans, get_ans_of(ans, 9))


def ten(ans):
    cnt = [0] * 4
    for i in range(1, 11):
        cnt[get_ans_of(ans, i)] += 1
    real_ans = max(cnt) - min(cnt)
    return real_ans == (3, 2, 4, 1)[get_ans_of(ans, 10)]


def tos(ans):
    s = []
    for i in range(1, 11):
        s.append("ABCD"[get_ans_of(ans, i)])
    return ''.join(s)


def judge(ans):
    for i in (one, two, three, four, five, six, seven, eight, nine, ten):
        if not i(ans):
            return False
    return True


def main():
    for i in range(4 ** 10):
        if judge(i):
            print(tos(i))


if __name__ == '__main__':
    main()

答案是:BCACA CDABA

有些时候,一些常用函数写的尽量简洁,会影响人的编程思维。比如上面问题中,如果把get_ans函数尽量简化,则能够少定义许多变量

def a(*i):
    ret = list(map(lambda x: (ans >> ((x - 1) * 2)) & 3, i))
    return ret[0] if len(i) == 1 else ret


def only_one(ans_list, my_ans):
    return len(ans_list) == 1 and ans_list[0] == my_ans


def one():
    return True


def two():
    return a(5) == (2, 3, 0, 1)[a(2)]


def three():
    def diff_from_other(ans_list, i):
        for j in range(len(ans_list)):
            if j != i:
                if ans_list[i] == ans_list[j]:
                    return False
        return True

    ans_group = a(3, 6, 2, 4)
    real_ans = [i for i in range(4) if diff_from_other(ans_group, i)]
    return only_one(real_ans, a(3))


def four():
    same_group = ((1, 5), (2, 7), (1, 9,), (6, 10))
    real_ans = [i for i in range(4) if a(same_group[i][0]) == a(same_group[i][1])]
    return only_one(real_ans, a(4))


def five():
    options = (8, 4, 9, 7)
    real_ans = [i for i in range(4) if a(options[i]) == a(5)]
    return only_one(real_ans, a(5))


def six():
    group = ((2, 4,), (1, 6), (3, 10), (5, 9))
    real_ans = [i for i in range(len(group)) if a(group[i][0]) == a(8) and a(group[i][1]) == a(8)]
    return only_one(real_ans, a(6))


def seven():
    cnt = [0] * 4
    for i in range(1, 11):
        cnt[a(i)] += 1
    mi = 0
    for i in range(4):
        if cnt[i] < cnt[mi]:
            mi = i
    mi_count = 0
    for i in range(4):
        if cnt[i] == cnt[mi]:
            mi_count += 1
    if mi_count != 1:
        return False
    return mi == (2, 1, 0, 3)[a(7)]


def eight():
    options = (7, 5, 2, 10)
    real_ans = [i for i in range(4) if abs(a(options[i]) - a(1)) != 1]
    return only_one(real_ans, a(8))


def nine():
    options = [6, 10, 2, 9]
    real_ans = [i for i in range(4) if (a(options[i]) == a(5)) != (a(1) == a(6))]
    return only_one(real_ans, a(9))


def ten():
    cnt = [0] * 4
    for i in range(1, 11):
        cnt[a(i)] += 1
    real_ans = max(cnt) - min(cnt)
    return real_ans == (3, 2, 4, 1)[a(10)]


def tos():
    s = []
    for i in range(1, 11):
        s.append("ABCD"[a(i)])
    return ''.join(s)


def judge():
    return all(i() for i in (one, two, three, four, five, six, seven, eight, nine, ten))


ans = 0
for i in range(4 ** 10):
    ans = i
    if judge():
        print(tos())

这类题目虽然可以用暴力枚举,但是人脑却能够规划出一条最佳推理路径,能够少考虑很多种情况,快速得到答案,这跟我喜欢玩的数独游戏、竖式字谜是一个道理。
如果能够破解人类思考问题时的启发式思想,那将是机器推理的一大步!

原文地址:https://www.cnblogs.com/weiyinfu/p/8971771.html