Tuesday, September 28, 2010

Puzzle: Java identity hashcode

Just for fun, I came up with this original (hopefully) puzzle: can you find a Java string whose hashCode() is equal to the numeric value that the string "represents"?

Here are some random guesses that fail to solve the puzzle but perhaps convey the idea:
System.out.println("42".hashCode()); // 1662

System.out.println(
   Integer.toBinaryString(
      "10101010101010101010101010101010"
         .hashCode()
   )
);  // 11001111110011111101111111110000

System.out.println(
   "Long.MAX_VALUE + Integer.valueOf(666)".hashCode()
   ==
    Long.MAX_VALUE + Integer.valueOf(666)
); // false
This is just for fun, so the rules are pretty relaxed. You can use base conversion, expressions, constants, widening conversion, autoboxing, etc. Anything creative is acceptable.

Here's a rather dumb solution that isn't mathematically/algorithmically interesting, using comment to break the circular dependency and unicode escapes to pretty much concoct any number you want.
System.out.println(
   "1337 // \u3902 \u016d \u06b0".hashCode()
   ==
    1337 // \u3902 \u016d \u06b0
); // true
Allowing comments really makes this puzzle rather trivial to solve:
System.out.println(
   "Integer.MAX_VALUE // nc7!''$".hashCode()
   ==
    Integer.MAX_VALUE // nc7!''$
); // true

Here's a more interesting solution that takes advantage of the atrocity that is Integer.getInteger (see discussion on stackoverflow).
System.out.println(
   "Integer.getInteger(new String(new char[] { '\u7a11', '\u0000', '\u5238' }), 42)".hashCode()
   ==
    Integer.getInteger(new String(new char[] { '\u7a11', '\u0000', '\u5238' }), 42)
); // true unless system properties is messed with
So it's definitely possible to solve this without using comments. This still breaks the circular dependency, though, this time by using a default value for Integer.getInteger.

The next step is obvious: try to solve this puzzle without any kind of "cheating".

Well, I don't know what is and isn't cheating anymore, but this breaks the circular dependency by multiplication with 0.
System.out.println(
   "0 * '\u31b1' * '\0' * '\u1167'".hashCode()
   ==
    0 * '\u31b1' * '\0' * '\u1167'    
); // true
Okay, so perhaps the next step is not to use Unicode escapes, because that really does make things somewhat easier.

4 comments:

  1. System.out.println("-2097072302".hashCode() + "-2097072301".hashCode());
    System.out.println(-2097072302L + -2097072301L);

    ReplyDelete
  2. System.out.println(("43624670".hashCode() - "43624678".hashCode()) == (43624670 - 43624678));

    ReplyDelete
  3. Nice attempts, but you have two strings, requiring two hashCode calls for each "solution". A "proper" solution should just be one string with one hashCode call.

    ReplyDelete
  4. "2552+2451410".hashCode() == 2552+2451410

    ReplyDelete