1. 能否拆分
结论:除了 $2^n$ 之外,其他自然数均可以拆分
所有奇数都能写成 $2i + 1$ 的形式,因此至少可以拆成 $(i, i+1)$,所以奇数可以拆分
偶数里边,奇数倍数的可以拆分,其他的(也就是 $2^n$) 无法拆分
1 boolean ifConsistOfConsecutiveInteger(int n) { 2 if (n <= 0) { 3 return false; 4 } 5 boolean result = false; 6 if ((n & (n - 1)) != 0) { 7 result = true; 8 } 9 10 return result; 11 }
2. 输出可以拆分成的各种形式
2.1 数学方法
以 $a$ 表示连续自然数中的第一个,则
$n = a + (a + 1) + (a + 2) + ... + (a + k - 1)$
整理一下
$2n = 2ka + k^2 - k$
整理成关于 $k$ 的一元二次方程
$k^2 + (2a - 1)k - 2n = 0$
解这个方程,$Delta = (2a -1)^2 + 8n$
$k = (1 - 2a + sqrt{Delta})/2$
然后,考虑三个问题
1) 连续自然数序列里的第一个数必然小于等于 $n/2$,如果大于的话,再和后面的数相加肯定大于 $n$
2) $Delta$ 开方之后必须是整数
3) $1 - 2a + sqrt{Delta}$ 必须是偶数,否则除以 $2$ 会出现小数
代码如下($n$ 太大时不适用,会有溢出):
1 void printExpression(int n) { 2 long first; 3 long incr; 4 for (int i = 1; i <= n / 2; i++) { 5 first = i; 6 long delta = (2 * first - 1) * (2 * first - 1) + 8 * n; 7 long sqrt_delta = (int) Math.sqrt(delta); 8 9 if (sqrt_delta * sqrt_delta == delta && sqrt_delta % 2 != 0) { 10 incr = (sqrt_delta - (2 * first - 1)) / 2; 11 System.out.println("num of consecutive integer is " + incr); 12 for (int j = 1; j <= incr; j++) { 13 System.out.print(first + j - 1 + " "); 14 } 15 System.out.println(); 16 } 17 } 18 }
2.2 双指针解法(参考剑指 Offer)
1 void printExpression2(int n) { 2 int small = 1; 3 int big = 2; 4 int sum = small + big; 5 while (small <= n / 2) { 6 if (sum == n) { 7 for (int i = small; i <= big; i++) { 8 System.out.print(i + " "); 9 } 10 System.out.println(); 11 } 12 while (sum > n) { 13 sum -= small; 14 small++; 15 16 if (sum == n) { 17 for (int i = small; i <= big; i++) { 18 System.out.print(i + " "); 19 } 20 System.out.println(); 21 } 22 } 23 24 big++; 25 sum += big; 26 } 27 }