Given an integer N, we need to find the number of integral pairs (x, y) such that x^2 + y^2 = N.
n can range from [0, 2147483647] and there can be a max of 100 such numbers in the input file.
From elementary geometry, we know that x^2 + y^2 = r represents a circle centered at (0,0) with a radius r. Therefore, x^2 + y^2 = n represents a circle with radius n. This is illustrated in figure 1. Now the problem is to find all possible solutions to this equation (in the first quadrant) where x, y are integers.
Figure 1
Bruteforcing all possible integral pairs of (x, y) upto n is equivalent to searching within the geometric space (grey rectangle) illustrated in fig 2.
Figure 2
Algorithmically this approach takes O(n^2) time.Consider the sample input as shown below (this was the file I received)
201105652147483646125743187325100058258911482843225525014751491418583200771022907856104149351831215306625372654318160225592832521474836431538292481
On my machine, brute force approach takes approx 14 hours (!!!) to solve this input. Obviously Facebook wasnt kidding when it hinted: "Too hard to bruteforce, switching to dp". Clearly, we are exploring a lot of unwanted points. One obvious way to improve this is to explore points bounded by the circle as shown by blue shaded rectangle in fig 3.
Figure 3
This is equivalent to looping x and y from 0 to n. This makes sense because x or y can never exceed . For example, with n = 25, if x = 5, y = 0 and vice versa. This reduces the complexity from O(n^2) to O(n) reducing execution time from 14 minutes to 80.5 sec!
Can we do any better? Why are we even exploring the blue rectangular region? Its sufficient if we examine all points on the arc of the circle in the first quadrant. i.e., we can increment x = 0 to x' (if x' is irrational number, its safe to use ceil(x')) and examine if the corresponding y points on the circle is an integer. y can be calculated using sqrt(n - x^2). Y is an integer if floor(y) == y. As illustrated in figure 4, all the points to the left of the line y=x is the mirror image of the points to the right. i.e, if we find a point, say (3, 4) to the left of y=x, then we will find a mirror image at (4, 3). Since the problem doesn't differentiate between these two. It is sufficient if we iterate x until the point x'. x' is given by .
Figure 4
With this, the complete algorithm in JAVA is listed below:
private static int getNumSumSquares(int n) { if(n==0) return 1; int iterations = (int) Math.ceil(Math.sqrt((n * 1.0)/2)); double y; int count = 0; for(int x=0; x <= iterations; x++) { y = Math.sqrt(n - (x*x)); //check if y = int. if(Math.floor(y) == y) count++; } return count; }
enjoy programming and pretty much everything. Wow, i just realized that!
ReplyDeletegood. by lakshman
I ended up with the same logic however did not quite think of it in geometric terms. Quite insightful!
ReplyDeleteI m not too sure when u say bruteforcing would be O(n^2). Is that always gonna be the case with bruteforcing? I wrote a brute force program which was O(n) i.e. ran from 0 to n. I used one variable to hold x^2 and another to hold y^2 and then in every passs of the loop, i incremented one and decremented another and checked whether they form a perfect square pair or not, if they did i incremented the count everytime and in the end the unique number of ways was count/2. Needless to say my code was much faster than O(n^2) code but still slower than more optimal code as it took 8 mins to solve the list.
ReplyDeleteline 6 should be:
ReplyDeleteint iterations = (int) Math.ceil(Math.sqrt((n * 1.0)/2)) + 1;
suppose n=18 (18=3^2+3^2)
in your program, iterations = 3;
then for-loop takes x from 0 to 2
no chance to check 3^2+3^2
:)
@akshay: thx
ReplyDeleteWell, i agree with that and I think devising a better mathematical algorithm thats not at all bruteforce would be infeasible as everything boils down to reducing the search space effectively to use just the things you need. Also, I could not quite understand the rationale behind doing floor(y) == y, I understand it gives the smallest value less than y which is essentially omitting the fractional part or rounding it. Do I understand correctly?
ReplyDelete@Ngat: Yea, you're right. Thanks for pointing this out. I must have gotten lucky in the competition. I'll correct my post.
ReplyDeleteThanks for taking time to reply to the questions mate. Great work!
ReplyDeleteI used brute force... hardly tuk a couple of seconds.... I tried with ur input too.... Used java
ReplyDeleteBy brute force do you mean the O(n^2) approach? i.e., looping x, y from 0 to n?
ReplyDelete