前言

老师在纸上写了两个正整数,
把它们的和告诉了小红,
把它们的积告诉了小明。 老师说:这两个正整数可能相等也可能不相等。
1. 小明说:我不知道这两个数是多少。
2. 小红说:我也不知道这两个数是多少。
3. 小明说:我现在知道了。 请问这两个数到底是多少?
原题如上,是在某个b站up主的视频里看到的
我是感觉这题非常适合写成代码让计算机来算
说干就干,用java8实现了一个基础草率版本的算法
折腾
先简单还原一下现场过程
小明得到4,可以拆分出 1 x 4 或者 2 x 2
这里可能出现的答案明显不唯一
故问1, 小明回答,不知道
假设这里能通过,小明得到的只能是素数
比如小明拿到3 必然马上拆分出是 1 x 3
这里没通过,也说明小明必然没拿到素数
小红得到5,可以拆分出 1 + 4 或者 2 + 3
套娃小明分析得出 1 x 4 = 4 , 2 x 3 = 6
4和6都不是素数,都存在可能,答案明显不唯一
故问2, 小红回答,不知道
最后小明个zz回答知道了
这只存在一种可能,就是小明套娃小红套娃小明后,所有的可能性里,只存在一个分支,是小红不知道,其余小红必知道。
回到数学上,刚才说
小明是4,分析自己可以是 1 x 4 或者 2 x 2
套娃小红后 小红只能是 1 + 4 = 5 或者 2 + 2 = 4
如果小红是 5
可以分析成 1 + 4 = 5 或者 2 + 3 = 5
套娃小明后得出 1 x 4 = 4 或者 2 x 3 = 6
如果小红是 4
可以分析成 1 + 3 = 4 或者 2 + 2 = 4
套娃小明后得出 1 x 3 = 3 或者 2 x 2 = 4
这种情况是绝对不可能出现的,因为如果小明是3
第一回合,小明必知道自己是 1 x 3 或者 3 x 1
所以如果小红是4,第二回合能直接排除掉1 和 3,只能是2 和 2
小红说不知道,所以小红也必不可能是 4
那么小红只能是5, 1 + 4 = 5 或者 4 + 1 = 5
小明个zz就知道了
说的有点啰嗦,反正就是这个意思,下面上代码
先上个基础款
package com.company;/*** 原题* 老师在纸上写了两个正整数,* 把它们的和告诉了小红,* 把它们的积告诉了小明。* 老师说:这两个正整数可能相等也可能不相等。* 1. 小明说:我不知道这两个数是多少。* 2. 小红说:我也不知道这两个数是多少。* 3. 小明说:我现在知道了。* 请问这两个数到底是多少?** 思路* 结合 1,说明小明得到的数字 分解成 x * y 可能性不唯一* 结合 2,说明小红得到的数字 分解成 x + y 再结合成 x * y 结果依旧不唯一* 结合 3,说明小明得到的数字 分解成 x * y 再结合成 x + y 可以得到多个结果,但只有一个小红不能唯一,其余结果小红在2中必能解开*/public class Main {/*** 判断是否为素数*/public static boolean isPrime(int number) {for (int i = 2; i < number; i++) {if (number % i != 0) {continue;}return false;}return true;}/*** 排除不合格的数字b* 拆解b 后模拟乘 不为素数的可能性必须大于2*/public static boolean checkNumberB(int number) {int count = 0;for (int i = 1; i <= number / 2; i++) {int x = i;int y = number - i;if(!isPrime(x * y)){count ++;}}return count < 2;}/*** 能进入这里说明不是素数* 拆解a 后模拟加*/public static boolean checkNumberA(int number) {int count = 0;for (int i = 1; i <= Math.sqrt(number); i++) {// 能整除if(number % i == 0){int x = i;int y = number / i;// 核心就是这句话 套娃if(checkNumberB(x + y)){count ++;}}}// a这个数字拆分完 模拟完 只允许出现一个能通过的可能性 否则都不符合问题3return count != 1;}/*** 核心算法*/public static void core() {for (int i = 1; i < 100; i++) {for (int j = 1; j < 100; j++) {int a = i * j;int b = i + j;// 对应问题1 a 不能是素数if (isPrime(a)) {continue;}// 对应问题2 b 拆分后模拟 i*j所有可能性能通过验算if(checkNumberB(b)){continue;}// 对应问题3 略if(checkNumberA(a)){continue;}// 上面3个逻辑排除完 还没被排除的就是正确答案 i 和 j 就是那2个数,a 是积,b 是和System.out.println("i = " + i + " , j = " + j + " , a = " + a + " , b = " + b);}}}public static void main(String[] args) {core();}}
运行结果如下
i = 1 , j = 4 , a = 4 , b = 5i = 4 , j = 1 , a = 4 , b = 5
上述代码已得到正确答案,但我设置的范围是100以内,如果数字设置过大,运算复杂度高,会非常缓慢。当然我也能估计出来,可以用数学的方法,来证明只存在这一组答案,不需要计算大范围。但我还是想锻炼一下我的AMD 4800H CPU,所以这里再加上多线程试试
代码如下
package com.company;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 原题* 老师在纸上写了两个正整数,* 把它们的和告诉了小红,* 把它们的积告诉了小明。* 老师说:这两个正整数可能相等也可能不相等。* 1. 小明说:我不知道这两个数是多少。* 2. 小红说:我也不知道这两个数是多少。* 3. 小明说:我现在知道了。* 请问这两个数到底是多少?* <p>* 思路* 结合 1,说明小明得到的数字 分解成 x * y 可能性不唯一* 结合 2,说明小红得到的数字 分解成 x + y 再结合成 x * y 结果依旧不唯一* 结合 3,说明小明得到的数字 分解成 x * y 再结合成 x + y 可以得到多个结果,但只有一个小红不能唯一,其余结果小红在2中必能解开*/public class Main {/*** 判断是否为素数*/public static boolean isPrime(int number) {for (int i = 2; i < number; i++) {if (number % i != 0) {continue;}return false;}return true;}/*** 排除不合格的数字b* 拆解b 后模拟乘 不为素数的可能性必须大于2*/public static boolean checkNumberB(int number) {int count = 0;for (int i = 1; i <= number / 2; i++) {int x = i;int y = number - i;if (!isPrime(x * y)) {count++;}}return count < 2;}/*** 能进入这里说明不是素数* 拆解a 后模拟加*/public static boolean checkNumberA(int number) {int count = 0;for (int i = 1; i <= Math.sqrt(number); i++) {// 能整除if (number % i == 0) {int x = i;int y = number / i;// 核心就是这句话 套娃if (checkNumberB(x + y)) {count++;}}}// 只能存在一个通过套娃 checkNumberB 的结果return count != 1;}/*** 核心算法*/@SuppressWarnings("AlibabaThreadPoolCreation")public void core(int nThreads) {ExecutorService executorService = Executors.newFixedThreadPool(nThreads);for (int i = 1; i < 1000; i++) {for (int j = 1; j < 1000; j++) {executorService.submit(new CalcThread(i,j));}}executorService.shutdown();}public static void main(String[] args) {// 开200个线程去跑new Main().core(200);}class CalcThread extends Thread {private int i;private int j;public CalcThread(int i , int j){this.i = i;this.j = j;}@Overridepublic void run() {int a = i * j;int b = i + j;// 对应问题1 a 不能是素数if (isPrime(a)) {return;}// 对应问题2 b 拆分后模拟 i*j所有可能性能通过验算if (checkNumberB(b)) {return;}// 对应问题3 略if (checkNumberA(a)) {return;}// 上面3个逻辑排除完 还没被排除的就是正确答案 i 和 j 就是那2个数,a 是积,b 是和System.out.println("i = " + i + " , j = " + j + " , a = " + a + " , b = " + b);}}}
注意我这里开200个线程还能正常操作,如果你的电脑CPU不太能经受得起锻炼,少开点线程试试水,开个16线程足以
运行结果如下
i = 1 , j = 4 , a = 4 , b = 5i = 4 , j = 1 , a = 4 , b = 5

最后运行结果还是意料之中的,但cpu曲线图异常的优美,另外温度也很感人。速度的话远不及预期,可能是因为数字越大,越卷。。。
END