我们如何开发一种动态编程算法来计算总和为x
的不同质数的最小数量?
假设动态编程计算不同素数的最小数量,其中p
和x
的每一对的最大值为p
。有人可以帮忙吗?
答案 0 :(得分:7)
如果我们假设Goldbach conjecture为真,那么每个偶数整数> 2是两个素数之和。
所以我们知道答案,如果x是偶数(如果x == 2则为1,否则为2)。
如果x是奇数,则有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,无论你的最大速度是多少,然后你就会离开一些未找到的数字,为了解决在查找中没有解决方案的数字,尝试将该数字分解为在查找中找到的数字之和(您可能需要在查找中存储主要组合并重复删除那些组合)。