• 只考加法的面试题


    问题描述:写一个程序,对于一个64位正整数,输出它所有可能的连续自然数(两个以上)之和的算式。

    例如:3=1+2; 9=5+4; 9=2+3+4。

    刚开始看到这个题目,直接想到的方法就是蛮力解决:即对于每一个输入的整数,从1开始加,一直加到等于或者大于这个整数为止。如果等于这个整数,则找到了一种连续自然数相加的模式;如果大于这个整数,则无法找到,继续从2开始加,依次类推。例如对于5来说,首先从1开始加:1+2+3=6>5,这样从1开始就无法找到,从而停止继续相加;然后从2开始相加:2+3=5,成功找到。

    这是很自然的一种想法,可以达到题目要求。但是,很显然工作量比较大,程序时间复杂度高。于是百度之,找到一种比较好的解决方法,此方法采用了数学分析(数学是个好东西啊),稍微一变换,程序就很简单了。

    思路是这样的:

    假设这个正整数n可以表示成2个以上的连续自然数之和,那么可以设这个序列为a,a+1....a+i。

    那么n=a+(a+1)+...+(a+i)=(i+1)*a + (i+1)*i/2。如果我们可以确定i的范围,那么我们就可以遍历i,对于所有的i的取值,得到对应的a的值,然后判断a是否为整数,即可知是否符合要求。这里i可以认为表示的是序列的长度,那么最长的序列必然是n=1+2...+i。所以有i*(i+1)=2n。

    解得i=(sqrt(8n+1)-1)/2,所以i的取值就是[1, (sqrt(8n+1)-1)/2]区间中。随即,遍历i的取值,然后判断对应的a是否为整数即可。

    好的,有了思路就可以开始编码了,下面是代码部分:



     

     

     

     

     

     

     

     

    我们知道:

    1+2 = 3

    4+5 = 9

    2+3+4 = 9

    等式的左边都是两个或两个以上连续的自然数相加,是不是所有的整数都可以写成这样的形式呢?

    问题1  对于一个64位正整数,输出它所有可能的连续自然数(两个以上)之和的算式。

    问题2  大家在测试上面程序的过程中,肯定会注意到有一些数字不能表达为一系列连续的自然数

    之和,例如32好像就找不到。那么,这样的数字有什么规律呢?能否证明你的结论?

    问题364位正整数范围内,子序列数目最多的数是哪一个?

    解答1. 首先分析,

    对于正整数N,如果表示成2个连续自然数相加,N = m + (m + 1) = 2m + 1,则N为奇数;

    如果表示成3个连续的自然数相加,N = (m - 1) + m + (m + 1) = 3m,则N为3的倍数;

    如果表示成4个连续的自然数相加,N = (m - 1) + m + (m + 1) + (m + 2) = 2 * (2m + 1),则N为某奇数的2倍;

    如果表示成5个连续的自然数相加,……,则N为5的倍数;

    如果表示成5个连续的自然数相加,……,则N为某奇数的3倍;

    ……

     

    已经找到规律了:对于N,

    如果表示成偶数个(2 * k) 连续的自然数相加,      则N为k的倍数;

    如果表示成奇数个(2 * k + 1)连续的自然数相加, 则N为(2 * k + 1)的倍数。

     

    这样程序就好写啦:

    [cpp] view plaincopy

    1. #include <math.h>     
    2. #include <stdio.h>     
    3. #include <stdlib.h>     
    4.     
    5. bool AddSubN(__int64 N)    
    6. {    
    7.     if(N < 3)    
    8.     {    
    9.         printf("No sequences fit N./n");    
    10. 10.         return false;    
    11. 11.     }    
    12. 12.     bool bFind = false;    
    13. 13.     int num = 0;    
    14. 14.     printf("/n %I64d ./n", N);    
    15. 15.     
    16. 16.     __int64 maxLoop = sqrt(2 * N);    
    17. 17.     //当N = 1 + 2 + ... + m = m * (m + 1) / 2时的序列长度最大,     
    18. 18.     //所以maxLoop比这个小     
    19. 19.     //注意:这里可能得用大整数开方,我懒得写了     
    20. 20.     
    21. 21.     __int64 i, j, testN;    
    22. 22.             // 看N是否能被表达成 i 个连续自然数之和     
    23. 23.     for(i = 2; i <= maxLoop; i ++)    
    24. 24.     {    
    25. 25.         if(!(i & 0x1))  //如果 i 是偶数,则N为 i/2 的倍数,且 N /(i/2) 为奇数;     
    26. 26.         {    
    27. 27.             if((!(N % (i >> 1))) && ((N / (i >> 1))) & 1)    
    28. 28.             {    
    29. 29.                 __int64 sub0 = (N / (i >> 1) - 1) / 2;    // i 个数中,中间偏左的一个     
    30. 30.                 sub0 -= i / 2 - 1;                      // i 个数中最左边的一个     
    31. 31.                 testN = 0;    
    32. 32.                 for(j = 0; j < i; j ++)    
    33. 33.                     testN += sub0 + j;    
    34. 34.                     //打印出来,测试是否跟输入的N一样     
    35. 35.                 printf("/n %I64d = ", N, testN);    
    36. 36.     
    37. 37.                     //打印连续自然数序列     
    38. 38.                 for(j = 0; j < i - 1; j ++)    
    39. 39.                     printf("%I64d + ", sub0 + j);    
    40. 40.                 printf("%I64d ./n", sub0 + j);    
    41. 41.     
    42. 42.                 if(!bFind)    
    43. 43.                     bFind = true;    
    44. 44.                 num ++;    
    45. 45.     
    46. 46.             }    
    47. 47.         }    
    48. 48.         else        // 如果 i 是奇数,则N为 i 的倍数     
    49. 49.         {    
    50. 50.             if(!(N % i))    
    51. 51.             {    
    52. 52.                 __int64 sub0 = N / i;    
    53. 53.                 sub0 -= i / 2;          //找到i个数中最左边的一个     
    54. 54.     
    55. 55.                 testN = 0;    
    56. 56.                 for(j = 0; j < i; j ++)    
    57. 57.                     testN += sub0 + j;    
    58. 58.                 //打印出来,测试是否跟输入的N一样     
    59. 59.                 printf("/n %I64d = ", N, testN);    
    60. 60.                     
    61. 61.                 //打印连续自然数序列     
    62. 62.                 for(j = 0; j < i - 1; j ++)    
    63. 63.                     printf("%I64d + ", sub0 + j);    
    64. 64.                 printf("%I64d ./n", sub0 + j);    
    65. 65.                     
    66. 66.                 if(!bFind)    
    67. 67.                     bFind = true;    
    68. 68.     
    69. 69.                 num ++;    
    70. 70.             }    
    71. 71.         }    
    72. 72.     }    
    73. 73.     if(!bFind)    
    74. 74.     {    
    75. 75.         printf("No sequences fit N./n");    
    76. 76.         return false;    
    77. 77.     }    
    78. 78.     
    79. 79.     printf("/n---------------/n %d sequences fit N found ./n", num);    
    80. 80.     
    81. 81.     return true;    

    82. }    

    1. 83.     

    84. void main()    

    85. {    

    1. 86.     __int64 N = 3;    
    2. 87.     while(N >= 1)    
    3. 88.     {    
    4. 89.         printf("Please input a int64 number. (input 0 or -1 to escape)/n");    
    5. 90.         scanf("%I64d", &N);    
    6. 91.         system("cls");    
    7. 92.         AddSubN(N);    
    8. 93.         printf("/n/n/n");    
    9. 94.         system("pause");    
    10. 95.         system("cls");    
    11. 96.     }    

    97. }  


     

    解答2. 从分析中可以看出,N的因式分解必然要有奇数。证明如下:

     a. 首先证明,只要N的因式分解中有奇数,N就能表示为自然数连和。

         如果N的因式分解中有奇数,假设为s,且N= k * s,

         如果k > s/2,

         则 N可以表示为这个序列的和:k - (s / 2), k - (s / 2) + 1 ... k + (s / 2);

         例如 54 = 6 x 9, 可表示为 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10;(6 x 9,偶数为6,则6在这9个自然数的序列的中间)

         如果 k < s/2,

         则N可以表示为这个序列的和:(s + 1)/2 - k, (s+1)/2 - k + 1,..., (s + 1)/2 + k - 1;

         如54 = 2 x 27,则可表示为 12 + 13 + 14 + 15;(中间两个数 13 + 14 = 27)

     b. 再证明:如果N的因式分解中没有奇数,则N不能表示成连和的形式。 

         反证:如果能,假设N能表示成k个自然数的连和。

         如果k为偶数,设这k个数的中数(中间偏左的一个)为m,将这k个数收尾相加得到k/2个自然数的序列,

         易证这k/2个自然数都等于2m+1,

         那么N = (2m + 1) * k / 2,与N的因式分解中没有奇数矛盾;

     

         如果k为奇数,设中数为n,那么N = k * n,与N的因式分解中没有奇数矛盾。

         综上,得证。 

     ------------------------------------------------------------------------------------------------------------

    假设N的质因子分解中有k种奇数(不是k个)记为odd[1,..,k],odd[i]有n[i]个,那么N能表示的序列数为:

        Ns = (n[0] + 1) * (n[2] + 1) * (n[3] + 1) * ... * (n[k] + 1) - 1

     

    验证:3*3*3*3*5*5*7*7*7 = 694575,我用程序打印,输出了59个。

    这里有4个3,2个5,3个7,(4+1)*(2+1)*(3+1) - 1 = 60 - 1 = 59,验证成功。而694575乘以2的任意幂次方,所得结果都是59.

    显然,拆分个数,只与奇质因子的数目有关。

    2 ^ 64 = 1.8e19

    3 * 5 *7 *11 *13 *17 * 19 *23 *29 *31 * 37 *41 * 43 *47 *53 = 1.6e19

     

    假设N是有最多因子个数的最小64位奇数,设 N = 3^a3 * 5^a5 * 7^a7 …

    则一定有 a3 >= a5 >= a7 … 否则只要交换不满足条件的那两个数,得到相同因子个数但比N更小的数,这与假设矛盾。

      S = 2 ^ 64 = 1.8e19

    M=3*5*7*11*13*17*19*23*29*31*37*41*43*47*53=1.6e19(因子个数2^15)

    因而,N的最大质因子一定小等于53

     

    由S / (M / 53) = 60  可将60拆分成3^3(因子数5*2^13)  3^2 * 5(因子数3*2^14)

    可得局部最优解:R1 = 3^3 *5^2 *7*11*13*17*19*23*29*31*37*41*43*47

    如果N不等于R1,则a47 = 0(要将S / (M / 53/47)) = 2820 拿出来拆分)

      若N包含k个质因子a, t为满足a^t > 47(显然t >= 2)的最小整数,则 k < 2*t-1

    (否则若将t个a拆分成47,由 (k+1)*1 – (k-t+1) * 2 = 2*t-k-1 <=0,

    可知拆分后得到的数更优,与N最优矛盾)。

    因此a3 <=2*4-2=6,

    a5 <= 2*3 – 2 = 4, 

    a7 <= 2*2-2 = 2

    a11 <= 2*2-2 = 2

     

    若a7 <=1, 则a3<=4,否则可以将2个3拆成1个7,得到更优解。由

    S/(3^4*5^4)/ (7*11*13*17*19*23*29*31*37*41)  = 35

    (能得到的最多因子个数为25*2^10 < 3*2^14不是最优解)

    因而 a7 = 2

     

    ( 若az = 2, ax = a, ay =b 且 z > x * y,若不能将 z拆分成 x * y,则有

       (a+1)*(b+1)*3 > (a+2)*(b+2)*2,即 (a-1)*(b-1) >= 7 )

     

    若a23=2则可将1个23拆成3和7,由 (1+a3)*3*3 – (1+a3+1)*4*2 = a3-7<0

    可知得到的数更优,与假设矛盾,因而 a23<=1,

    由于 S/(3^6*5^4)/(7*11*13*17*19)^2 = 387 > 23因而 一定含有因子23,a23 = 1

     

    若a31=0,则 a5 = 2(否则,5*7合并成31,得到更优解)

    由 2^64 / (3^6*(5*7*11*13*17*19)^2 * 23 * 29) = 14

    可知,该情况下得到的最大数不是最优, 因而 a31 = 1

     

    (若a17 =2则 a3>=5, a5=3 或 a3>=4 a5=4,否则可以将17拆分成3*5)

     

    利用前面的结论,

      a3 >= a5 >= a7 …

      a3 <= 6  a5 <= 4  a7 = 2  a23 = 1  a31 = 1  a47 = 0

    可在较短时间内搜索出满足上述条件的因子个数最多的奇数,再与局部最优解R1进行比较,就可以确定最优解。

  • 相关阅读:
    wpf.xaml.behavior
    为你的硬件自动化统一构建root和firmware
    Dsm as deepin mate(2):在阿里云上真正实现单盘安装运行skynas
    Boot界的”开源os“ : coreboot,及再谈云OS和本地OS统一装机的融合
    0pe单文件夹,grub菜单全外置版
    阿里云上利用virtiope+colinux实现linux系统盘动态无损多分区
    比WEB更自然,jupyter用于通用软件开发的创新意义:使任何传统程序秒变WEB
    编程实践选型通史:*坦无架构APP开发支持与充分batteryincluded的微实践设施
    除了LINUX,我们真的有可选的第二开源操作系统吗?
    聪明的Mac osx本地云:同一生态的云硬件,云装机,云应用,云开发的完美集
  • 原文地址:https://www.cnblogs.com/fickleness/p/3155031.html
Copyright © 2020-2023  润新知