你的算法的问题是它会有重复的递归路径。一个直接的解决方案是调整算法以使用一个集合来避免重复:
public static void solutions(int x1, int x2, int x3, int num,
Set<String> combinations){
if (x1 + x2 + x3 == num) {
combinations.add(x1 + "+" + x2 +"+" + x3);
} else if ( x1 <= num - 2 && x2 <= num - 2 && x3 <= num - 2
&& x1 <= 10 && x2 <= 10 && x3 <= 10
&& x1 + x2 + x3 < num) {
if(x1 < 10) solutions(x1 + 1, x2, x3, num, combinations);
if(x2 < 10) solutions(x1, x2 + 1, x3, num, combinations);
if(x3 < 10) solutions(x1, x2, x3 + 1, num, combinations);
}
}
但是,由于递归调用的数量,这种解决方案有点慢。例如:
num = 3 -> count = 1 duplicates found 1 Time taken 0.04(s)
num = 4 -> count = 3 duplicates found 3 Time taken 0.0(s)
num = 5 -> count = 6 duplicates found 9 Time taken 0.0(s)
...
num = 10 -> count = 36 duplicates found 2187 Time taken 0.004(s)
...
num = 20 -> count = 63 duplicates found 118569594 Time taken 5.739(s)
...
查看找到的重复的数量,可以看到有相当数量的无用递归调用导致开销。尽管如此,我们可以进一步限制递归调用的数量。我们只需要调用:
solutions(x1, x2 + 1, x3, num, combinations);
如果(x1
此外,不需要对x3 进行递归调用,可以使用公式x3 = num - x2 - x1 推导出其值。因此优化后的版本如下所示:
public static void solutions(int x1, int x2, int x3, int num, Set<String> combinations){
if(x1 <= 10 && x2 <= 10 && (num - x2 - x1) <= 10) {
combinations.add(x1 + "+" + x2 + "+" + (num - x2 - x1));
}
if ( x1 <= num - 2 && x2 <= num - 2 && x3 <= num - 2 && x1 + x2 + x3 < num) {
if(x1 < 10) solutions(x1 + 1, x2, x3, num, combinations);
if(x2 < 10 && x1 <= x2) solutions(x1, x2 + 1, x3, num, combinations);
}
}
结果:
num = 3 -> count = 1 duplicates found 0 Time taken 0.045(s)
num = 4 -> count = 3 duplicates found 0 Time taken 0.0(s)
num = 5 -> count = 6 duplicates found 0 Time taken 0.0(s)
...
num = 10 -> count = 36 duplicates found 65 Time taken 0.001(s)
...
num = 20 -> count = 63 duplicates found 21670 Time taken 0.018(s)
....
对于num = 20,大约5471x 更少的递归调用。在我的机器上,它从 5.739 秒减少到了 0.018 秒。如果找到更多可以限制递归调用次数的表达式,那么算法会快得多。理想情况下,找到可以将重复次数减少到 0 的表达式,从而消除对集合的需求。
很多更好的解决方案:
在对该主题进行了一些研究之后,我推导出了一个算法(基于 here 提出的解决方案),它使用上述表达式将重复的数量减少到 0,因此无需使用set。基本上,我重新排列和更改了之前解决方案的表达式及其逻辑,并应用了去除x3的递归调用的优化,以减少递归调用的总数,.即从16779 到2007 的整体递归调用(与set 的优化版本中的328991 相比)。
代码如下:
private static int solutions2(int x1, int x2, int x3, int num){
int count = 0;
if (x2 < Math.min(10, num - x1)) count = solutions2(x1, x2 + 1, 1,num);
else if (x1 < Math.min(10, num)) count = solutions2(x1 + 1, 1, 1, num);
if(x1 <= 10 && x2 <= 10) {
int x3_tmp = num - x2 - x1;
if(0 < x3_tmp && x3_tmp <= 10) {
System.out.println(x1 + "+" + x2 +"+" + x3);
return 1 + count;
}
}
return count;
}
一个运行的例子:
class Example {
private static int solutions(int x1, int x2, int num){
int count = 0;
if (x2 < Math.min(10, num - x1)) count = solutions(x1, x2 + 1, num);
else if (x1 < Math.min(10, num)) count = solutions(x1 + 1, 1, num);
if(x1 <= 10 && x2 <= 10) {
int x3_tmp = num - x2 - x1;
if(0 < x3_tmp && x3_tmp <= 10) {
System.out.println(x1 + "+" + x2 + "+" + x3_tmp);
return 1 + count;
}
}
return count;
}
public static int solutions(int num){
return (num < 3 || num > 30) ? 0 : solutions(1,1, num);
}
public static void main(String[] args){
int count = solutions(5);
System.out.println("Count = " +count);
}
}
你会得到输出:
3+1+1
2+2+1
2+1+2
1+3+1
1+2+2
1+1+3
Count : 6