这个问题真奇妙,从高中数学论坛,初中奥赛乃至小学数学网站都有收录,甚至还引起了CSDN上的讨论。故事是这样的:

一天,鬼谷子随意从2~99中选取了两个数。他把这两个数的和告诉了庞涓,把这两个数的乘积告诉了孙膑。但孙膑和庞涓彼此不知到对方得到的数。第二天,庞涓很有自 信地对孙膑说:虽然我不知到这两个数是什麽,但我知道你一定也不知道。随后,孙膑说:那我知道了。庞涓说:那我也知道了。

问题是求这两个数各是多少。

既然两人往来了三轮,那就一轮一轮地看。

Round 1

有两种方法可以解决Round 1:分析或暴力。不妨设庞涓拿到的和是S,孙膑拿到的积是P(正好和姓氏反了……)。

分析

庞涓既然不知道两个数是什么,那S必然不是4或198。所以S的范围是[5, 197]。

庞涓可以根据自己手中的S,猜出孙膑手中的P的所有可能值。设这些可能值构成了集合SET_S。既然庞涓能肯定孙膑猜不出来,那对于SET_S中的任何值,必然有不只 一种分解。(对于积,分解指因子分解;对于和,分解指加法拆分。可根据上下文判断。)

如果庞涓手里的S可以分解两个素数,那存在P恰好是这两个素数的乘积的情况,这是孙膑就能判断了,矛盾。所以S必然不能是两个素数的和。这可以推出:

  1. S不是偶数(哥德巴赫猜想)。
  2. S不是2+素数的形式。

其次,如果庞涓手里的S是一个大于53的奇数,那么有可能P = (S - 53) * 53。注意53是素数,且两个数的范围是[2, 99]。所以这个53必然是单独的一个因子,于是孙膑可以判断,矛盾。故S不能是一个大于53的奇数。

这样一筛,可以知道S的可能取值为{11,17,23,27,29,35,37,47,51,53},共10个。

暴力

所谓暴力美学就是列个表把所有可能的积算一遍,这样就知道哪些积可以有多种分解方法了。然后把[5, 197]轮一遍,对每个数都求出可能的积的集合。若这个集合是有多种分解方法的积的集合的子集就OK,否则就不OK。

Round 2

孙膑可以进行和我们上面一样的分析,得出S的10个可能值。然后他说他知道了。这说明P的所有分解的和中,有且只有一个在那10个S的可能值里。

这个Round我们暂时不做任何分析,留到Round 3。

Round 3

庞涓听孙膑说他知道了之后自己也知道了,那说明S的所有分解算出的积中,有且只有一个不在其他9个S的可能值的分解算出的积中。(同时在S的分解的积中和其他9个S的 可能值的分解的积中出现的积必定不是P,否则孙膑在Round 2中无法确定。若S的分解算出的积中有多于一个不在其他9个S的可能值的分解算出的积中,则庞涓在Round 3中无法确定。)

于是我们把这10个S的可能值中的每一个对应的积都算出来,这就是10个集合。然后看哪个集合恰好有一个元素不在其余9个集合的并集里,那这个元素就是P,这个集合对 应的S的可能值就是S。

blabla说了一通,不知道说没说明白。语言在表达思想方面还是有点无力,机器翻译成别的语言之后就更不知道变成什么样子了。所以还是扔代码吧:

#!/usr/bin/python

from collections import Counter
from math import sqrt

def crange(a, b):
    return range(a, b + 1)

def get_prods(s):
    prods = set()
    for x in crange(2, s // 2):
        prods.add(x * (s - x))
    return prods

def get_sums(p):
    sums = set()
    for x in crange(2, int(sqrt(p))):
        if p % x == 0:
            sums.add(x + p / x)
    return sums

def round_one():
    prods_cnt = Counter()

    for x in crange(2, 99):
        for y in crange(x, 99):
            prods_cnt[x * y] += 1

    prods_rep = set()

    for k in prods_cnt.keys():
        if prods_cnt[k] > 1:
            prods_rep.add(k)

    sums = set()

    for s in crange(5, 197):
        prods = get_prods(s)
        ok = True
        for p in prods:
            if p not in prods_rep:
                ok = False
                break
        if ok:
            sums.add(s)

    return sums

def round_three():
    results = []

    sums = round_one()
    d = dict()
    for s in sums:
        prods = get_prods(s)
        d[s] = prods
    for s in sums:
        rest = set()
        for s2 in sums:
            if s == s2:
                continue
            rest.update(d[s2])
        if len(d[s]) - len(d[s].intersection(rest)) == 1:
            results.append([s, d[s].difference(rest).pop()])

    return results

def org_nums():
    results = round_three()
    nums = []
    for s, p in results:
        a = int((s + sqrt(s * s - 4 * p)) / 2)
        b = int((s - sqrt(s * s - 4 * p)) / 2)
        nums.append([a, b])
    return nums

for a, b in org_nums():
    print('a = {}, b = {}'.format(a, b))