5

I have to print the number of ways you can represent a given number as it's prime number parts.

Let me clarify: Let's say I have been given this number 7. Now, first of all, I have to find all the prime numbers that are less than 7, which are 2, 3 and 5. Now, in how many ways can I summarize those numbers (I can use one number as many times I want) so that the result equals 7? For example, number 7 has five ways:

2 + 2 + 3
2 + 3 + 2
2 + 5
3 + 2 + 2
5 + 2

I'm totally lost with this task. First I figured I'd make an array of usable elements like so: { 2, 2, 2, 3, 3, 5 } (7/2 = 3, so 2 must appear three times. Same goes with 3, which gets two occurences). After that, loop through the array and choose a 'leader' that determines how far in the array we are. I know the explanation is horrible, so here's the code:

#include <iostream>
#include <vector>

int primes_all[25] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};

int main()
{
    int number;
    std::cin >> number;

    std::vector<int> primes_used;

    for(int i = 0; i < 25; i++) {
        if(primes_all[i] < number && number-primes_all[i] > 1) {
            for(int k = 0; k < number/primes_all[i]; k++)
                primes_used.push_back(primes_all[i]);
        }
        else break;
    }

    int result = 0;

    for(size_t i = 0; i < primes_used.size(); i++) {
        int j = primes_used.size()-1;
        int new_num = number - primes_used[i];

        while(new_num > 1 && j > -1)
        {
            if(j > -1) while(primes_used[j] > new_num && j > 0) j--;

            if(j != i && j > -1) {
                new_num -= primes_used[j];

                std::cout << primes_used[i] << " " << primes_used[j] << " " << new_num << std::endl;
            }

            j--;
        }

        if(new_num == 0) result++;
    }

    std::cout << result << std::endl;

    system("pause");
    return 0;
}

This simply doesn't work. Simply because the idea behind it is wrong. Here's a little details about the limits:

  • Time limit: 1 second
  • Memory limit: 128 MB

Also, the biggest number that can be given is 100. That's why I made the array of prime numbers below 100. The result grows very fast as the given number gets bigger, and will need a BigInteger class later on, but that's not an issue.

A few results known:

Input    Result

7        5
20       732
80       10343662267187

SO... Any ideas? Is this a combinatory problem? I don't need code, just an idea. I'm still a newbie to C++ but I'll manage


Keep in mind that 3 + 2 + 2 is different than 2 + 3 + 2. Also, were the given number to be a prime itself, it won't be counted. For example, if the given number is 7, only these sums are valid:

2 + 2 + 3
2 + 3 + 2
2 + 5
3 + 2 + 2
5 + 2
7 <= excluded
Olavi Mustanoja
  • 2,045
  • 2
  • 23
  • 34

3 Answers3

9

Dynamic programming is your friend here.

Consider the number 27.

If 7 has 5 results, and 20 has 732 results, then you know that 27 has at least (732 * 5) results. You can use a two variable system (1 + 26, 2 + 25 ... etc) using the precomputed values for those as you go. You don't have to recompute 25 or 26 because you already did them.

corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • And don't do this in recursion, "because you already did them", but rather in a loop starting with building the sum for `1`, then for `2` and so on until you build the sum for your target number. Compute new values by reusing the old values + add the base case "single prime number" in each step. You don't need your set of numbers which can be used (`primes_used`) – leemes Jan 08 '13 at 15:57
  • This went over my head. Could you clarify just a bit? – Olavi Mustanoja Jan 08 '13 at 16:19
  • 2
    This won't work in general as proposed here; you're disregarding the identity of the primes and these matter since distinct ordering matters. – Eamon Nerbonne Jan 19 '13 at 13:35
  • Did you mean "at least 732 * 5" results? – Peaceful Oct 27 '17 at 17:19
  • Did I mean it? No, no I didn't. Should I have put it because it's more correct, yes, yes I should have. =) – corsiKa Oct 27 '17 at 17:31
3

The concept you are searching for is the "prime partitions" of a number. S partition of a number is a way of adding numbers to reach the target; for instance, 1+1+2+3 is a partition of 7. If all the addends are prime, then the partition is a prime partition.

I think your example is wrong. The number 7 is usually considered to have 3 prime partitions: 2+2+3, 2+5, and 7. The order of the addends doesn't matter. In number theory the function that counts prime partitions is kappa, so we would say kappa(7) = 3.

The usual calculation of kappa is done in two parts. The first part is a function to compute the sum of the prime factors of a number; for instance, 42=2·3·7, so sopf(42)=12. Note that sopf(12)=5 because the sum is over only the distinct factors of a number, so even though 12=2·2·3, only one 2 is included in the calculation of the sum.

Given sopf, there is a lengthy formula to calculate kappa; I'll give it in LaTeX form, since I don't know how to enter it here: \kappa(n) = \frac{1}{n}\left(\mathrm{sopf}(n) + \sum_{j=1}^{n-1} \mathrm{sopf}(j) \cdot \kappa(n-j)\right).

If you actually want a list of the partitions, instead of just the count, there is a dynamic programming solution that @corsiKa pointed out.

I discuss prime partitions in more detail at my blog, including source code to produce both the count and the list.

user448810
  • 17,381
  • 4
  • 34
  • 59
  • That's some cool info. However, in my case, `3 + 2 + 2` is different than `2 + 3 + 2` is different than `2 + 2 + 3` – Olavi Mustanoja Jan 08 '13 at 16:18
  • That's not the normal way. First, you should clarify with your instructor exactly what is being requested. Then, if you really want what you say you do, Google for "prime partitions with repetition" or "distinct prime partitions." In any case, be aware that your example of the prime partitions of 7 omits the number 7 itself, which is prime, so by your method there are six ways, not five, to make the partitions of 7. – user448810 Jan 08 '13 at 16:30
  • Sorry, I thought I mentioned. I'll edit the description of the problem. Thanks for pointing the path, tho :D – Olavi Mustanoja Jan 08 '13 at 16:37
  • Is this an SPOJ problem? Or some other web coding site? If so, please give a link to the original problem. If it's homework, please post a link to the assignment. – user448810 Jan 08 '13 at 17:05
  • It's homework, the assignment came from my tutor's mouth and never existed on paper, and it was in finnish. I described the problem as well as I could – Olavi Mustanoja Jan 08 '13 at 17:19
  • Thanks for responding. Please discuss with your tutor exactly the definition of a prime partition. It's very hard for me to believe that the definition is as you describe it. – user448810 Jan 08 '13 at 17:30
  • He answered my email almost instantly, and yes, the definition is as I described it. – Olavi Mustanoja Jan 08 '13 at 17:39
  • Then you will have some hard work to do. Why don't you start by computing by hand as many values as you can, starting with 2. Let us know how you get on. – user448810 Jan 08 '13 at 17:46
0

Here's an efficient implementation which uses dynamic programming like corsiKa suggests, but does not use the algorithm he describes.

Simply: if n is reachable via k distinct paths (including the single-step one, if it exists), and p is prime, then we construct k paths to n+p by appending p to all paths to n. Considering all such n < N will produce an exhaustive list of valid paths to N. So we just sum the number of paths so discovered.

#include <iostream>

int primes_all[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};

const int N_max = 85;
typedef long long ways;
ways ways_to_reach_N[N_max + 1] = { 1 };

int main()
{
    // find all paths
    for( int i = 0; i <= N_max; ++i ) {
        ways ways_to_reach_i = ways_to_reach_N[i];

        if (ways_to_reach_i) {
            for( int* p = primes_all; *p <= N_max - i && p < (&primes_all)[1]; ++p ) {
                ways_to_reach_N[i + *p] += ways_to_reach_i;
            }
        }
    }

    // eliminate single-step paths
    for( int* p = primes_all; *p <= N_max && p < (&primes_all)[1]; ++p ) {
        --ways_to_reach_N[*p];
    }

    // print results
    for( int i = 1; i <= N_max; ++i ) {
        ways ways_to_reach_i = ways_to_reach_N[i];

        if (ways_to_reach_i) {
            std::cout << i << " -- " << ways_to_reach_i << std::endl;
        }
    }

    return 0;
}

Replacing the typedef ways with a big integer type is left as an exercise to the reader.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720