已知两个自然数a,b,满足a>=2,b>=2。现有甲乙两人,甲知道a+b的值,乙知道a*b的值。
(1)甲对乙说:我不知道这两个数,但是我知道你也不知道;
(2)乙対甲说:我本来不知道,但是我现在知道了;
(3)甲说:我也知道了。
要求:
1,求出一对这样的值;
2,这样的值有多对,用代码求出多组这样的值。
思考:
试想,如果甲知道的数字是4,那么甲,乙均知道这两个数:a=b=2;
如果甲知道的数字是5,那么甲知道这两个数,分别是2,3,第(1)句话不成立;
如果甲知道的数字是6,那么甲不知道这两个数,因为这两个数可能是2,4或3,3,但是如果是2,4,那么乙知道的数是8,乙知道这两个数是2,4;如果是3,3,那么乙知道的数字是9,乙也知道,这两个数均是3,故第(1)句不成立;
如果甲知道的数字是7,那么甲不知道这两个数,可能是2,5或3,4;若是2,5,乙知道的是10,那么乙也知道这两个数,第(1)句不成立;
如果甲知道的数字是8,那么甲不知道这两个数,可能是2,6或3,5或4,4;若是3,5,第(1)句不成立;
如果甲知道的数字是9,那么甲不知道这两个数,可能是2,7或3,6或4,5;若是2,7,第(1)句不成立;
如果甲知道的数字是10,那么可能是2,8或3,7或4,6或5,5;若是3,7或5,5,第(1)句不成立;
如果甲知道的数字是11,那么可能是2,9或3,8或4,7或5,6;若是2,9,乙知道的是18,则乙无法确定这两个数是2,9还是3,6,即乙无法确定;若是3,8,乙知道的数是24,乙无法确定是3,8还是4,6还是2,12,即乙无法确定;若是4,7,则乙知道的数是28,乙无法确定是2,14还是4,7,即已无法确定;若是5,6,则乙知道的数是30,已无法确定是2,15还是3,10还是5,6,即乙无法确定。故第(1)句满足;
然后看第(2)句,乙在听到甲的话后,乙就知道了,甲的话直接排除了上面这些可能,乙由此知道了这两个值,乙如果拿的是18,则在乙看来,可能是2,9或3,6,若是2,9,则甲确实不知道,甲也可以推出乙不知道,若是3,6,甲手里是9,则甲没办法知道乙不知道,故3,6不符合,所以,乙此时可以知道是2,9,即2,9也满足第(2)句;乙如果拿的是24,对于乙来说可能是2,12或3,8或4,6,若是2,12,甲拿的是14,14可能是2,12或3,11或4,10或5,9或6,8或7,7,如果是3,11或7,7,则甲没办法确定乙一定不知道,不满足第(1)句;若是3,8,则甲手里是11,满足前两句;若是4,6,则甲手里是10,由上面分析可知,甲如果拿的是10,则不能断定乙不知道这两个数,故此时,乙确定不是4,6;故弱乙手里是24,则乙可以推断出是3,8,即3,8也满足第二句;乙如果拿的是28,则乙不确定是2,14还是4,7,若是2,14,则甲手里是16,16=3+13的时候甲不确定乙不知道,故不是2,14,若是4,7,甲手里是11,故此时乙也可以推断出是4,7,即4,7也满足第二句;若是30,则乙不确定是2,15还是3,10还是5,6,若是2,15,则甲是17,满足,若是3,10,则甲是13,13=2+11不满足第一句,若是5,6,甲是11,也满足,故乙在听到甲的话后任然无法确实是2,15还是5,6,所以乙手里不可能是30;
由上分析,可能的数值组合有
(2,9),甲:11;乙:18
(3,8),甲:11;乙:24
(4,7),甲:11;乙:28
然后看第三句,甲听了乙的话后,甲也知道了,所以甲手里不能是11,因为此时甲任然无法确定是上面三个中的哪一个,故上面没有正解。
由上归纳总结:
假设X=a+b,Y=ab
条件A:已知常数X,任取m,n满足m+n=X,至少另外一对(m‘,n’)满足m‘>=2,n’>=2, m‘n’ = mn, m!=m’, n!=n’;换种表述,任取m,n满足m+n=X,m,n不全为质数(例如3,5,乙如果知道两数之积是15,就能知道这两个数),且m,n中如果有一个是质数,则另一个数不能是该质数的平方(例如3,9,乙如果知道两数之积是27,就能知道这两数);这两种表述,对应的是两种不同的算法,分别对应下面代码中situation11和situation12两个方法。
条件B:已知常数Y,所有满足mn=Y的(m,n)数对不止一对,但是这些数对中,P=m+n,满足条件A的常量P只有一个;
条件C:已知常量X,所有满足m+n=X的数对(m,n),取Y=m+n满足条件B的数对(m,n)只有一组。
同时满足上面三个条件的(m,n)即为最终待求数对。
先贴结果:100以内的这样数组有三组:
(4,13),(4,61),(16,73)
200以内这样的数有9组,1000以内这样的数有36组。。。
代码:
public class SpecialPair {
public static void main(String[] args) {
List<Integer> origin = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 4; i < 1000; i++) {
origin.add(i);
}
List<Integer> firstResult = firstFilter(origin);
System.out.println("firstResult = " + firstResult);
LinkedHashMap<Integer, List<SortedPair>> secondResult = secondFilter(firstResult);
System.out.println("secondResult = " + getHashMapString(secondResult));
List<SortedPair> thirdResult = thirdFilter(secondResult);
System.out.println("thirdResult = " + thirdResult);
System.out.println("search " + thirdResult.size() + " results in range [" + origin.get(0)
+ ", " + origin.get(origin.size() - 1) + "] cost " + (System.currentTimeMillis() - start) + " ms!");
}
private static <K, V> String getHashMapString(HashMap<K, V> map) {
if (map.size() < 1) {
return "{}";
}
StringBuilder sb = new StringBuilder().append("{");
for (K k : map.keySet()) {
sb.append("\n" + k + ": " + map.get(k));
}
sb.append("\n}");
return sb.toString();
}
/**
* 甲对乙说:我不知道,但是我知道你也不知道
*
* @param origin
* @return
*/
private static List<Integer> firstFilter(List<Integer> origin) {
List<Integer> result = new ArrayList<>();
for (int i : origin) {
if (situation12(i)) {
result.add(i);
}
}
return result;
}
/**
* 是否满足条件1
*
* @param num
* @return
*/
private static boolean situation11(int num) {
if (num < 4) {
return false;
}
int mid = num / 2;
for (int i = 2; i <= mid; i++) {
int pro = i * (num - i);
int found = 0;
int n = (int) Math.sqrt(pro);
for (int j = 2; j <= n; j++) {
if (pro % j == 0) {
found++;
}
if (found >= 2) {
break;
}
}
if (found == 1) {
return false;
}
}
return true;
}
private static boolean situation12(int num) {
if (num < 4) {
return false;
}
int mid = num / 2;
for (int i = 2; i <= mid; i++) {
if (isPrimePair(i, num - i) || isPrimeSquare(i, num - i)) {
return false;
}
}
return true;
}
/**
* 乙対甲说:我本来不知道,现在我知道了
*
* @param origin
* @return
*/
private static LinkedHashMap<Integer, List<SortedPair>> secondFilter(List<Integer> origin) {
LinkedHashMap<Integer, List<SortedPair>> result = new LinkedHashMap<>();
for (int sum : origin) {
List<SortedPair> pairs = situation2(sum);
if (pairs != null && pairs.size() > 0) {
result.put(sum, pairs);
}
}
return result;
}
private static List<SortedPair> situation2(int num) {
if (num < 4) {
return null;
}
int mid = num / 2;
List<SortedPair> pairs = new ArrayList<>();
for (int i = 2; i <= mid; i++) {
int pro = i * (num - i);
SortedPair sortedPair = checkPro(pro);
if (sortedPair != null) {
pairs.add(sortedPair);
}
}
return pairs;
}
/**
* 甲说:我也知道了
*
* @param pairs
* @return
*/
private static List<SortedPair> thirdFilter(LinkedHashMap<Integer, List<SortedPair>> pairs) {
List<SortedPair> result = new ArrayList<>();
for (int key : pairs.keySet()) {
if (pairs.get(key).size() == 1) {
result.add(pairs.get(key).get(0));
}
}
return result;
}
private static boolean isPrimePair(int a, int b) {
return isPrimeNum(a) && isPrimeNum(b);
}
private static boolean isPrimeSquare(int a, int b) {
return isPrimeNum(a) && (b == a * a)
|| isPrimeNum(b) && (b * b == a);
}
private static boolean isPrimeNum(int num) {
if (num < 2) {
return false;
}
int n = (int) Math.sqrt(num);
for (int i = 2; i <= n; i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
private static SortedPair checkPro(int num) {
if (num < 4) {
return null;
}
int n = (int) Math.sqrt(num);
SortedPair sortedPair = null;
for (int i = 2; i <= n; i++) {
if (num % i == 0) {
int j = num / i;
if (situation12(i + j)) {
if (sortedPair == null) {
sortedPair = new SortedPair(i, j);
} else if (!sortedPair.equals(new SortedPair(i, j))) {
return null;
}
}
}
}
return sortedPair;
}
static class SortedPair {
int first;
int second;
public SortedPair(int first, int second) {
this.first = first <= second ? first : second;
this.second = first <= second ? second : first;
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof SortedPair)) {
return false;
}
SortedPair pair = (SortedPair) obj;
if (this == pair) {
return true;
}
return first == pair.first && second == pair.second;
}
public int getSum() {
return first + second;
}
public int getPro() {
return first * second;
}
@Override
public String toString() {
return "SortedPair{" +
"first=" + first +
", second=" + second +
", sum=" + getSum() +
", pro=" + getPro() +
'}';
}
}
}
补充:
经某大神提示,哥德巴赫猜想在这里是可以用的。
哥德巴赫猜想:任一大于2的偶数都可写成两个质数之和。
结合上面条件A的另外一种表述,可以知道甲知道的两数之和一定不是偶数,这样在代码层面只需要遍历奇数,第一次过滤效率可以提高一倍。