Good hashcode function for 2D coordinates - java

I would like to use a HashMap
to map (x, y) coordinates to values.
What is a good hashCode() function definition?
In this case, I am only storing integer coordinates of the form (x, y)
where y - x = 0, 1, ..., M - 1 for some parameter M.

To get unique Value from two numbers, you can use bijective algorithm described in here
< x; y >= x + (y + ( (( x +1 ) /2) * (( x +1 ) /2) ) )
This will give you unquie value , which can be used for hashcode
public int hashCode()
{
int tmp = ( y + ((x+1)/2));
return x + ( tmp * tmp);
}

I generally use Objects.hash(Object... value) for generating hash code for a sequence of items.
The hash code is generated as if all the input values were placed into an array, and that array were hashed by calling Arrays.hashCode(Object[]).
#Override
public int hashCode() {
return Objects.hash(x, y);
}
Use Objects.hash(x, y, z) for 3D coordinates.
If you wish to handle it manually, you could do compute hashCode using:-
// For 2D coordinates
hashCode = LARGE_PRIME * X + Y;
// For 3D coordinates
hashCode = LARGE_PRIME^2 * X + LARGE_PRIME * Y + Z;

To calculate a hash code for objects with several properties, often a generic solution is implemented. This implementation uses a constant factor to combine the properties, the value of the factor is a subject of discussions. It seems that a factor of 33 or 397 will often result in a good distribution of hash codes, so they are suited for dictionaries.
This is a small example in C#, though it should be easily adabtable to Java:
public override int GetHashCode()
{
unchecked // integer overflows are accepted here
{
int hashCode = 0;
hashCode = (hashCode * 397) ^ this.Hue.GetHashCode();
hashCode = (hashCode * 397) ^ this.Saturation.GetHashCode();
hashCode = (hashCode * 397) ^ this.Luminance.GetHashCode();
return hashCode;
}
}
This scheme should also work for your coordinates, simply replace the properties with the X and Y value. Note that we should prevent integer overflow exceptions, in DotNet this can be achieved by using the unchecked block.

Have you considered simply shifting either x or y by half the available bits?
For "classic" 8bit thats only 16 cells/axis, but with todays "standard" 32bit it grows to over 65k cells/axis.
#override
public int hashCode() {
return x | (y << 15);
}
For obvious reasons this only works as long as both x and y are in between 0 and 0xFFFF (0-65535, inclusive), but thats plenty of space, more than 4.2bio cells.
Edit:
Another option, but that requires you to know the actual size, would be to do x + y * width (where width ofc is in the direction of x)

That depends on what you intend on using the hash code for:
If you plan on using it as a sort of index, E.g. knowing x and y will hash into an index where (x, y) data is stored, it's better to use a vector for such a thing.
Coordinates[][] coordinatesBucket = new Coordinates[maxY][maxX];
But if you absolutely must have a unique hash for every (x, y) combination, then try applying the coordinates to a decimal table (rather than adding or multiplying). For example, x=20 y=40 would give you the simple and unique code xy=2040.

Related

Representing the equation of a line in a variable in Java

I don't think I need code here, but just so you can see what I'm looking at:
public class Valuation {
//line is a monotonic (non-decreasing. Could be constant at points)
//line in 2D space where x=0 -> y=0 and x=1 -> y=1
//the gradient cannot be infinite
//line is only defined between x=0 and x=1. Can catch when arguments to
//functions are unacceptable given this.
LineEquation line;
float cut(float from, float value){
//Using 'from' as x, return the least value x' where 'value' is the difference
//between the y value returned by x and the y value returned by x'
}
float eval(float from, float to){
//require to > from
//return the difference between the y value returned by 'to'
//and the y value returned by 'from'
}
The question I have is how do I represent a line/curve like this in Java? I can verify the lines given fit the requirements that I have, but I want to have this LineEquation class to be able to handle essentially any line that fits these requirements. These could be quadratic curves or lines where we have something like, when x is between 0 and 0.5, the equation is a, and then when x is between 0.5 and 1, the equation is b. I got frustrated thinking of all the ways you could describe a line that meets the specifications, and then how I would go through them all, and how I would have to deal with all the different types in different ways. Unfortunately I do not have the vocabulary to find a library that has what I want.
If you're using Java 8, then probably the simplest thing to do would be to store the curve as a Function<Float,Float>, which can implement any kind of equation for any kind of curve, provided y is single-valued for any given x, and x always falls within range for a float.
Your class might look like this.
public class Valuation {
final Function<Float,Float> curve;
public Valuation(final Function<Float,Float> curve) {
this.curve = curve;
}
float eval(float from, float to){
return curve.apply(to) - curve.apply(from);
}
}
Then you can create these with calls such as
new Valuation( x -> ( x * x + 2 * x + 3 ))
for a typical monotonic quadratic, or
new Valuation( x -> ( x > 0.5 ? 3 * x : 1 + x ))
for a piecewise function consisting of two linear sections.
I haven't shown the code for cut. Had to leave something up to you!

HashCode implementation for coordinates

To put you in context: I have a coordinate class where I have two attributes: x and y and those represent pixels. I have redefined the equals method and in it I have given a tolerance of C pixels to determine if two coordinates are equal.
I also tried to redefine the hashCode method because I need to put those coordinates as a key for a map. Here's my redefinition:
#Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + this.x;
hash = 67 * hash + this.y;
return hash;
}
Now what I want to do is to create a Coordinate whenever a click occurs and get() that coordinate from my map even if it is up to C pixels from the other coordinate. I think my problem is that since it is not the same x and y, they dont have the same hashcode so get() only works if the click occurs exactly at the same x and y. Is there a way to implement my hashcode so that it has a "tolerance" like my equals() does? Am I doing anything wrong?

How to get the difference between two integers

I'm trying to make a program which uses areas and each area has an id (e.g.:1;1) and I'm trying to get the size of a specified area with comparing two ids but this method returns 1 as size.
//Pos1 = -2;3 Pos2 = 0;1
int x = Integer.valueOf(pos2.x).compareTo(pos1.x);
int y = Integer.valueOf(pos2.y).compareTo(pos1.y);
int size = Math.abs(x * y);
So how can I make this work?
compareTo is not supposed to return the exact difference between two values. From the docs:
Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
Use
int x = Math.abs(pos2.x-pos1.x);
int y = Math.abs(pos2.y-pos1.y);
int size = x * y;
The result is 1 because compareTo() returns 0 if the arguments are equal, -1 if the first int is smaller than the second one and 1 if the second one is smaller (you can read more about it in the official docs).
--> You should not use this method for this purpose. Calculate the difference instead:
int x = pos2.x - pos1.x;
int y = pos2.y - pos1.y;
int size = Math.abs(x * y);
Purpose of Integer.compareTo ( ) is not to find the difference between two Integer objects. Its purpose is to specify the ordering between two Integer objects when they are sorted by Arrays.sort ( ) or Collections.sort( ).
You may find the difference in following ways:
int x = pos2.x - pos1.x;
int y = pos2.y - pos1.y;
int size = Math.abs(x * y);
compareTo will return -1 if pos2.x is smaller than pos1.y, 0 if they are the same, and 1 if pos2.x is greater than pos1.y.
Use this instead:
int size = Math.abs((pos2.x-pos1.x)*(pos2.y-pos1.y));

Java Comparable Class - Comparison method violates its general contract

I'm writing an isometric game that renders objects based on their Y coordinate, using a comparable class, sorting by their Y value, which changes. I am getting the error "Comparison method violates its general contract!" and have read about how to return a negative number, 0, or a positive number so I have implemented this:
public boolean equals(Entity e) {
if ((e.y-y)==0)
return (e.id == id);
return (e.y == y);
}
public int compareTo(Entity e) {
if ((e.y-y)==0)
return (e.id - id);
return (int) (e.y - y); // Render order by y coordinate
}
But I'm still getting the error. Is the sorting not going to work if the values change or am I doing something else wrong?
The equals method is not involved in the contract, so we can ignore it.
I suspect that the problem is caused by integer overflow. The problem is that x - y does not always give you a positive answer if x > y and a negative number if x < y. If the difference between the numbers is large enough, then the express x - y will overflow and the result will have the wrong sign.
If that is the problem, then the simple solution is to use Integer.compare(x, y) instead of x - y
Another possibility is that the entities are being mutated at the same time as you are (for example) sorting them.
Float.compare(x, y) has worked much better.
I'd assumed that x and y were int. If they are float then the true cause of the problem is harder to understand. Either way, using Float.compare(x, y) is a better solution.
But if x and y are actually int, then using Float.compare(x, y) will give you incorrect answers for some x and y values. For close values of x and y with large enough magnitudes, the int to float conversion will lose precision, and Float.compare will say they are equal.

