【问题标题】:Find 3 numbers that are sum up to a given number找到 3 个和给定数字相加的数字
【发布时间】:2021-02-27 05:11:13
【问题描述】:

我需要创建一个递归方法,它以一个数字作为输入(范围从 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 个。

【问题讨论】:

    标签: java algorithm performance recursion


    【解决方案1】:

    你的算法的问题是它会有重复的递归路径。一个直接的解决方案是调整算法以使用一个集合来避免重复:

    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的递归调用的优化,以减少递归调用的总数,.即从167792007 的整体递归调用(与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
    

    【讨论】:

      猜你喜欢
      • 2021-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多