在有效时间内将正整数写为 2 的幂和的总方法数
Posted
技术标签:
【中文标题】在有效时间内将正整数写为 2 的幂和的总方法数【英文标题】:Total number of ways to write a positive integer as the sum of powers of 2 in efficient time 【发布时间】:2013-05-08 05:01:29 【问题描述】:我一直在查看Number of ways to write n as a sum of powers of 2,它工作得很好,但我想知道如何提高该算法的运行时效率。它无法在任何合理的时间内(不到 10 秒)计算超过 ~1000 的任何东西。
我假设它与将其分解为子问题有关,但不知道如何去做。我在想像 O(n) 或 O(nlogn) 运行时之类的东西——我相信它是有可能的。我只是不知道如何有效地分工。
通过 Chasefornone 编码
#include<iostream>
using namespace std;
int log2(int n)
int ret = 0;
while (n>>=1)
++ret;
return ret;
int power(int x,int y)
int ret=1,i=0;
while(i<y)
ret*=x;
i++;
return ret;
int getcount(int m,int k)
if(m==0)return 1;
if(k<0)return 0;
if(k==0)return 1;
if(m>=power(2,k))return getcount(m-power(2,k),k)+getcount(m,k-1);
else return getcount(m,k-1);
int main()
int m=0;
while(cin>>m)
int k=log2(m);
cout<<getcount(m,k)<<endl;
return 0;
【问题讨论】:
请向我们展示您的代码... 如果您最多可以使用 2 的每个幂一次,则答案是一个常数:ONE。否则,只求 2 的幂就足以解决问题。 编辑添加代码 - 来自 Chasefornone - 不是我自己的。 【参考方案1】:由于我们正在处理某个基数的幂(在本例中为 2),我们可以在 O(n)
时间(和空间,如果我们考虑固定大小的计数)轻松完成。
关键是分区的生成函数。设p(n)
为将n
写成基数b
的幂的次数。
然后考虑
∞
f(X) = ∑ p(n)*X^n
n=0
可以把f
写成无穷积,
∞
f(X) = ∏ 1/(1 - X^(b^k))
k=0
如果只希望系数达到某个极限l
,则只需考虑b^k <= l
的因素。
以正确的顺序(降序)将它们相乘,在每一步都知道只有索引可被b^i
整除的系数是非零的,所以只需要将n/b^k + n/b^(k-1) + ... + n/b + n
系数相加,总共O(n)
。
代码(不防止较大参数溢出):
#include <stdio.h>
unsigned long long partitionCount(unsigned n);
int main(void)
unsigned m;
while(scanf("%u", &m) == 1)
printf("%llu\n", partitionCount(m));
return 0;
unsigned long long partitionCount(unsigned n)
if (n < 2) return 1;
unsigned h = n /2, k = 1;
// find largest power of two not exceeding n
while(k <= h) k <<= 1;
// coefficient array
unsigned long long arr[n+1];
arr[0] = 1;
for(unsigned i = 1; i <= n; ++i)
arr[i] = 0;
while(k)
for(unsigned i = k; i <= n; i += k)
arr[i] += arr[i-k];
k /= 2;
return arr[n];
工作速度足够快:
$ echo "1000 end" | time ./a.out
1981471878
0.00user 0.00system 0:00.00elapsed
【讨论】:
完美!谢谢你。我仍然习惯于使用二进制和有符号/无符号数字进行所有这些数学运算。您将如何防止大输入溢出?分成2块运行两次? 不,第一次溢出会发生在结果中,你需要使用任意精度类型。然后下一个可能性是索引中的溢出 (i
),为此,使用 unsigned long long
会带你走得足够远,以至于在任何溢出危险之前你就会内存不足。另外,如果你想处理大于几千的输入,你最好 calloc
数组而不是把 VLA 放在堆栈上。【参考方案2】:
解决此类问题的一种普遍适用的方法是缓存中间结果,例如如下:
#include <iostream>
#include <map>
using namespace std;
map<pair<int,int>,int> cache;
/*
The log2() and power() functions remain unchanged and so are omitted for brevity
*/
int getcount(int m,int k)
map<pair<int,int>, int>::const_iterator it = cache.find(make_pair(m,k));
if (it != cache.end())
return it->second;
int count = -1;
if(m==0)
count = 1;
else if (k<0)
count = 0;
else if (k==0)
count = 1;
else if(m>=power(2,k))
count = getcount(m-power(2,k),k)+getcount(m,k-1);
else
count = getcount(m,k-1);
cache[make_pair(m,k)] = count;
return count;
/*
The main() function remains unchanged and so is omitted for brevity
*/
原始程序(我称之为nAsSum0
)的结果是:
$ echo 1000 | time ./nAsSum0
1981471878
59.40user 0.00system 0:59.48elapsed 99%CPU (0avgtext+0avgdata 467200maxresident)k
0inputs+0outputs (1935major+0minor)pagefaults 0swaps
对于带缓存的版本:
$ echo 1000 | time ./nAsSum
1981471878
0.01user 0.01system 0:00.09elapsed 32%CPU (0avgtext+0avgdata 466176maxresident)k
0inputs+0outputs (1873major+0minor)pagefaults 0swaps
...都在 Cygwin 下的 Windows 7 PC 上运行。因此,带有缓存的版本太快了,time
无法准确测量,而原始版本大约需要 1 分钟才能运行。
【讨论】:
以上是关于在有效时间内将正整数写为 2 的幂和的总方法数的主要内容,如果未能解决你的问题,请参考以下文章