在有效时间内将一个正整数写成2的幂的和的方法总数
Total number of ways to write a positive integer as the sum of powers of 2 in efficient time
我一直在寻找将n写成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),因此我们可以在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("%llun", 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
解决此类问题的一般方法是缓存中间结果,例如:
#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分钟。
相关文章:
- 比较C++中两个整数的最有效和最干净的方法是什么?
- #include < conio.h> 和 getch() 方法之间的关系是什么?
- 一种优雅或至少可行的方法,用于使用和接受具有重载方法和运算符的不同大小的文字数组常量
- 优雅和最短的方法,只保存一半的字节
- 模板中的模板方法 - 实例声明和类方法声明签名不同
- 使用 object 中的方法调用带有 std::bind 和 std::function.target 的 C 样式函数
- 类的方法和对象。参考?智能指针?简单的初始化?
- Visual Studio 2015资源视图和资源编译器使用不同的方法在项目目录中查找图标文件.如何修复
- 用推广make_shared和make_unique的方法复合指针特征类
- 在类中定义数组的方法和字段
- 是否可以基于类模板的参数调用类方法和全局方法
- C++ - 将函数链接到触发器的有效和高效方法
- 运算符重载C++、(+、-、*、/ 等)有没有比复制、替换和粘贴更聪明的方法?
- 将非类库分离为标头和实现的方法
- Arduino trim() 和 replace() 方法返回从 'void' 到非标量类型'String'请求的转换
- 具有相同名称的方法和内部类(错误:.. 与前面的声明冲突)
- C++:涉及继承和"using"时的方法重载
- 是否可以覆盖 "find" 和 "erase" boost::bimaps::bimap.left 的方法?怎么办?
- 返回混合 \ 和 / 的文件系统路径字符串方法
- 在有效时间内将一个正整数写成2的幂的和的方法总数