输出和为n的所有的连续自然数序列
?
輸出和為n的所有的連續自然數序列
如 n = 9:
?9
?4 5
?2 3 4
?
《編程之美》的題目(2.21只考加法的面試題),去年曾經寫過本題的代碼,后來不知道把代碼放哪里了。按以前的思路,重寫了下代碼,寫完后翻了下書,結果發現與書上要求的還不一樣。
?
假設,拆分成的數列為:m, m+1, … m+k-1
則 n = (m + m + k - 1) * k / 2?或 2*n = (2*m + k - 1) * k
?
顯然就是求2*n的所有因子,最簡單的方法就是暴力搜索:
?
對公式2*n = (2*m + k - 1) * k 進行轉變,可得下面幾種方法:
?
方法一:
?? 2*m + k – 1 = 2 * n / k
??從k = 1 開始判斷 k 是否是 2 * n的因子,當 k > 2 * n / k 時終止
?? (由于2*m + k – 1和 k奇偶性相反,輸出結果前還要判斷k與2 * n / k是否奇偶性相反)
循環次數約為:sqrt(2*n)
?
方法二:
??m = (n – k * (k - 1) / 2) / k > 0
??從k = 1 開始判斷 k 是否是(n – k * (k - 1) / 2)的因子,當m <= 0時終止
循環次數約為:sqrt(2*n)
?
上面兩種方法,效率相差不大,并且都不高,對 n = 2^30,這種不能拆分的數,要進行大量的計算。注意到2*m + k – 1和 k奇偶性相反,可以先將2 * n的所有質因子2提取出來,假設總共有t個2,則 2 * n = 2^t * nn(nn為奇數),問題轉為求nn的所有因子,假設nn = a * b (a <= b),由于 2 * m + k – 1 一定大于k,由2 * n = 2^t * a * b可得兩組2*n的兩組因子(2^t * a, b) 和 (a, 2^t * b) 這兩組數中,較小的那個就是k。采用這種方法的好處是,對偶數能極大的減少計算,特別是對n = 2^30,可以不進行任何除法計算。
對 nn求因子,可以采用上面的方法一和方法二,于是得到方法三和方法四。
?
方法三:
2 * n = 2^t * nn?奇數 nn = i * j?(i?<= j)?? ->?? j = nn / i
從i = 1開始(每次遞增2),判斷i是否是nn的因子,當 j > i 時終止。
循環次數約為:sqrt(nn) / 2
?
方法四:
2 * n = 2^t * nn?奇數 nn = i * (i + j)?(j >= 0)?? ->?? j = (nn – i * i) / i?>= 0
從i = 1開始(每次遞增2),判斷i是否是(nn – i * i) / i的因子,當 j < 0時終止。
循環次數約為:sqrt(nn) / 2
?
方法五:
既然是求所有的因子,那么最好的方法當然是利用質因子進行組合。
下面的代碼存在一些需要優化的地方,比如存在重復計算、防止編譯器對n / i 和n % i進行兩次除法計算。如果數不是太大,可以緩存所有的質因子。
另見: 輸出自然數n的所有因子。
(下面的代碼,要將函數參數改為unsigned的話,需要將i=1的情況單獨列出來討論,即可保證計算過程中不會發生溢出。)
?
void output(int beg, int len) { printf("%d-%d\n", beg, beg + len - 1); }
inline void output2(int i, int j) { output((i - j + 1) / 2u, j); }
?
//方法三
void seq3(int n)??????????
{??????????????????????? //2*n = (2*m+k-1)*k?//m, m+1, ... m+k-1
?if (n <= 0) return;?? //?
?unsigned count = 1;??
?while (n % 2u == 0) { n /= 2u; ++count; } //去除質因子2,設f = 2^x???
?for (unsigned i = 1; ;i += 2) { //獲取nn的所有因子 nn = i * j 且 i <= j
??? unsigned j = n / i;
??? if (i > j) break;
??? if (n % i)?continue;
??? output2(j << count, i);?//k=i ?2m+k-1=j*2*f?
??? if (i == j) break;
??? unsigned t = i << count;???
??? if (t > j) output2(t, j);??
??? else output2(j, t);
?}
?printf("\n");
}
?
?
//方法四
void seq4(int n)??????????
{??????????????????????? //2*n = (2*m+k-1)*k?//m, m+1, ... m+k-1
?if (n <= 0) return;?? //?
?unsigned count = 1;??
?while (n % 2u == 0) { n /= 2u; ++count; } //去除質因子2,設f = 2^x
?n -= 1 * 1;???
?for (unsigned i = 1; n >= 0; n -= 4 * i + 4, i += 2) { //獲取nn的所有因子
??? if (n % i == 0) {???? // nn = (i+a)*i?-> a = (nn - i * i) / i
????? unsigned j = n / i + i;
????? output2(j << count, i);?//k=i 2m+k-1=j*2*f ??
????? if (i == j) continue;
????? unsigned t = i << count;?????
????? if (t > j) output2(t, j);??
????? else output2(j, t);
??? }??
?}
?printf("\n");
}
?
?
//方法五
unsigned gn = 0;?//值為 2 * n
static void factor(unsigned n, unsigned k = 1, unsigned beg = 3)
{
?assert(n & 1);
?assert(beg & 1);
?if (n == 1) {
??? unsigned t = ::gn / k;
??? if (t > k) output2(t, k);
??? else output2(k, t);
??? return;
?}
?assert(n >= beg);
?
?for (unsigned i = beg, count = 0; ;i += 2) {
??? //if (n % i) {
??? //?if (n / i >= i) continue;
??? //?factor(1, k, n);?? // n是質數
??? //?factor(1, k * n, n);
??? //?return;?
??? //}
??? if (i > n / i) {
????? factor(1, k);?? // n是質數,
????? factor(1, k * n);
????? return;???????
??? }
??? if (n % i) continue;
???
??? ++count;
??? n /= i;
??? while (n % i == 0) { ++count; n /= i; }
??? for (unsigned j = 0, f = k; j <= count; ++j, f *= i)?
????? factor(n, f, i + 2);
??? return;??
?}
}
?
void seq(int n)??????????
{?? ?????????????????????//2*n = (2*m+k-1)*k?//m, m+1, ... m+k-1
?if (n <= 0) return;
?::gn = n * 2u;
?while (n % 2u == 0) { n /= 2u;} //去除質因子2
?factor(n);
?printf("\n");
}
?
?
?
轉載于:https://www.cnblogs.com/flyinghearts/archive/2011/03/22/1992003.html
總結
以上是生活随笔為你收集整理的输出和为n的所有的连续自然数序列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 化解三大错误晚餐方式。
- 下一篇: 9、C语言中sscanf使用及运算符优先