繁体   English   中英

在有效时间内将正整数写为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)时间(和空间,如果我们考虑固定大小的计数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

这种问题的一般适用方法是缓存中间结果,例如如下:

#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分钟才能运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM