2

I need to fill a int[2000][2000] matrix with some logic.

My code for filling array:

// n: (1 to 2000)
for(int i=0; i<n; i++){
    for(int j=0; j<n; j++){
        uniMatrix[i][j] = (((calc(i * j * i * j)) & 1) == 0) ? 0 : 1;
    }
}

Here: i * j * i * j is my take on getting square of i*j. The calc() is a method used to get a value. Then, I check if value returned by calc() is even or odd. If it is even, I store 0 in matrix on (i, j), otherwise I store 1 in it.

My calc() function is as below:

private static int calc(int n){

    // value of n=calc(1 | 2 | 3) is known
    if(n < 4) return n;

    // if value is present in HashMap, return it (don't calculate again)
    if(map.containsKey(n)) {
        return map.get(n);
    }

    // calculate the answer
    int answer = 1 * calc(n-1) + 2 * calc(n-2) + 3 * calc(n-3);

    // store it in HashMap so that we don't have to recalculate it
    map.put(n, answer);

    return answer;
}

Now, If n is 13, it creates a [13x13] matrix. But, for n=14, it throws a StackOverflowError at map.containsKey(n). I need to be able to make [2000x2000] matrices.

I know that the problem might be recursion. But, is there a way around this? Can I do something with BitSets(I don't have a clue on how to do it)?

I can use other datatype matrices: String[][] or boolean[][] too. I can't use a library outside Java SDK/JDK.

Edit: It is not a duplicate of "What is StackOverflowError?", I know what it is, I know why they occur. I need help in finding an alternative to my approach to prevent this error.

rupinderjeet
  • 2,984
  • 30
  • 54
  • If you got some advice to improve performance, I'd welcome it. – rupinderjeet Dec 22 '16 at 06:08
  • 3
    so when `i` is 1999 and `j` is 1999 you are going to have a stack of `i * j * i * j` - do not think this is a little excessive? – Scary Wombat Dec 22 '16 at 06:18
  • 3
    The problem is definitely recursion. Bitsets and other data types won't help you here. Convert the function to an iterative one. – Robert Rouhani Dec 22 '16 at 06:19
  • Wombat, You're right! It becomes a lot bigger in the `calc()` too. Robert, can you show me an example? – rupinderjeet Dec 22 '16 at 06:20
  • Possible duplicate of [What is a StackOverflowError?](http://stackoverflow.com/questions/214741/what-is-a-stackoverflowerror) – denvercoder9 Dec 22 '16 at 06:36
  • 1
    Fact: We can mark every single SOE question as duplicate of "What is SOE" question. Just because it covers "What" "How" "Why" "Prevention". right? I need an alternative. I know why it is thrown(and I know I should not use recursion). – rupinderjeet Dec 22 '16 at 07:00
  • The problem here is value return by `calc` really really big. you cannot store it to `int` or event `long` or `double`. I used `BigInteger` instead of `int` and I try 'calc(100)` and it return `10447838103188040832744916848333498450`. your case 2000*2000=4,000,000. Cannot image how long the returned value. – Antony Dao Dec 22 '16 at 07:02
  • You know now that you should not use recursion in this case, that’s a good first step. I think you will be able to rewrite your code yourself. If you hit any problem, I think it’s best to post a new question with your attempt. – Ole V.V. Dec 22 '16 at 07:03
  • @AntonyDao You are wrong. `calc(100) = 639576146`; unless, you are actually making it: `calc(100 * 100)` which throws SOE – rupinderjeet Dec 22 '16 at 07:05
  • @rupinderjeet do you realize your `1 * calc(n-1) + 2 * calc(n-2) + 3 * calc(n-3); ` core logic seems like can handle using linear regression mathematical solutions ? Just think in this way you dont need to recurcievly call your method always if you can find the linear regression of your problem there are other java Math libraries it may help you out..!! – Rookie007 Dec 22 '16 at 07:08
  • You’ve set yourself a big goal in terms of data. I believe you are trying to calculate `calc(n)` for all `n` from 0 through 15968023992001. To store all those in a map you will need huge amounts of RAM. – Ole V.V. Dec 22 '16 at 07:08
  • You just need to know whether `calc(n)` is even or odd? Correct me if I’m wrong: I believe `calc(1)` and `calc(3)` are odd and all the others are even. – Ole V.V. Dec 22 '16 at 07:10
  • Yes, I just need to know even or odd. Can you explain your theory about calc(1) and calc(3) being odd; while others being even; `calc(6) calc(7) calc(8) calc(10)` is odd – rupinderjeet Dec 22 '16 at 07:14
  • 1
    @rupinderjeet Please see this link below. I think `cal(100)` return you small value because you use `int`. Please correct me if I misunderstand. https://docs.google.com/spreadsheets/d/1QA-I8zN0-9JKS8sSqEZ0VHtfgN6wvhpwq5aYeUL_Vg4/edit?usp=sharing – Antony Dao Dec 22 '16 at 07:18
  • 1
    I was a bit too fast there. However, you just need to know the parity (evenness/oddness) of every `calc(n)`. There certainly is a pattern, even though it’s more complicated than I thought at first. I still think it’s worth trying to find it. – Ole V.V. Dec 22 '16 at 07:22
  • Sorry, my mistake, antony. you were right. Ole, You might be right. If someone's interested, can we extend discussion in a [SO chatroom](http://chat.stackoverflow.com/rooms/130638). I had like to avoid lengthy comment discussions – rupinderjeet Dec 22 '16 at 07:23

2 Answers2

5

You cannot calculate calc for the range of values that you need. At i = j = 216 you get an integer overflow (the result of i * j * i * j becomes negative), and so the result of calc would likely be incorrect. Worse, your memoization map would explode in size as well.

The good news is that you don't actually need to calculate it. Look at this expression:

uniMatrix[i][j] = (((calc(i * j * i * j)) & 1) == 0) ? 0 : 1;

You don't need the calculated value, all you need to know is if the value is even or odd. And you can know that, using simple math. The heart of the calc implementation is this:

int answer = calc(n - 1) + 2 * calc(n - 2) + 3 * calc(n - 3);

And we know that the first 3 values are 1, 2, 3. In fact it's enough to know that they are odd, even, odd. The values that will follow after can be calculated based on simple math:

  • Multiplying any number by 2 will be even
  • Multiplying any number by 3 will remain what it was (odd if it was odd, even if it was even)
  • Adding an even number of odd numbers will be an even number
  • Adding an even number to any other number will have the even-ness of the other number

Now, let's see the first couple of values calculated by calc, and verify the logic of deciding if the value will be odd or even:

  • 0 -> 0 : even (given)
  • 1 -> 1 : odd (given)
  • 2 -> 2 : even (given)
  • 3 -> 3 : odd (given)
  • 4 -> 10 : even, because odd + even + odd is even
  • 5 -> 22 : even, because even + even + even is even
  • 6 -> 51 : odd, because odd + even + even is odd
  • 7 -> 125 : odd, because even + even + odd is odd
  • 8 -> 293 : odd, because even + even + odd is odd
  • 9 -> 696 : even, because odd + even + odd is even
  • 10 -> 1657 : odd, because odd + even + even is odd
  • 11 -> 3928 : even, because odd + even + odd is even
  • 12 -> 9330 : even, because even + even + even is even
  • 13 -> 22157 : odd, because odd + even + even is odd
  • 14 -> 52601 : odd, because even + even + odd is odd
  • 15 -> 124905 : odd, because even + even + odd is odd
  • 16 -> 296578 : even, because odd + even + odd is even

If you notice, a repeating pattern has emerged:

even odd even even odd odd odd

Only 0 breaks the pattern, in which case the value is given as 0. As such, your implementation can be rewritten as this, with no risk of stack overflow:

int[] pattern = {0, 1, 0, 0, 1, 1, 1};
for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
        long x = (long) i * j * i * j;
        if (x < 2) {
            uniMatrix[i][j] = (int) x;
        } else {
            uniMatrix[i][j] = pattern[(int)((x - 2) % pattern.length)];
        }
    }
}

For 0, since it doesn't follow the pattern, an exceptional treatment is necessary. For 1, x - 2 would be negative. Since the correct value for 0 and 1 is itself, the if (x < 2) branch is an easy way to treat these cases.

Rookie007
  • 1,229
  • 2
  • 18
  • 50
janos
  • 120,954
  • 29
  • 226
  • 236
  • 1
    You beat me with your answer by a few seconds. – Henry Dec 22 '16 at 07:44
  • @janos wow your answer is awesome. !!. but care to explaing `if(x<2) {}else {}` part ? why x<2 ? – Rookie007 Dec 22 '16 at 08:05
  • 2
    @Babel For 0, since it doesn't follow the pattern, an exceptional treatment is necessary. For 1, `x - 2` would be negative. Since the correct value for 0 and 1 is itself, the `if (x < 2)` branch is an easy way to treat these cases. I hope this answers your question. – janos Dec 22 '16 at 08:09
2

we are just interested in the result modulo 2 so all the calculations can be done modulo 2. The recursion reduces then to (writing calc' for calc modulo 2):

calc'(1) = 1
calc'(2) = 0
calc'(3) = 1
calc'(n) = (calc'(n-1) + calc'(n-3)) % 2

So we see that the next value depends only on two previous values in fixed distance and since the range of numbers for each value is finite (can be only 0 or 1), we know the sequence must be eventually periodic. A little experimentation shows the period is 7.

So the values modulo 2 are (for n >= 1):

calc'(n) = 
     1 if n%7 = 1,
     0 if n%7 = 2,
     1 if n%7 = 3,
     0 if n%7 = 4,
     0 if n%7 = 5,
     1 if n%7 = 6,
     1 if n%7 = 0.
Henry
  • 42,982
  • 7
  • 68
  • 84
  • I accepted janos's answer. It is more detailed and as you said, he posted first. But, thanks a lot for your answer. – rupinderjeet Dec 22 '16 at 09:26