2

On january the 19th a new world record was established: the one of the largest prime number found so far. The number amounts to 2^74207281 - 1 and has over 22.3 million digits. I saw on the numberphile channel on youtube that they printed this number in a 3-part book. Now my question is: how do you get a string representation of a number which is so large? Obviously this will take quite some time, but what is the most efficient way of doing this?

The only way I would know to do this in Java is by using BigIntegers, is this the best way? How about in other languages?

Héctor van den Boorn
  • 1,218
  • 13
  • 32
  • There are many ways this can be done which makes the question too broad for stack overflow. There are "big number" type libraries in (several) other languages as well. – mah Jan 22 '16 at 16:59
  • I found the number online and have it saved to a text file. To calculate that number, though, would take you months to finish! – Rabbit Guy Jan 22 '16 at 16:59
  • In a single string? I don't think you'll have enough RAM in your machine to do that no matter what you used. – durbnpoisn Jan 22 '16 at 16:59
  • 3
    @durbnpoisn according to the question, the number has 22.3 million digits. I think your machine has enough RAM to hold that in a string. – mah Jan 22 '16 at 17:00
  • I HOPE you have enough ram for that! – Rabbit Guy Jan 22 '16 at 17:00
  • I was exaggerating. :) – durbnpoisn Jan 22 '16 at 17:03
  • 1
    22.3 million digits = 22.3MB of ram. Considering the another numbers and jvm resources for the calculation, I guess it not even reaches 100MB in use. – elias Jan 22 '16 at 17:07
  • Same Q in python: http://stackoverflow.com/questions/34936226/how-can-i-convert-an-absolutely-massive-number-to-a-string-in-a-reasonable-amoun – dave_thompson_085 Jan 22 '16 at 19:14
  • Optimized binary to decimal usually performs successive division by the largest power of ten that fits in a machine word (e.g., 1000000000 for 32-bit) and processes the remainder for decimal digits. This is more efficient than successive division by 10. Libraries like GMP have asymptotically efficient algorithms for huge integers, as well as integer division as multiplication by reciprocals. Whether or not Java's BigInteger does this is another question. – Brett Hale Jan 23 '16 at 06:16
  • The latest Java uses the divide-and-conquer method. This takes 2'30" on my computer. But another method only takes 7 seconds. Since I also wrote a BigInteger implementation (in Delphi), I am interested in how they do that. My own homebrewn divide-and-conquer approach still needs 5'30". I guess Java is faster because they implement Barret divsion, which I didn't implement yet. – Rudy Velthuis Jan 25 '16 at 10:45
  • @elias: actually, it is more, if you use UTF-16. – Rudy Velthuis Jan 25 '16 at 10:58
  • Right, but there's no reason for this, in this case. Anyway, 200MB would not be a problem also. – elias Jan 25 '16 at 16:23
  • I suggest printing it in binary. It's just 74,207,281 ones :) – Roberto Bonvallet Jan 26 '16 at 18:44

3 Answers3

6

The only way I would know to do this in Java is by using BigIntegers, is this the best way?

In short

The answer to your question is: YES.


In detail

I think java and BigInteger is the best solution. A minimal example could look like this:

public class Prime {

    public static void main(String[] args) {
        BigInteger number = new BigInteger("2")
                .pow(74207281)
                .subtract(new BigInteger("1"));
        System.out.println(number);
    }

}

In even more detail

Maybe it's a good idea to let your computer print the numbers in small groups, instead of creating one, huge, String - for better performance:

import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.util.LinkedList;

public class Prime {

    private static final BigInteger THOUSAND = new BigInteger("1000");

    public static void main(String[] args) throws Exception {
        BigInteger number = new BigInteger("2")
                .pow(74/*207281*/) // use 74207281 for the real number
                .subtract(new BigInteger("1"));

        System.out.println("calculation done, creating texts");

        int counter = 0;

        LinkedList<String> threes = new LinkedList<>();

        for (;;) {

            // divide by 1000 to get next 3 digits
            BigInteger[] divideAndRemainder = number.divideAndRemainder(THOUSAND);
            number = divideAndRemainder[0];
            BigInteger lastThreeDigits = divideAndRemainder[1];

            // format digits, with leading zeros
            String text = String.format("%03d", lastThreeDigits);

            // add them to the list
            threes.addFirst(text);

            // stop, if we reached the end
            if (number.signum() == 0) {
                break;
            }

            // print progress
            if (counter++ > 999) {
                System.out.print(".");
                counter = 0;
            }
        }

        System.out.println("\ntexts ready, writing to file");

        counter = 0;
        try (FileOutputStream output = new FileOutputStream(new File("C:\\temp\\bignumber.txt"))) {
            for (String text : threes) {
                output.write(text.getBytes());
                output.write(' ');

                // print progress
                if (counter++ > 999) {
                    output.write('\n');
                    System.out.print(".");
                    counter = 0;
                }
            }
        }

        System.out.println("\ndone");
    }

}
slartidan
  • 20,403
  • 15
  • 83
  • 131
  • Granted the question is flawed in that it doesn't specify a language for the problem that a solution is wanted for, and further flawed in that it's tagged with some Java-specific things... but if you're going to post a language specific answer, don't you feel it would be better to avoid the one language the OP states he already knows how to solve the problem in? – mah Jan 22 '16 at 17:03
  • 'Take some time' is an understatement, and I suspect 'run out of memory' kicks in as well. – DJClayworth Jan 22 '16 at 17:03
  • 2
    Just tried it. BigInteger number created in 9 millis; Doing number.toString() in 74,048 millis; Printing the string to console in 4,047 millis; Printing in book - unknown. – Anssssss Jan 22 '16 at 17:28
  • For the real number, with 22+ million digits, Java's own method needs much longer. On my computer approx. 2`30". But not with this rather simple (quadratic) method. Then it takes a lot longer. Note that each time, the entire number must be divided by 10^9 or whatever factor is used. It takes ages until that gets faster. – Rudy Velthuis Jan 25 '16 at 10:51
1

You don't actually need to store the whole number as a string before printing it to a file. You just need to output each digit one by one.

As for representing the number itself in memory; that's a different matter. There are efficient ways of storing and manipulating large numbers of the Mersenne form: you wouldn't want to rely on a canned large integer class (such as BigInteger in Java) for doing that. The necessary mathematical operations for primality testing would be too slow.

There are far more comprehensive details in the GIMP project. See http://www.mersenne.org/

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Outputting each digit one by one is the problem. Representing it in binary form is pretty easy, and calculating it (once you know it is 2^xyz - 1 and you have xyz) is easy too.. – Rudy Velthuis Jan 25 '16 at 10:57
1

On a Linux system it is quite easy to achieve:

echo "2 74207281 ^ 1 - p" | dc > /tmp/74207281.txt

The file is --not surprisingly-- about 22 MB in size. On an i7 it took about 45 minutes to calculate. Not timed exactly.

By the way, 74207281 is a prime itself as well, according to Mersenne's theorem.

Mogsdad
  • 44,709
  • 21
  • 151
  • 275