/ / Minimalna liczba różnych liczb pierwszych, które sumują się do x - algorytmu, programowania dynamicznego

Minimalna liczba różnych liczb pierwszych, które sumują się do algorytmu x, dynamiczne programowanie

W jaki sposób możemy opracować dynamiczny algorytm programowania, który oblicza minimalną liczbę różnych liczb pierwszych, które się sumują x?

Załóżmy, że programowanie dynamiczne oblicza minimalną liczbę różnych liczb pierwszych, spośród których jest największa p za każdą parę x i p. Czy ktoś może pomóc?

Odpowiedzi:

7 dla odpowiedzi № 1

Jeśli założymy Hipoteza Goldbacha jest prawdą, wtedy każda parzysta liczba całkowita> 2 jest sumą dwóch liczb pierwszych.

Znamy więc odpowiedź, jeśli x jest parzyste (1 jeśli x == 2 lub 2 inaczej).

Jeśli x jest nieparzyste, to są 3 przypadki:

  1. x jest pierwsze (odpowiedź to 1)
  2. x-2 jest liczbą pierwszą (odpowiedź to 2)
  3. w przeciwnym razie x-3 jest liczbą parzystą większą niż 2 (odpowiedź to 3)

1 dla odpowiedzi nr 2

Przede wszystkim potrzebujesz listy liczb pierwszych do x. Nazwijmy tę tablicę liczb całkowitych.

Teraz chcemy zapełnić odpowiedź tablicową [x] [p], gdzie x jest sumą liczb pierwszych, a p jest maksymalną dla każdej liczby głównej w zbiorze (ewentualnie włączając, ale niekoniecznie włączając p).

Po wszystkich obliczeniach istnieją 3 możliwości odpowiedzi [x] [p]:

1) jeśli p = x i p jest liczbą pierwszą => odpowiedź [x] [p] zawiera 1

2) jeśli nie można rozwiązać problemu dla podanego x, p => odpowiedź [x] [p] zawiera -1

3) jeśli możliwe jest rozwiązanie problemu dla podanego x, p => odpowiedź [x] [p] zawiera liczbę liczb pierwszych

Podczas obliczeń jest jeszcze jedna możliwa wartość odpowiedzi [x] [p]:

4) nie rozwiązaliśmy jeszcze problemu z podanym x, p => odpowiedź [x] [p] zawiera 0

Jest całkiem oczywiste, że 0 nie jest odpowiedzią na nic poza x = 0, więc jesteśmy bezpieczni inicjalizując tablicę z 0 (i robiąc specjalną obróbkę dla x = 0).

Aby obliczyć odpowiedź [x] [p] możemy iterować (niech qjest wartością podstawową, którą sprawdzamy) przez wszystkie liczby pierwsze do (włącznie) p i znaleźć minimum powyżej 1 odpowiedzi [xq] [q-1] (nie rozważaj całej odpowiedzi [xq] [q-1] = - 1 przypadki chociaż). Tutaj 1 przychodzi dla q, a odpowiedź [x-q] [q-1] powinna być obliczona w rekursywnym wywołaniu lub przed obliczeniami.

Teraz mała optymalizacja: iteruj liczby pierwsze od wyższego do niższego, a jeśli x / q jest większe od bieżącej odpowiedzi, możemy zatrzymać, ponieważ aby uzyskać sumę x, potrzebujemy co najmniej x / q liczb pierwszych. Na przykład, nie weźmiemy pod uwagę q = 2 dla x = 10, ponieważ mamy już odpowiedź = 3 (faktycznie, zawiera ona 2 jako jedną z 3 liczb pierwszych - 2 + 3 + 5, ale już to mamy poprzez rekursywną odpowiedź wywołania (10-5, 4)), ponieważ 10/2 = 5, to znaczy, że otrzymamy 5 jako odpowiedź w najlepszym przypadku (w rzeczywistości nie istnieje dla 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("}");
//      }
}

}

0 dla odpowiedzi № 3

Zacznij od listy wszystkich liczb pierwszych niż x. Weź największy. Teraz musimy rozwiązać problem (x - pmax). Na tym etapie będzie to łatwe, x - pmax będzie niskie. Zaznacz wszystkie liczby pierwsze jako "używane" i przechowuj rozwiązanie 1. Teraz weź największą z liczb wciąż leżącą na liście i powtarzaj, aż wszystkie liczby pierwsze zostaną użyte lub odrzucone. Jeśli (x - pmax) jest wysoki, problem staje się bardziej złożony.

To jest twój pierwszy przebieg, algorytm brutalnej siły. Zacznij to najpierw, zanim zastanowisz się, jak przyspieszyć działanie.


0 dla odpowiedzi nr 4

Zakładając, że nie używasz hipotez Goldbacha, w przeciwnym razie patrz doskonała odpowiedź Petera de Rivaza:

dynamiczne programowanie zazwyczaj wykorzystuje nakładające się podproblemy. Zwykle przechodzisz z góry do dołu, ale w tym przypadku oddolne może być prostsze

Sugeruję, że sumujesz różne kombinacje liczb pierwszych.

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

zacznie się bardzo wolno, jeśli tymiej dużą liczbę liczb pierwszych, w tym przypadku zmień maks. r na coś takiego jak 1 lub 2, bez względu na to, co maksimum jest wystarczająco szybkie dla ciebie, a wtedy pozostanie ci kilka liczb, które nie zostaną znalezione, aby rozwiązać liczba, która nie ma rozwiązania w wyszukiwaniu, spróbuj podzielić tę liczbę na sumy liczb, które znajdują się w odnośniku (być może trzeba będzie zapisać kombinacje pierwszorzędne w odnośniku i odrzucić te kombinacje).