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 }