一个整数可以由其他若干个连续整数的和表示(java)

题目的具体描述:
一个整数N,他可能等于比它小的若干个整数(大于2)相加。如果存在这样的连续整数,将他们输出,如果没有则不输出。
例: 整数18,18=5+6+7=3+4+5+6。所以输出[5 6 7],[3 4 5 6]。

这是一个公司给出的笔试编程题,当时我没做出来,当时一直在想总结规律,写出效率很高的代码,结果最后连一个可行方案都写出来。值得反思,当时时间有限,应该先写一个可行方案,在去考虑优化的,当时没有编译器进行调试,也不好测试优化的结果。

后面花了两个小时,终于把优化后的算法写出来了。没有网上查资料,完全自己思考,因为题目本身并不难。

public class ArithmeticSquence {

    public static void main(String args[]){
        long startTime = System.currentTimeMillis();//获取当前时间
        arithmetic_squence(100000000);
        long endTime = System.currentTimeMillis();//获取当前时间
        System.out.println("程序运行时间:"+(endTime-startTime)+"ms");
    }

    public static void arithmetic_squence(int n){

        int comma=0;//用来作为记录有多少组连续整数,也用来在输出连续整数时添加逗号的标记
        for(int i=2;i<=n/2+1;i++){//i表示该整数由i个连续整数相加

            int value=n/i-i/2;//当该整数由i个连续整数相加时,这些连续整数的最小值的表达式(不是特别精确)

            if (value<0)//当计算到value<0时,说明此时已经到边缘了,不能再有其他组合了
                break;
            else if(value<1)//0<=value<1时,这时候刚好到边缘,是最后有可能的组合
                value=1;


            for(int j=value;j<=n/i+1;j++){//在可能的范围内循环判断可能出现的组合
                int temp=(j+j+i-1)*i/2;//等差数列,首相加末项乘以项数除以2
                if(temp>n)//如果大于0,那就说明这里的组合不可能满足要求了
                    break;
                else if(temp==n){//成功找到连续整数

                    //注释部分是为了在控制台输出连续整数
//                  if(comma!=0)
//                      System.out.print(",");
//                  
//                  System.out.print("[");
//                  
//                  for(int k=0;k<i;k++){
//                      if(k!=0)
//                          System.out.print(" ");
//                      System.out.print(j+k);
//                  }
//                  System.out.print("]");

                    comma++;//记录有多少组连续整数组合
                    break;
                }

            }   
        }
        System.out.println(comma);
    }
}

对于整个算法的严整型,我只验算了前20个数字,我也不会去证明,如果有bug,请指出。

说说整个思路

1、连续整数,就可以理解为公差为1的等差数列。然后用求和公式去验证。

2、对于整数N,假如它等于两个连续整数相加,那么这两个整数,是不是就是N/2附近的两个数,这个好理解把。那么如果等于3个连续整数相加,那么是不是就是N/3附近的三个数。如果它等于N/2(向下取整,java里默认是这样的)个整数相加时,那么N/(N/2)=2,也就是2附近的数,那2附近的数其实就是1和3,也就是说,N=N/2个整数相加已经是一种极限的了(我自己理解的,至于有没有特例,不敢肯定)。
所以第一个for循环里i的判断条件就可以求得,再加1是因为java里对int型整除是默认向下取整的,并不是四舍五入,为保证不漏掉极限的那种情况,就加一个1。

3、第二个for循环,举个例子理解。对于整数N=18,假设由两个连续整数相加,那么就是18/2=9,9周围有两种组合,8+9=17<18,9+10=19>18。所以18不能由两个连续整数相加,假设由3个整数相加,18/3=6,那么取6附近的数有4,5,6,7,8。4+5+6=15,5+6+7=18,6+7+8=21。其实这里你就可以看到,当一个整数N等于其他n个连续整数相加时,它一定是由N/n附近的整数组成,而且应该一定包含N/n(取整)这个整数(没有证明过,所以不敢说肯定),并且至少包含这个整数左边或者右边的一位数。所以int value=n/i-i/2,value就是这样得来的(这里更缩小为i/2,觉得有问题可以改成value=n/i-i+1,并且注意这里的n不等于括号外面那个n,而是等于N),并且value取的是可能范围里最小的那个数,然后从最小值一直往上加n个数,看看是否会等于N,如果某个时候相加已经大于N了,那么说明不存在n个连续整数相加等于N。

4、if (value<0) break;代码里有这么两行,这两行提高了整个代码很多的效率。可以去掉这里,仅保留if(value<1) value=1试试,效率完全不在一个等级。因为此时假设整数N等于i个连续整数相加,而这时候可能范围的最小值已经小于0< N/i<1了,此时还是可以满足包含N/i这个整数并且至少包含它左边或者右边的一个整数(此时只能选右边)。那么如果N等于i+1个连续整数相加时,value的值只会更小,而且i+1>3,其实这时候必须包含N/i左边以及右边的两个整数,那么显然不可能满足了(这里0不能算)。

主要的优化思路就在上面了。再说明一次,上面算法只经过实践证明,没有严格的理论证明,不敢保证一定没有bug。

对于整数100000000(一亿),有8个连续整数组合相加等于它,只需要5ms(不输出到控制台,和机器有关,我电脑配置很一般,约3500元的组装机)。首先,数字越大,需要循环的次数也就越多,一亿已经够大了,5ms这个效率,应该可以接受了吧。

~~~~~~~~~~~~~~~~~~~~~~~
一下内容是网上查资料学习到的:
对于int value=n/i-i/2。
假设一个整数可以由连续整数相加求得。那么有N=(value+value+i-1)*i/2(value代表连续整数中最小的那个),那么value=N/i-(i-1)/2,这个value的公式会更严谨,所以上面也改成这个更好。

改进后代码

public class test2 {

    public static void main(String args[]){
        long startTime = System.currentTimeMillis();//获取当前时间
        arithmetic_squence(20);
        long endTime = System.currentTimeMillis();//获取当前时间
        System.out.println("程序运行时间:"+(endTime-startTime)+"ms");
    }

    public static void arithmetic_squence(int n){

        int comma=0;//用来作为记录有多少组连续整数,也用来在输出连续整数时添加逗号的标记
        for(int i=2;i<=n/2+1;i++){//i表示该整数由i个连续整数相加

            int value=n/i-(i-1)/2;//当该整数由i个连续整数相加时,这些连续整数的最小值的表达式

            if(value<1)//最小取1
                break;

                int temp=(value+value+i-1)*i/2;//等差数列,首相加末项乘以项数除以2

                if(temp==n){//成功找到连续整数

                    //注释部分是为了在控制台输出连续整数
//                  if(comma!=0)
//                      System.out.print(",");
//                  
//                  System.out.print("[");
//                  
//                  for(int k=0;k<i;k++){
//                      if(k!=0)
//                          System.out.print(" ");
//                      System.out.print(value+k);
//                  }
//                  System.out.print("]");

                    comma++;//记录有多少组连续整数组
            }   
        }
        System.out.println(comma);
    }

}

果然,算法优化无止尽。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章