-7

The user will input 2 integers, x and y, using standard input. You need to calculate the remainder of x modulo y (x % y).

Now, here is the problem:

0 <= x <= 10^100000, (this is "less than or equal" not an arrow). 0 < y <= 100000

We are looking for optimal solution (Time Complexity).

I didn't find the answer on Stackoverflow, so after finding the solution I thought that I should pose this question here for future reference and to see if there are better ways.

Matt
  • 74,352
  • 26
  • 153
  • 180
Mr. M
  • 62
  • 1
  • 15

2 Answers2

4

BigInteger has the divideAndRemainder(...) method, one that returns a BigInteger array, the first item the division result, and the second, the remainder (which is what mod really does in Java).

Update

Including comment by Mark Dickinson to other answer:

There's a much simpler linear-time algorithm: set acc to 0, then for each digit d in x in turn (left to right), convert d to an integer and set acc = (acc * 10 + d) % y. Once the digits are consumed, acc is your result.

Implemented all 3 algorithms as described:

private static int testMarkDickinson(String x, int y) {
    int acc = 0;
    for (int i = 0; i < x.length(); i++)
        acc = (acc * 10 + x.charAt(i) - '0') % y;
    return acc;
}

private static int testHovercraftFullOfEels(String x, int y) {
    return new BigInteger(x).divideAndRemainder(BigInteger.valueOf(y))[1].intValue();
}

private static int testMrM(String x, int y) {
    String s = x;
    while (s.length() >= 7) {
        int len = Math.min(9, s.length());
        s = Integer.parseInt(s.substring(0, len)) % y + s.substring(len);
    }
    return Integer.parseInt(s) % y;
}

