![](/img/trans.png)
[英]Finding a combination of numbers of size N that sum up to a given number
[英]Find 3 numbers that are sum up to a given number
我需要創建一個遞歸方法,它以一個數字作為輸入(范圍從 3 到 30,不包括),並找到添加三個自然數(范圍從 1 到 10,包括)的所有組合,以便它們等於這個數字。
例如,如果輸入是5
,我需要找到所有6
組合: "1+1+3"
、 "1+2+2"
、 "1+3+1"
、 "2+1+2"
, "2+2+1"
和"3+1+1"
。
該方法應該打印選項,並返回選項的數量。 到目前為止,這是我的代碼:
public static int solutions(int num)
{
if(num < 3 || num >30)
return 0;
return solutions(1,1,1, num);
}
private static int solutions(int x1, int x2, int x3, int num)
{
if(x1 > 10 || x2 > 10 || x3 > 10)
return 0;
if (x1+x2+x3 != num)
{
return solutions(x1 + 1, x2, x3, num)+
solutions(x1, x2 + 1, x3, num)+
solutions(x1, x2, x3 + 1, num);
}
else
{
System.out.println(x1 + "+" + x2 +"+" + x3);
return 1;
}
}
這段代碼給了我太多答案(都是正確但多余的),例如 5 它給了我 9 個答案而不是 6 個。
您的算法的問題在於它將具有重復的遞歸路徑。 一個直接的解決方案是調整算法以使用一個集合來避免重復:
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 <= x2)。
此外,不需要對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
的表達式,從而消除對集合的需求。
一個更好的解決方案:
在對該主題進行了一些研究之后,我推導出了一個算法(基於此處提供的解決方案),它使用上述表達式將重復的數量減少到 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);
}
}
你會得到 output:
3+1+1
2+2+1
2+1+2
1+3+1
1+2+2
1+1+3
Count : 6
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.