Java: Cache results of computation? - java

I'm writing a program to calculate a value that is a measure of the similarity between two objects. The comparison is commutative, so compare(a, b) == compare(b, a).
The program's output to the console is a matrix of all results. However, since the matrix has each comparison twice ((a, b) and (b, a)), I'd like to save time by only calculating it once. What is the best way to cache these results?
Rough example of what the output looks like:
a b c
a 0 20 9001
b 20 0 333
c 9001 333 0

It sounds like you're already caching the results really - in the matrix. Just compute one "triangle" of the matrix and fill in the rest from that:
// Compute one triangle
for (int i=0; i < size; i++)
{
for (int j=0; j <= i; j++)
{
matrix[i][j] = computeValue(i, j);
}
}
// Now mirror it
for (int i = 0; i < size; i++)
{
for (int j = i + 1; j < size; j++)
{
matrix[i][j] = matrix[j][i];
}
}

As others have mentioned, you should just calculate one side of the triangle. You don't hove to copy it or even allocate space for it either. Just transform your x and y coordinates into a single index, and you can have an array that's a little over half the size of the full square matrix. eg:
class SymmetricMatrix {
private final double[];
/**
* #param size the size of one dimension of the matrix. eg: 3 for a 3x3 matrix.
*/
SymmetricMatrix(int size) {
matrix = new double[index(size) + 1];
}
private index(int x, int y) {
if (x > y) {
int tmp = x;
x = y;
y = tmp;
}
// now x <= y
i = (y * y + y) / 2 + x;
}
public double get(int x, int y) {
return matrix[index(x, y)];
}
public void set(int x, int y, double value) {
matrix[index(x, y)] = value;
}
}
This example uses double values, but you can easily adjust that (or even make it generic, if you want to use objects).
To fill it in:
SymmetricMatrix matrix = new SymmetricMatrix(size);
for (int y = 0; y < size; y++) {
for (int x = 0; x <= y; x++) {
matrix.set(x, y, /* value */);
}
}

