与x求和的不同素数的最小数量

时间:2016-12-18 21:53:56

标签: algorithm dynamic-programming

我们如何开发一种动态编程算法来计算总和为x的不同质数的最小数量?

假设动态编程计算不同素数的最小数量,其中px的每一对的最大值为p。有人可以帮忙吗?

4 个答案:

答案 0 :(得分:7)

如果我们假设Goldbach conjecture为真,那么每个偶数整数> 2是两个素数之和。

所以我们知道答案,如果x是偶数(如果x == 2则为1,否则为2)。

如果x是奇数,则有3种情况:

  1. x是素数(答案是1)
  2. x-2是素数(答案是2)
  3. 否则x-3是大于2的偶数(答案是3)

答案 1 :(得分:1)

首先,您需要一个最多x的素数列表。我们将这个整数数组称为素数。

现在我们要填充数组answer [x] [p],其中x是素数之和,p是集合中每个素数的最大值(可能包括但不一定包括p)。

在所有计算之后,有三种可能的答案[x] [p]:

1)如果p = x且p是prime =>答案[x] [p]包含1

2)如果不能解决给定x的问题,p =>答案[x] [p]包含-1

3)如果可以解决给定x的问题,p =>答案[x] [p]包含素数

在计算过程中,[x] [p]还有一个可能的值:

4)我们还没有解决给定x的问题,p =>答案[x] [p]包含0

很明显0不是x = 0之外的任何答案,所以我们可以安全地用0初始化数组(并对x = 0进行特殊处理)。

要计算答案[x] [p]我们可以迭代(让q是我们正在迭代的素数值)通过所有素数直到(包括)p并找到最小值超过1 +答案[xq] [q-1] (不要考虑所有答案[xq] [q-1] = - 尽管1例)。这里1代表q和答案[x-q] [q-1]应该在递归调用中或在此计算之前计算。

现在有一个很小的优化:迭代素数从高到低,如果x / q大于当前答案,我们可以停止,因为要求和x我们至少需要x / q素数。例如,我们甚至不会考虑q = 2的x = 10,因为我们已经有答案= 3(实际上,它包括2作为3个素数中的一个 - 2 + 3 + 5,但我们已经得到它通过递归调用答案(10-5,4)),因为10/2 = 5,那就是我们最多得到5作为答案(实际上q = 2时不存在)。

package ru.tieto.test;

import java.util.ArrayList;

public class Primers {

    static final int MAX_P = 10;
    static final int MAX_X = 10;
    public ArrayList<Integer> primes= new ArrayList<>();
    public int answer[][] = new int[MAX_X+1][MAX_P+1];

    public int answer(int x, int p) {
        if (x < 0)
            return -1;
        if (x == 0)
            return 0;
        if (answer[x][p] != 0)
            return answer[x][p];

        int max_prime_idx = -1;
        for (int i = 0; 
             i < primes.size() && primes.get(i) <= p && primes.get(i) <= x; 
             i++)
            max_prime_idx = i;

        if (max_prime_idx < 0) {
            answer[x][p] = -1;
            return -1;
        }

        int cur_answer = x+1;
        for (int i = max_prime_idx; i >= 0; i--) {
            int q = primes.get(i);
            if (x / q >= cur_answer)
                break;
            if (x == q) {
                cur_answer = 1;
                break;
            }
            int candidate = answer(x-q, q-1);
            if (candidate == -1)
                continue;
            if (candidate+1 < cur_answer)
                cur_answer = candidate+1;
        }

        if (cur_answer > x)
            answer[x][p] = -1;
        else
            answer[x][p] = cur_answer;

        return answer[x][p];
    }


    private void make_primes() {
        primes.add(2);

        for (int p = 3; p <= MAX_P; p=p+2) {
            boolean isPrime = true;
            for (Integer q : primes) {
                if (q*q > p)
                    break;
                if (p % q == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
                primes.add(p);
        }
//      for (Integer q : primes)
//          System.out.print(q+",");
//      System.out.println("<<");       
    }

    private void init() {
        make_primes();
        for (int p = 0; p <= MAX_P; p++) {
            answer[0][p] = 0;
            answer[1][p] = -1;
        }
        for (int x = 2; x <= MAX_X; x++) {
            for (int p = 0; p <= MAX_P; p++)
                answer[x][p] = 0;
        }

        for (Integer p: primes)
            answer[p][p] = 1;
    }

    void run() {
        init();
        for (int x = 0; x <= MAX_X; x++)
            for (int p = 0; p <= MAX_P; p++)
                answer(x, p);
    }

    public static void main(String[] args) {
        Primers me = new Primers();
        me.run();

//      for (int x = 0; x <= MAX_X; x++) {
//          System.out.print("x="+x+": {");
//          for (int p = 0; p <= MAX_P; p++) {
//              System.out.print(String.format("%2d=%-3d,",p, me.answer[x][p]));
//          }
//          System.out.println("}");
//      }
    }

}

答案 2 :(得分:0)

从低于x的所有素数列表开始。 拿最大的。现在我们需要解决(x - pmax)的问题。在这个阶段,这很容易,x - pmax会很低。将所有素数标记为&#34;使用&#34;并存储解决方案1.现在取出列表中最大的素数并重复,直到所有质数都被使用或被拒绝。如果(x - pmax)很高,则问题会变得更加复杂。

这是你的第一关,蛮力算法。在考虑如何加快速度之前先做好工作。

答案 3 :(得分:0)

假设您没有使用哥德巴赫猜想,否则请参阅Peter de Rivaz的优秀答案:

动态编程通常利用重叠的子问题。通常你会自上而下,但在这种情况下,自下而上可能更简单

我建议您总结各种素数组合。

lookup = {}
for r in range(1, 3):
   for primes in combinations_with_replacement(all_primes, r):
        s = sum(primes)
        lookup[s] = lookup.get(s, r) //r is increasing, so only set it if it's not already there
如果你有大量的素数,这将会很快开始变慢,在这种情况下,将max r改为1或2,无论你的最大速度是多少,然后你就会离开一些未找到的数字,为了解决在查找中没有解决方案的数字,尝试将该数字分解为在查找中找到的数字之和(您可能需要在查找中存储主要组合并重复删除那些组合)。