判断一个正整数是否可以由连续正整数求和而来

58同城面试遇到的问题,判断一个正整数n是否可以由连续正整数求和而来。我没回答上来,面试官给了提示,回来自己想想,觉得有两种方法:

  1. 模拟法:用一个在数轴上的滑动窗(即一个正整数区间),来模拟求解过程。具体来说,如果滑动窗内整数的和小于n,则滑动窗右边界向右移动,如果滑动窗内整数的和大于n,则滑动窗左边界向右移动,如果滑动窗内整数的和等于n,则n可以由滑动窗内的整数求和表示,程序终止。
  2. 数学分析法:可以证明(证明在本文最后),如果n可以表示为:n=(2*m+1)*2k, m, k>=0; 则m>0时,n可以由连续正整数求和而来,m=0时,n不可以由连续正整数求和而来。实际上,正整数中,只有2的各次方不可以由连续正整数求和而来,如n=1,2,4,8,16,32... ;另一方面,只要n含有大于1的奇数因子,都可以由连续正整数求和而来。

下面是代码:

View Code
 1 #include <iostream>
 2 using namespace std;
 3 
 4 //模拟法
 5 bool valid(int n){
 6     if(n<=1) return false;
 7     //定义滑动窗的参数,first: 滑动窗左边界,last: 滑动窗右边界,sum: 滑动窗内整数的和
 8     int first, last, sum;
 9     //参数初始化
10     first=last=sum=1;
11     do{
12         //n>sum, 则滑动窗右边界向右移动
13         if(n>sum)    {
14             last++;
15             sum+=last;
16         }
17         //n<sum, 则滑动窗左边界向右移动
18         else if(n<sum) {
19             sum-=first;
20             first++;
21         }
22         //n==sum, 循环结束,返回true
23         else
24             return true;
25     }
26     while(first!=last); //如果滑动窗只含一个整数,循环结束,返回false
27     return false;
28 }
29 
30 //数学分析法
31 bool valid2(int n){
32     while(n%2==0) n/=2;
33     return n>1;
34 }
35 
36 int main()
37 {
38     for(int i=1;i<=1000;i++)
39         if(!valid(i))
40             cout<<i<<endl;
41     for(int i=1;i<=1000;i++)
42         if(!valid2(i))
43             cout<<i<<endl;
44     return 0;
45 }

程序的运行结果就不贴出来了,有兴趣的话,可以自己试试。下面来证明前面提到的一个结论:如果n可以表示为:n=(2*m+1)*2k, m, k>=0; 则m>0时,n可以由连续正整数求和而来,m=0时,n不可以由连续正整数求和而来。
证明:假设n可以由正整数区间[k1,k2],k2>k1>0内的整数求和而来,即

  • n=(k1+k2)(k2-k1+1)/2=(2*m+1)*2k, m, k>=0, k2>=k1>0; 即
  • (k1+k2)(k2-k1+1)=(2*m+1)*2k+1

容易证明,(k1+k2)和(k2-k1+1)奇偶性不一致,即一奇一偶。如果(k1+k2)为奇数,可以有(m=0时,只有)

  • k1+k2=2*m+1;
  • k2-k1+1=2k+1;

求得k1=m+1-2k;k2=m+2k;同理如果(k2-k1+1)为奇数,可以有m=0时,只有

  • k2-k1+1=2*m+1;
  • k1+k2=2k+1;

求得k1=2k-m;k2=m+2k;
看上去可以得到两个区间[2k-m, m+2k]和[m+1-2k, m+2k](实际上m>0时,最后得到的区间可能更多),但是就这两个区间区间而言,不会同时有效。
来看看最后的结论,m=0时,两个区间都无效,说明此时,n不可以由正整数区间[k1,k2],k2>k1>0内的整数求和而来。
0<m<2k 时,[2k-m, m+2k]有效;m>=2k 时,[m+1-2k, m+2k]有效。即m>0, n至少可以由一个正整数区间[k1,k2],k2>k1>0内的整数求和而来。
证明结束!


最后,不足之处,不吝赐教!

原文地址:https://www.cnblogs.com/emituofo/p/2770193.html