Calculate only one triangle, and make an access function like
get(int x, int y) {
if (x > y) { return matrix[x][y] };
return matrix[y][x];

Looks like you don't need it for this task but if you have an expensive function and need to cache the result there is a very good thread safe method here:
http://www.javaspecialists.eu/archive/Issue125.html

You need to be rather careful with caching results of methods like compareTo and equals.
If you have N array instances you potentially need to cache N^2 comparison results. (This is of course application dependent ...)
Also, if your application creates and discards large numbers of (matrix) instances, then you may end up with lots of entries in the cache, resulting in garbage retention problems. You can mitigate this with weak references, but they make garbage collection significantly slower.
If I was doing this, I'd first profile the application to determine if the compareTo and equals methods are really bottlenecks. Then if they were, I'd consider using something other than caching to speed up the methods; e.g. storing a lazily computed hash with each array could speed up equals.

Related

How do I count Iterations in loop and compare it to the iterations from the other class?

I wrote code that solves polynomials in two ways:
normal form a0x+a1x...an*x
Horner method.
Now, what I need, is to count the amount of multiplications in both classes and compare them. So, my program could decide in which case the Horner method is a better way to solve a polynomial. I couldn't find any ways to count the multiplicators myself.
static int count_2=0;
static int count_1=0;
//---------------------------------------------------------------------------//
public static double evalSimple(double[] a, double x) {
double y_temp = 0;
double y=0;
int n=1;
for(int i=1; i < a.length ; ++i) {
y_temp = (a[i] * Math.pow(x,n));
y = y + y_temp;
++n;
++count_1;
}
System.out.println(count_1);
return y+a[0];
}
//here would be the class to compare the amount of the multiplikations
I tried to initiate the variables count_1 & count_2 and put them in the for-loop, but I didn't get how to return the value (not just to print them in console) of them for the test environment.
A function can always return only one value. But you can make this a result object. I made the example with only one of your methods:
class Result {
double solution;
int iterations;
}
public static Result evalHorner(double[] a, double x) {
Result result = new Result();
for (int i = a.length - 1; i >= 0; i--) {
result.solution = a[i] + result.solution * x;
++result.iterations;
}
return result;
}
Also note that I did not use a global counter variable, so the returned value is fresh for the exact invocation.

Find triplets (a, b ,c) in an array such that a + b + c = 0

I want to find all distinct triplets (a, b, c) in an array such that a + b + c = 0.
I implemented the algorithm in java but I am getting TLE when the input is large (for example 100,000 zeroes, etc).
For 100,000 zeroes, it should output (0, 0, 0) only.
Can someone give some idea about how to speed this up?
Below is the function which I have written. It takes an array as input and returns all unique triplets having the desired property as a list.
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ll = new ArrayList<List<Integer>>();
for(int i = 0; i < nums.length - 1; i++){
int x = nums[i];
int start = i + 1;
int end = nums.length - 1;
int sum = -x;
while(start < end){
int y = nums[start] + nums[end];
if(y == sum){
List<Integer> list = new ArrayList<Integer>();
list.add(nums[start]);
list.add(nums[end]);
list.add(x);
Collections.sort(list);
ll.add(list);
}
if(y < sum)
start++;
else
end--;
}
}
return ll.stream()
.distinct()
.collect(Collectors.toList());
}
I think that there is nothing you can do about the time complexity. Two indices must explore the array independently (except for starting/ending points), while the third can be constrained, like in your algorithm, which means that the complexity is O(n2). This dominates the preliminary sorting of the array, which is O(n·log(n)), and also a “demultiplication” step, which is O(n).
I wrote “demultiplication” because a “deduplication” is not desirable: suppose the array is [-1,-1,0,2]. Deduplicating it would eliminate the only solution. But a solution can't contain an integer more than twice, unless it's 0, in which case [0,0,0] is a solution. All integers appearing more than twice, or thrice in the case of 0, are redundant and can be eliminated in one pass after sorting and before the main algorithm.
As for the factor, it could be improved by limiting the exploration to what makes sense. I would modify your algorithm by making the pair of indices that you move until they meet, start outwards from where they meet, until the lower one hits the major index, or the upper one hits the end of the array. The starting point of the scan can be remembered across scans, adjusting it downwards as the major index moves upwards. If the starting point (actually a starting pair of adjacent indices) is outside the current range, the scan can be omitted tout court. Finding the initial starting point is an additional part of the algorithm which, after sorting, could be O(log(n)), but a very simple O(n) version would do just as well.
I have no time now to translate all the above into Java code, sorry. All I can do now is jot down the “demultiplication” code (untested) that goes right after the sorting of the array:
int len = 1;
int last = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
int x = nums[i];
if (x != last) {
nums[len++] = x;
last = x;
count = 1;
} else if (count < 2 || x == 0 && count < 3) {
nums[len++] = x;
count++;
}
}
// use len instead of nums.length from this point on
The big time component I see, is that for the 100,000 zeroes example, you will hitting the if (y == sum) block for every single possible case. This appears to be the worst case for performance since you will never skip that block.
The largest improvement I can see is to first de-duplicate your input. Unfortunately sets won't work as we need still maintain up to three of the same entry. Thus, my recommendation is, after your sort, to loop through the input array and whenever you encounter more than three copies of a number in a row, remove the extras. They are not needed for the problem and just waste time.
You could create a List (an implementation of that is ArrayList) to store the combinations you already had. Always store a new value in the format of
a,b,c
where a <= b <= c
so, whenever you get a combination which may or may not already have been found, generate a String in the same format and check whether it is present in your List. If so, then do not add it. Otherwise add it to your List. After this you could convert the found values into numeric values. If you want to quicken it up, you could create a class like:
class XYZ {
public int x;
public int y;
public int z;
public XYZ(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public isMatch(int x, int y, int z) {
return (this.x == x) &&
(this.y == y) &&
(this.z == z);
}
public static boolean anyMatch(List<XYZ> list, int x, int y, int z) {
for (XYZ xyz : list) {
if (xyz.isMatch(x, y, z)) return true;
}
return false;
}
public static void addIfNotExists(List<XYZ> list, int x, int y, int z) {
if (!anyMatch(list, x, y, z)) list.add(new XYZ(x, y, z));
}
}
and you could use this class for your purpose, just make sure that x <= y <= z.
The filtering of non-unique triplets at the end can be eliminated by using a hash-table that stores the triplets in a sorted order, so all combinations of a triplet (with different ordering) gets stored exactly once.
Use a hashmap/hashset instead of an arraylist.
HashSet<List<Integer>> ll = new HashSet<List<Integer>>();
. . .
list.addAll(a,b,c)
Collections.sort(list)
ll.add(list)
In addition to this, You could also use another lookup table to ensure each repeating item in nums[] is used to calculate the triplets only once.
lookup_table = HashMap();
for(int i = 0; i < nums.length - 1; i++){
// we have already found triplets starting from nums[i]
// eg. [-1,-1,0,1], we don't need to calculate
// the same triplets for the second '-1'.
if (lookup_table.contains(nums[i]))
continue;
// Mark nums[i] as 'solved'
lookup_table.add(nums[i])
// usual processing here
int x = nums[i];
Or, since your nums[] list is already sorted, you could just skip repeating items, doing away the need for another lookup table.
i = 0;
while (i < nums.length - 1){
// we have already found triplets starting from nums[i]
// eg. [-1,-1,0,1], we don't need to calculate
// the same triplets for the second '-1'.
x = nums[i];
// skip repeating items
while (x == nums[i++]);
// usual processing here
. . .
i++;
}
and then you could just return the hashset as a list at the end.

find the value pair in 2 sorted arrays (1 value from each array) where the sum is closest to a target value

The original question has a list of unsorted list of 2 integers. To simplify this problem let's just consider the input is 2 sorted arrays of integers and an integer target. Value pair can repeat if there are more than 1 solution pair.
For example: [7,8,14],[5,10,14] target: 20
The solution is [14, 5] as 14 from first array and 5 from second array sums 19 which is closest to 20.
My solution was to loop through both array from beginning to end and compare against a tracked minimum difference and update if new difference is smaller.
But this is brute force. Is there any better solution?
Most solutions I found online was to find the target from the same array, is there any similarities between 2 arrays target problem and 1 array?
One key insight: Given a pair (x, y) whose sum is higher than the target, that sum is closer than the sum of any pair (x, y'), where y' > y. Conversely, if the sum of (x, y) is lower than the target, that sum is closer than the sum of any pair (x', y) where x' < x.
This yields an algorithm in linear time:
Start the first element of list X and the last element of list Y
Check if it's the best pair so far (if so, remember it)
If that sum is less than the target, move to the next higher element of X. If that sum is greater than the target, move to the next lower element of Y
Loop steps 2 - 3 until you run out of elements in X or Y
In Java:
private static Pair<Integer, Integer> findClosestSum(List<Integer> X, List<Integer> Y, int target) {
double bestDifference = Integer.MAX_VALUE;
Pair<Integer, Integer> bestPair = null;
int xIndex = 0;
int yIndex = Y.size() - 1;
while (true) {
double sum = X.get(xIndex) + Y.get(yIndex);
double difference = Math.abs(sum - target);
if (difference < bestDifference) {
bestPair = new Pair<>(X.get(xIndex), Y.get(yIndex));
bestDifference = difference;
}
if (sum > target) {
yIndex -= 1;
if (yIndex < 0) {
return bestPair;
}
} else if (sum < target) {
xIndex += 1;
if (xIndex == X.size()) {
return bestPair;
}
} else {
// Perfect match :)
return bestPair;
}
}
}
You can prove this algorithm is exhaustive through the logic in the starting paragraph. For any pair that wasn't visited, there must be a pair including one of its two elements that was visited, and which has a sum strictly closer to the target.
EDIT: If you only want sums which are less than the target (not those which overshoot), the same logic still applies. In the overshoot case, (x, y') is just as invalid as (x, y), and therefore it cannot be a better candidate sum. In this case, only step 2 needs to be modified, to store the sum only if it's the closest non-exceeding sum so far.
Thank you for your algorithm, I have implemented my logic. Yes it does need to be the closest pair below target so I have made code changes accordingly. Since the inputs could be duplicates hence I made sure the handle that as well. Also result could be multiple so that is handled as well. Let me know if you find any potential optimization. Here is the code:
public static List<List<Integer>> findClosest(int[] x, int[] y, int target){
List<List<Integer>> result = new ArrayList<List<Integer>>();
int[] pair = new int[2];
int bestDiff = Integer.MIN_VALUE;
int xIndex = 0;
int yIndex = y.length - 1;
//while left doesn't reach left end and right doesn't reach right end
while(xIndex < x.length && yIndex >= 0){
int xValue = x[xIndex];
int yValue = y[yIndex];
int diff = xValue + yValue - target;
//values greater than target, y pointer go right
if(diff > 0){
yIndex--;
while(yIndex > 0 && yValue == y[yIndex - 1]) yIndex--;
}else{//combined == 0 which match target and < 0 which means the sum is less than target
//duplicates result, just add
if(diff == bestDiff){
result.add(Arrays.asList(xValue, yValue));
}
//found better pair, clear array and add new pair
else if(diff > bestDiff){
result.clear();
result.add(Arrays.asList(xValue, yValue));
bestDiff = diff;
}
xIndex++;
}
}
return result;
}

Finding the closest object (barrier) to the player

I have a program that checks distance and whether or not the player has collided with a barrier. I now am trying to calculate which barrier in the array of barriers is the closest to the moving player, then returning the index of that barrier.
Here is what I have so far:
public static int closestBarrier(GameObject object, GameObject[] barriers)
// TODO stub
{
int closest = 0;
for (int i = 0; i < barriers.length - 1; i++) {
if (Math.sqrt((object.getX() - barriers[i].getX())
* (object.getX() - barriers[i].getX()))
+ ((object.getY() - barriers[i].getY()) * (object.getY() - barriers[i]
.getY())) <= Math
.sqrt((object.getX() - barriers[i + 1].getX())
* (object.getX() - barriers[i + 1].getX()))
+ ((object.getY() - barriers[i + 1].getY()) * (object
.getY() - barriers[i + 1].getY()))) {
closest = i;
} else
closest = i + 1;
}
return closest;
}
I am still new to java so I understand what I already have probably isn't very efficient or the best method of doing it (or even right at all!?).
I'd refactor it a wee bit simpler like so:
public static int closestBarrier(GameObject object, GameObject[] barriers)
{
int closest = -1;
float minDistSq = Float.MAX_VALUE;//ridiculously large value to start
for (int i = 0; i < barriers.length - 1; i++) {
GameObject curr = barriers[i];//current
float dx = (object.getX()-curr.getX());
float dy = (object.getY()-curr.getY());
float distSq = dx*dx+dy*dy;//use the squared distance
if(distSq < minDistSq) {//find the smallest and remember the id
minDistSq = distSq;
closest = i;
}
}
return closest;
}
This way you're doing less distance checks (your version does two distance checks per iteration) and also you only need the id, not the actual distance, so you can gain a bit of speed by not using Math.sqrt() and simply using the squared distance instead.
Another idea I can think of depends on the layout. Say you have a top down vertical scroller, you would start by checking the y property of your obstacle. If you have a hash of them or a sorted list, for an object at the bottom of the screen you would start loop from the largest y barrier to the smallest. Once you found the closest barriers on the Y axis, if there are more than 1 you can check for the closest on the x axis. You wouldn't need to use square or square root as you're basically splitting the checks from 1 in 2D per barrier to 2 checks in 1D, narrowing down your barrier and discarding far away barriers instead of checking against every single object all the time.
An even more advanced version would be using space partitioning but hopefully you won't need it for a simple game while learning.

Compare elements of the same array

I have problem with comparing the value of array elements.
e.g. I wanted to compare the value of index 0 and index 2, and index 1 to index 3 and so on.
With the code below I suppose to get the result of numOfdifferentShape is 2 but I get 3.
How can I solve this problem? :-(
int numOfdifferentShape=0;
myArray = {40.0, 40.0, 40.0, 40.0, 80.0, 40.0, 40.0, 40.0}
for (int a=0; int a<myArray.size(); a=a+2)
{
for (int b=a+2; b<myArray.size; b=b+2)
{
if (!(myArray.get(a).equals(myArray.get(b) && myArray.get(a+1).equals(b+1)))
numOfdifferentShape++;
break;
}
}
There are several syntax errors in this code, but since TofuBeer has already pointed them out in the comments, I'll move on the the design and logic.
Going from the code, I'm assuming you don't have much experience with Java, and perhaps not with programming at all. So I'm going to go slowly here. I hope you aren't insulted by my explanations.
You say you are trying to find out how many of the objects which you are storing (as two ints) in your array are equal. To do this, you have to keep track of what unique objects you have already seen. Then you compare each object the list of unique objects and, if it doesn't match any of them, add it to the list. This is the basic algorithm.
Now, have you noticed that I keep using the word "object" in my description? When that happens, it usually means you should be making a class. I would make a simple one like this, holding the two integers:
class Box { // or whatever the objects are called
private final int height;
private final int width;
public Box(int h, int w) {
height = h;
width = w;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof Box))
return false;
Box b = (Box) other;
return b.height == height && b.width == width;
}
#Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + this.height;
hash = 97 * hash + this.width;
return hash;
}
}
Try to understand what each part of this code does (especially if this is actually your homework). Once you've got it, move on to the next part: doing the calculation that you were trying to do.
Let's say you have an array of Boxes, like this:
Box[] boxes = {
new Box(40, 40), new Box(40, 40), new Box(80, 40), new Box(40, 40)
};
(I can't tell if you're using an array or a list, so I'm just picking one to demonstrate.)
I already gave the algorithm for finding the number of unique items, so I'll show you how I would write it:
List<Box> unique = new ArrayList<Box>();
for (Box box : boxes) {
if (!unique.contains(box)) { // this is why I implemented equals() and hashCode()!
unique.add(box);
}
}
int numOfDifferentShape = unique.size();
This is much easier than trying to keep track of two ints for each object, plus it has the advantage that you can't get your array indices confused.
You could do this even more easily with a Set. It would look something like this:
Set<Box> boxSet = new HashSet<Box>();
for (Box b : boxes)
boxSet.add(b);
int numOfDifferentShape = boxSet.size();
Note that these last two snippets use features from Java 1.5, so I don't know if you've run into them before.
Does this make things clearer?
for (int i = 0; i < (myArray.size() - 2); ++i)
{
if (myArray[i] != myArray[i + 2])
++numOfdifferentShapes;
}
You have two loops, your description suggests you only want one.
You need to do bounds checking - do you want the n+2 to wrap to the start to the start of the array when it exceeds the length?
I think you have a parentheses problem. You wrote:
if (!(myArray.get(a).equals(myArray.get(b) && myArray.get(a+1).equals(b+1)))
when I think you mean:
if (!(myArray.get(a).equals(myArray.get(b)) && myArray.get(a+1).equals(b+1))
Also, in the same line, instead of:
equals(b+1)
don't you mean
myArray.get(b+1)
I have array list e.g.
{40,40,80,20,40,40} I wanted to
compare the elements. Even number of
index (e.g. index 0, index 2, index 4
etc) represents Height of an object
and Odd number of Index (e.g. index 1,
index 3 ec) represent Width of an
object. So, with the code above,
Object 1 (index 0 and 1).
Why not make an array of a Dimension class, something like this:
public class Dimension
{
private final int width;
private final int height;
public Dimension(final int w,
final int h)
{
width = w;
height = h;
}
public int getWidth()
{
return (width);
}
public int getHeight()
{
return (height);
}
}
then do a for loop something like this:
for(int i = 0; i < array.length; i += 2)
{
final Dimension a;
final Dimension b;
a = array[i];
b = array[i + 1];
// compare a.getLength() to b.getLength()
// or
// compare a.getWidth() to b.getWidth()
}
It is usually a bad idea to try and be "tricky" - saying even ones are with and odd ones are length is being tricky... bad idea IMO.

Categories

Resources