如何找到总和给定数字的最小primatics数

时间:2016-07-05 12:16:22

标签: c++ dynamic-programming primes

给定数字N(< = 10000),找到总和为N的最小primatic数字。

一个primatic数字指的是一个数字,它可以是一个素数,也可以表示为素数对它自身的幂,即素数。 4,27等

我尝试使用seive找到所有的primatic数字,然后将它们存储在一个向量中(下面的代码),但现在我无法查看如何找到总和为给定数字的最小primatic数字。< / p>

这是我的筛子:

#include<algorithm>
#include<vector>

#define MAX 10000

typedef long long int ll;

ll modpow(ll a, ll n, ll temp) {
    ll res=1, y=a;
    while (n>0) {
        if (n&1)
            res=(res*y)%temp;
        y=(y*y)%temp;
        n/=2;
    }
    return res%temp;
}


int isprimeat[MAX+20];

std::vector<int> primeat;

//Finding all prime numbers till 10000
void seive()
{
    ll i,j;
    isprimeat[0]=1;
    isprimeat[1]=1;
    for (i=2;  i<=MAX;  i++) {
        if (isprimeat[i]==0) {
            for (j=i*i;  j<=MAX;  j+=i) {
                isprimeat[j]=1;
            }
        }
    }
    for (i=2;  i<=MAX;  i++) {
        if (isprimeat[i]==0) {
            primeat.push_back(i);
        }
    }

    isprimeat[4]=isprimeat[27]=isprimeat[3125]=0;
    primeat.push_back(4);
    primeat.push_back(27);
    primeat.push_back(3125);
}

int main()
{
    seive();
    std::sort(primeat.begin(), primeat.end());
    return 0;
}

1 个答案:

答案 0 :(得分:0)

一种方法可以是在排序列表中存储小于或等于N的所有primatics - 调用此列表L - 并递归搜索最短序列。最简单的方法是“贪婪”:尽早选择最大的跨度/数字。

N = 14您有L = {2,3,4,5,7,8,9,11,13},因此您需要制作尝试这些序列的算法/流程:

  1. 13太小
  2. 13 + 13 - &gt; 13 + 2会太大
  3. 11太小
  4. 11 + 11 - &gt; 11 + 4会太大
  5. 11 + 3是匹配。
  6. 您可以通过在每次需要另一个primatic时使搜索功能递归来继续此过程,您的目标是发生最少次数。为此,您可以选择最大的 - &gt;每个位置中最小的primatic(总和中的第1个,第2个等),并且只有当到目前为止总和中的primatics足够小以至于另外的primatic不会超过N时,才包含另一个数字。

    我必须做一个工作示例来找到一个足够小的N,它不会导致总和中只有2个数字。请注意,因为你可以将任何自然数表示为最多4个自然数的平方和,并且你有一个比正方形组更密集的L集,所以我认为你很少有对于您想要手动计算的任何N,结果为3或更多。

    动态规划方法

    我必须澄清“贪婪”与“动态编程”不同,它可以给出次优结果。这确实有一个DP解决方案。同样,我不会在代码中编写最终过程,而是将其作为从中制作工作DP解决方案的参考点。

    要做到这一点,我们需要从下到上建立解决方案。您需要的是一种结构,可以为所有数字N存储已知解决方案,此列表可以以最佳方式逐步添加到更大的N

    考虑对于任何N,如果它是primatic,那么N的术语数量只是1.这适用于N=2-5,7-9,11,13,16,17,19。所有其他N的术语数必须至少为2,这意味着它是两个primatics的总和,或者是primatic和其他N 的总和。 / p>

    前几个不小的例子:

    6 - 可以是2+43+3,此处的所有条款本身都是primatic,因此6的最小条款数为2。

    10 - 可以是2+83+74+65+5。然而,6不是primatic,并采取该解决方案至少留下2个术语。

    12 - 可以是2+103+94+85+76+6。其中6+62+10包含非primatics,而其他则不包含,因此2个术语最少。

    14 - 同上,存在两种解决方案:3+115+97+7

    用于存储所有这些解决方案的结构需要能够跨越相同等级/数量的术语的解决方案。您已经有一个primatics列表,这也是只需要一个术语的解决方案列表。

    Sol[term_length] = list(numbers)。您还需要一个函数/缓存来查找一些N的最短项长度,例如S(N) = term_length iif N in Sol[term_length]

    对于Sol[1] = {2,3,4,5 ...}及以后,

    Sol[2] = {6,10,12,14 ...}Sol[3]等等。

    可以使用来自Sol[1]的一个术语来找到任何解决方案。任何需要两个primatics的解决方案都可以在Sol[2]中找到。任何需要3的解决方案都在Sol[3]等。

    您需要认识到的是,对于某些S(N) = 3 primatics,Sol[1][a] + Sol[1][b] + Sol[1][c]可以表示为a,b,c,但它也可以表示为Sol[1][a] + Sol[2][d],因为所有Sol[2]必须表达为Sol[1][x] + Sol[1][y]

    此算法实际上会针对给定的Sol[1]搜索N,然后查看Sol[1] + Sol[K]并增加K,但要执行此操作,您需要S }和Sol结构大致采用此处显示的形式(或者能够以类似方式访问/查询)。

    工作示例

    使用以上作为指导我快速将它们放在一起,甚至可以显示它使用的多项和。

    https://ideone.com/7mYXde

    如果你愿意,我可以深入解释代码,但真正的DP部分是在40-64行左右。递归深度(也是总和中附加项的数量)是k,一个简单的双迭代器,而循环检查是否可以使用第k个已知解和primatics求和,如果是,那么我们就完成了如果没有,那么检查k + 1解决方案,如果有的话。 SolS按照描述工作。

    唯一令人困惑的部分可能是使用反向迭代器,它只是使!= end()检查对于while条件是一致的(end不是有效的迭代器位置但是start是,所以!= begin将是用不同的方式写。)

    编辑 - 仅供参考,第一个至少需要3个术语的数字是959 - 必须运行我的算法到1000个数字才能找到它。它总结为6 + 953(primatic),无论你如何分裂6,它仍然是3个术语。