Testing with seeded random number for verifiable test (didn't want to hardcode a 98765 digit number):

public static void main(String[] args) {
    Random r = new Random(98765);
    char[] buf = new char[98765];
    for (int i = 0; i < buf.length; i++)
        buf[i] = (char)('0' + r.nextInt(10));

    String x = new String(buf);
    int y = 98765;

    System.out.println(testMarkDickinson(x, y));
    System.out.println(testHovercraftFullOfEels(x, y));
    System.out.println(testMrM(x, y));

    long test1nano = 0, test2nano = 0, test3nano = 0;
    for (int i = 0; i < 10; i++) {
        long nano1 = System.nanoTime();
        testMarkDickinson(x, y);
        long nano2 = System.nanoTime();
        testHovercraftFullOfEels(x, y);
        long nano3 = System.nanoTime();
        testMrM(x, y);
        long nano4 = System.nanoTime();
        test1nano += nano2 - nano1;
        test2nano += nano3 - nano2;
        test3nano += nano4 - nano3;
    }
    System.out.printf("%11d%n%11d%n%11d%n", test1nano, test2nano, test3nano);
}

Output:

23134
23134
23134
    8765773
 1514329736
 7563954071

All 3 produced same result, but there's a clear difference in performance, and Mr. M's "better solution" is the worst of them all.

Andreas
  • 154,647
  • 11
  • 152
  • 247
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • I'd guess that the decimal-to-binary conversion involved in converting a 100000-digit input string to a `BigInteger` would take much longer than a naive decimal-based implementation of the remainder operation. – Mark Dickinson Oct 19 '15 at 20:15
  • This would work, but doesn't have the smallest time complexity. – Mr. M Oct 19 '15 at 20:17
  • @Mr.M: I did not see mention of "smallest time complexity" in your original question. Is this a new requirement not mentioned yet? – Hovercraft Full Of Eels Oct 19 '15 at 20:17
  • 1
    @Mr.M: It's got the smallest time complexity of all the solutions so far. – Andy Turner Oct 19 '15 at 20:17
  • I did say "We are looking for optimal solution" – Mr. M Oct 19 '15 at 20:19
  • @Mr.M: optimal what? It's the **only** solution for very large numbers. – Hovercraft Full Of Eels Oct 19 '15 at 20:21
  • I already know a better solution, I am waiting to see if there is even better. – Mr. M Oct 19 '15 at 20:23
  • 4
    Please state your better solution, so we know whether to spend any more time on this question. – Andy Turner Oct 19 '15 at 20:23
  • 4
    @Mr.M How do you *know* that your (unspecified) solution is faster than using `BigInteger`? How can anyone expect to give an acceptable answer when you have an unstated acceptable criteria? – Andreas Oct 19 '15 at 20:23
  • There's not just one division algorithm, BigInteger apparently [uses Algorithm D in Knuth section 4.3.1](http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/math/MutableBigInteger.java/?v=source), which is long division optimized for machine words. That's O(n^2) while faster division algorithms are known. – harold Oct 19 '15 at 20:26
  • @harold In Java 8 it switches to a Burnikel-Ziegler algorithm for very large integers. – Andreas Oct 19 '15 at 20:29
  • FWIW, I imagine the linear-time solution can be substantially sped up by consuming multiple digits per iteration (if y <= 100000, then should be able to do 5 new digits without overflowing `int`). – Oliver Charlesworth Oct 19 '15 at 21:40
-2

The solution that is better than BigInteger is the following:

1- Read the x as String and y as an integer.

2- Cut the first 9 characters from the left of x and convert them to integer i.

3- calculate i % y and append it to the beginning of x.

4- repeat from 2 until the x string is less than 7 (max length of int - max length of divider).

5- concatenate s with what is left of x and calculate the modulus.

This solution is not constrained to any integer length.

Mr. M
  • 62
  • 1
  • 15
  • 1
    This is worse than BigInteger in the sense that it doesn't work. `x % y` is not equal to this concatenation of sub-results, in the general case. – Oliver Charlesworth Oct 19 '15 at 20:32
  • Sounds suspiciously simple. Are you sure this actually works? – Andy Turner Oct 19 '15 at 20:32
  • Modulus is basically subtraction until the remainder is smaller than the divider, if you have to subtract 7 you can start by subtracting 7000000 if the number allows it. – Mr. M Oct 19 '15 at 20:34
  • @ Oliver Charlesworth, this is not what I said. You can start the modulus on the most significant digits that are past the length of the divider – Mr. M Oct 19 '15 at 20:37
  • @harold, i is 9 digits, y is 6 at most, so it does – Mr. M Oct 19 '15 at 20:38
  • Ok I missed the final bit ("calculate the modulus") which implies a recursive implementation. That would thus be O(n^2), right? – Oliver Charlesworth Oct 19 '15 at 20:43
  • @Mr.M: Perhaps you could illustrate the steps with a small example (say `x = 23997826617809494874` and `y = 81542`)? – Mark Dickinson Oct 19 '15 at 20:45
  • Probably, but it have the need of having BigInteger parse it when mathematically there is no need. And it is not limited by any integer length. – Mr. M Oct 19 '15 at 20:45
  • 2
    `100000000_100000000_000 % 13 = 0`, but you logic is `100000000 % 13 = 9` leading to `s = "99000"`, and `99000 % 13 = 5` – Andreas Oct 19 '15 at 20:46
  • 3
    There's a much simpler linear-time algorithm: set `acc` to `0`, then for each digit `d` in `x` in turn (left to right), convert `d` to an integer and set `acc = (acc * 10 + d) % y`. Once the digits are consumed, `acc` is your result. – Mark Dickinson Oct 19 '15 at 20:47
  • @Andreas, actually 100000000 % 13 = 9 then append 9 to 100000000_000 = 9100000000000, then re-cut 9 digits. This way it will work. – Mr. M Oct 19 '15 at 20:51
  • @MarkDickinson Excellent answer. By far the fastest. Added to answer created by Hovercraft Full Of Eels. – Andreas Oct 19 '15 at 21:16
  • Even using `StringBuilder` or a `char[]` optimally, converting between decimal and internal representation of integers times and again will waste effort. – greybeard Oct 20 '15 at 01:05