Hash function for 2D point in limited Euclidean space

I am storing a lot of objects with geographically positions as 2D points (x,y) in granularity of meters. To represent the world I am using a grid divided in cells of 1 square km. Currently I am using HashMap<Position, Object> for this. Any other map or appropriate data structure is fine, but I the solution works so I am only interested in solving the details.
I have been reading a lot about making good hash functions, specifically for 2D points. So far, no solutions have been really good (rated in terms of as collision-free as possible).
To test some ideas I wrote a very simple java program to generate hash codes for points from an arbitrary number (-1000,-1000) to (1000, 1000) (x1, y1 -> x2,y2) and storing them in a HashSet<Integer> and this is my result:
# java HashTest
4000000 number of unique positions
test1: 3936031 (63969 buckets, 1,60%) collisions using Objects.hash(x,y)
test2: 0 (4000000 buckets, 100,00%) collisions using (x << 16) + y
test3: 3998000 (2000 buckets, 0,05%) collisions using x
test4: 3924037 (75963 buckets, 1,90%) collisions using x*37 + y
test5: 3996001 (3999 buckets, 0,10%) collisions using x*37 + y*37
test6: 3924224 (75776 buckets, 1,89%) collisions using x*37 ^ y
test7: 3899671 (100329 buckets, 2,51%) collisions using x*37 ^ y*37
test8: 0 (4000000 buckets, 100,00%) collisions using PerfectlyHashThem
test9: 0 (4000000 buckets, 100,00%) collisions using x << 16 | (y & 0xFFFF)
Legend: number of collisions , buckets(collisions), perc(collisions)
Most of these hash functions perform really bad. In fact, the only good solution is the one that shifts x to the first 16 bits of the integer. The limitation, I guess, is that the two most distant points must not be more than the square root of Integer.MAX_INT, i.e. area must be less than 46 340 square km.
This is my test function (just copied for each new hash function):
public void test1() {
HashSet<Integer> hashCodes = new HashSet<Integer>();
int collisions = 0;
for (int x = -MAX_VALUE; x < MAX_VALUE; ++x) {
for (int y = -MAX_VALUE; y < MAX_VALUE; ++y) {
final int hashCode = Objects.hash(x,y);
if (hashCodes.contains(hashCode))
collisions++;
hashCodes.add(hashCode);
}
}
System.console().format("test1: %1$s (%2$s buckets, %3$.2f%%) collisions using Objects.hash(x,y)\n", collisions, buckets(collisions), perc(collisions));
}
Am I thinking wrong here? Should I fine-tune the primes to get better results?
Edits:
Added more hash functions (test8 and test9). test8 comes from the reponse by #nawfal in Mapping two integers to one, in a unique and deterministic way (converted from short to int).
public void test1() {
int MAX_VALUE = 1000;
HashSet<Integer> hashCodes = new HashSet<Integer>();
int collisions = 0;
for (int x = -MAX_VALUE; x < MAX_VALUE; ++x) {
for (int y = -MAX_VALUE; y < MAX_VALUE; ++y) {
final int hashCode = ((x+MAX_VALUE)<<16)|((y+MAX_VALUE)&0xFFFF);
if (hashCodes.contains(hashCode))
collisions++;
hashCodes.add(hashCode);
}
}
System.out.println("Collisions: " + collisions + " // Buckets: " + hashCodes.size());
}
Prints: Collisions: 0 // Buckets: 4000000
I a similar question with the answer being to use a Cantor pairing function. Here:
Mapping two integers to one, in a unique and deterministic way.
The Cantor pairing function can be used for negative integers as well, using bijection.

Categories