Facebook hackercup - Double squares

Posted on January 11, 2011

I attended the qualification round of the hackercup, the requirement was to solve at least one of three problems to proceed to the next phase.
I chose to try to solve the first problem, that took me a whole night of thinking, more than I would like to admit I have to say :P, though I've passed this first part of the cup.

The problem

A double-square number is an integer X which can be expressed as the sum of two perfect squares.
For example, 10 is a double-square because 10 = 32 + 12. Your task in this problem is, given X, determine the number of ways in which it can be written as the sum of two squares. For example, 10 can only be written as 32 + 12 (we don't count 12 + 32 as being different). On the other hand, 25 can be written as 52 + 02 or as 42 + 32.

Input

You should first read an integer N, the number of test cases. The next N lines will contain N values of X.
Constraints
0 < X < 2147483647
1 < N < 100
Example input
5
10
25
3
0
1

Output

For each value of X, you should output the number of ways to write X as the sum of two squares.
Example output
1
2
0
1
1

Here is my solution:

  1. import java.io.File;
  2. import java.io.FileNotFoundException;
  3. import java.util.ArrayList;
  4. import java.util.Collection;
  5. import java.util.HashSet;
  6. import java.util.Scanner;
  7.  
  8. public class Puzzle {
  9.  
  10. public static void main(String[] args) throws FileNotFoundException {
  11. Scanner scanner = new Scanner(new File(args[0]));
  12. //jump over the first, which tells how many ints there will be
  13. scanner.nextInt();
  14. while(scanner.hasNextInt()){
  15. int numberToTest = scanner.nextInt();
  16.  
  17. if(numberToTest==0){
  18. System.out.println(1);
  19. continue;
  20. }
  21.  
  22. Collection<String> foundSquares = new ArrayList<String>();
  23. int integerPart = (int)Math.sqrt(numberToTest);
  24. while(integerPart>0){
  25. if(pow2(integerPart) == numberToTest){
  26. //integerPart is a perfect square
  27. foundSquares.add(String.valueOf(integerPart));
  28. integerPart--;
  29. continue;
  30. }
  31.  
  32. int remainder = (int) (numberToTest - pow2(integerPart));
  33. if(pow2(integerPart)+pow2(remainder)==numberToTest){
  34. String toBeAdded = integerPart+":"+remainder;
  35. if(! foundSquares.contains(toBeAdded)){
  36. foundSquares.add(toBeAdded);
  37. }
  38. }
  39. if(isPerfectSquare(remainder) && pow2(integerPart)+remainder==numberToTest){
  40. String toBeAdded = String.valueOf(integerPart+":"+root2(remainder));
  41. String toBeAddedReversed = String.valueOf(root2(remainder)+":"+integerPart);
  42. if(!foundSquares.contains(toBeAdded) && !foundSquares.contains(toBeAddedReversed)){
  43. foundSquares.add(toBeAdded);
  44. }
  45. }
  46. integerPart--;
  47. }
  48.  
  49. System.out.println(foundSquares.size());
  50. }
  51. }
  52.  
  53. private static int pow2(int number){
  54. return (int)Math.pow(number, 2);
  55. }
  56.  
  57. private static int root2(int number){
  58. return (int)Math.pow(number, 0.5);
  59. }
  60.  
  61. public final static boolean isPerfectSquare(long number){
  62. if (number<0){
  63. return false;
  64. }
  65. long test = (long)(Math.sqrt(number));
  66. return test*test == number;
  67. }
  68. }

Some thoughts

After watching my code for some time I start to feel that something is wrong might be improved, in most cases this has proved right, this time is no exception.
I'm not talking about this kind of improvement, I'm talking about not having useless code that is also readable. I'll start by pointing out what is readable for me and what is not.

Chunks of readable code

I like the usage of java.util.Scanner to read files, this time this proved particularly useful due to the fact that integers were separated by spaces, so just saying "hey scanner give me the next integer you will find", seems good to me. Note that the default limiter is the whitespace, so, again, it's okay.

Due to the large use of powers and roots I chose to put them in two methods, also because of the fact that they were easy to confuse with each other since they only differ by the second parameter.

Chunks of not-so-readable-code

The whole part inside the second while (starting at line 24) takes a lot more than a few seconds to be understood. I believe that the part of the class in which "the stuff is happening" should be easily readable, so one can read what is going on in a few seconds, I believe it is ok then to have more complex private methods that go deeper in detail, handling what is delegated from the part I'm talking about.
In particular I find that the reason behind adding the found squares to a Collection is quite obscure, it has to be clarified, the way it is now it's simply wrong unreadable.


Get the source code here.

Browse the archive

blog comments powered by Disqus