【问题标题】:how to find the minimum number of primatics that sum to a given number如何找到总和为给定数量的最小灵长类动物数量
【发布时间】:2016-07-05 12:16:21
【问题描述】:

给定一个数 N (

素数是指一个数,它要么是素数,要么可以表示为素数对自身的幂,即素数^素数,例如4、27等

我尝试使用 seive 找到所有的素数,然后将它们存储在一个向量中(下面的代码),但现在我不知道如何找到与给定数字相加的最小素数。

这是我的筛子:

#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;
}

【问题讨论】:

  • “请帮我做作业”不是一个好的 Stack Overflow 问题。如果您edit您的问题描述了您到目前为止所尝试的内容以及您遇到的问题,那么我们可以尝试帮助您了解您的具体问题。您还应该阅读How to Ask
  • @TobySpeight 这是我已经完成的代码(ideone 链接),但由于我是新手,我不知道如何插入代码。它在ideone上ideone.com/E2Eozd
  • 我已经从该链接添加了代码(减去不必要的包含、未使用的变量和混淆宏)。 HTH。
  • @TobySpeight 非常感谢你能帮我在哪里学习 stackoverflow 上的编辑
  • @coder - 如果您有关于编辑的问题,the help center 没有回答,您应该在Meta Stack Overflow 上提问。

标签: c++ dynamic-programming primes


【解决方案1】:

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

对于N = 14,您将拥有L = {2,3,4,5,7,8,9,11,13},因此您需要创建一个算法/过程来尝试这些序列:

  1. 13 太小了
  2. 13 + 13 -> 13 + 2 会太大
  3. 11 太小了
  4. 11 + 11 -> 11 + 4 会太大
  5. 11 + 3 是匹配项。

您可以通过使搜索函数在每次总和中需要另一个 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 的术语数量必须至少为两个,这意味着它要么是两个 primatics 的总和,要么是一个 primatic 和 其他一些 N 的总和。

前几个不平凡的例子:

6 - 可以是2+43+3,这里的所有术语本身都是原始的,所以6 的最小术语数是2。

10 - 可以是2+83+74+65+5。但是 6 不是原始的,去掉这个解至少会留下 2 个项。

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

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

存储所有这些解决方案的结构需要能够迭代相同等级/项数的解决方案。你已经有了一个灵长类的列表,这也是只需要一个术语的解决方案列表。

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] 中的一个术语找到,它是primatic。任何需要两个灵长类的解决方案都可以在Sol[2] 中找到。任何需要 3 的解决方案都将在 Sol[3] 等中。

您需要在这里认识到的是,对于某些a,b,c primmatics,数字S(N) = 3 可以表示为Sol[1][a] + Sol[1][b] + Sol[1][c],但它也可以表示为Sol[1][a] + Sol[2][d],因为所有Sol[2] 都必须可以表示为@ 987654373@.

这个算法实际上会在Sol[1] 中搜索给定的N,然后在Sol[1] + Sol[K] 中查找K,但要做到这一点,您将需要SSol 结构大致如下形式此处显示(或能够以类似方式访问/查询)。

工作示例

使用以上内容作为指导,我快速将其放在一起,它甚至显示了它使用的多项总和。

https://ideone.com/7mYXde

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

唯一令人困惑的部分可能是反向迭代器的使用,它只是为了让 != end() 检查 while 条件的一致性(end 不是有效的迭代器位置,但 begin 是,所以 != begin 的写法会有所不同)。

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

【讨论】:

  • 是否可以使用 dp @Xeren 来实现
  • @coder 我已经更新了我的答案,但我仍然认为有更好的方法可以做到这一点。我可能会在那里添加一个代码示例来展示它。
  • @coder 实现起来很简单,我添加了一个工作示例。
  • @Xeren 非常感谢您提供了如此精美的解释 :) 我是您的忠实粉丝 :) 抱歉回复晚了,因为我正忙于发布我的应用程序。
猜你喜欢
  • 2017-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
相关资源
最近更新 更多