How to make a range tree implementation thread safe - java

I've implemented a range tree which supports updates in the form of incrementing or decrementing the count of a specific value. It can also query the number of values lower or equal to the value provided.
The range tree has been tested to work in a single threaded environment, however I would like to know how to modify the implementation such that it can be updated and queried concurrently.
I know a simple solution would be to synchronise methods that access this tree, but I would like to know if there are ways to make RangeTree thread safe by itself with minimal affect on performance.
public class RangeTree {
public static final int ROOT_NODE = 0;
private int[] count;
private int[] min;
private int[] max;
private int levels;
private int lastLevelSize;
public RangeTree(int maxValue) {
levels = 1;
lastLevelSize = 1;
while (lastLevelSize <= maxValue) {
levels++;
lastLevelSize = lastLevelSize << 1;
}
int alloc = lastLevelSize * 2;
count = new int[alloc];
min = new int[alloc];
max = new int[alloc];
int step = lastLevelSize;
int pointer = ROOT_NODE;
for (int i = 0; i < levels; i++) {
int current = 0;
while (current < lastLevelSize) {
min[pointer] = current;
max[pointer] = current + step - 1;
current += step;
pointer++;
}
step = step >> 1;
}
}
public void register(int value) {
int index = lastLevelSize - 1 + value;
count[index]++;
walkAndRefresh(index);
}
public void unregister(int value) {
int index = lastLevelSize - 1 + value;
count[index]--;
walkAndRefresh(index);
}
private void walkAndRefresh(int node) {
int currentNode = node;
while (currentNode != ROOT_NODE) {
currentNode = (currentNode - 1) >> 1;
count[currentNode] = count[currentNode * 2 + 1] + count[currentNode * 2 + 2];
}
}
public int countLesserOrEq(int value) {
return countLesserOrEq0(value, ROOT_NODE);
}
private int countLesserOrEq0(int value, int node) {
if (max[node] <= value) {
return count[node];
} else if (min[node] > value) {
return 0;
}
return countLesserOrEq0(value, node * 2 + 1) + countLesserOrEq0(value, node * 2 + 2);
}
}

Louis Wasserman is right, this is a difficult question. But it may have simple solution.
Depending on your updates/reads ratio and the contention for the data, it may be useful to use ReadWriteLock instead of synchronized.
Another solution which may be efficient in some cases (depends on your workload) is to copy whole RangeTree object before update and then switch the reference to 'actual' RangeTree. Like it is done in CopyOnWriteArrayList. But this also violates atomic consistency agreement and leads us to eventual consistency.

Related

Sorting List of Objects by their int variable

I have a class (ClassOne) that has a list of instanced Classes(Process) and i'm trying to figure out how to sort them based on their priority int.
public class ClassOne
{
static List<Process> processList = new ArrayList<Process>();
public static void main(String[] args)
{
//hardcoded for example
processList.add(new Process(3));
processList.add(new Process(1));
processList.add(new Process(2));
The processes are current not ordered in the List by priority, so I call insertion sort
}
//Im pretty sure this is changing their priority instead of where they are in the List, but i dont know how to change it
public static void InsertionSort()
{
int n = processList.size();
for (int i = 1; i < n; ++i)
{
int key = processList.get(i).priority;
int j = i - 1;
/* Move elements of processList.get(0..i-1]).priority, that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && processList.get(j).priority > key)
{
processList.get(j+1).priority = processList.get(j).priority;
j = j - 1;
}
processList.get(j + 1).priority = key;
}
}
public class Process
{
int priority;
public Process(int tempPriority)
{
priority = tempPriority;
}
}
any sorting method works, I want want to sort each Process object in processList by their priority, from least to greatest.
code after trying first solution:
public static void InsertionSort()
{
System.out.println(processList.get(0).name);
System.out.println(processList.get(1).name);
int n = processList.size();
for (int i = 1; i < n; ++i)
{
int key = processList.get(i).priority;
int j = i - 1;
//The method set(int, Process) in the type List<Process> is not applicable for the arguments (int, int)
/* Move elements of processList.get(0..i-1]).priority, that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && processList.get(j).priority > key)
{
processList.set(j + 1, processList.get(j));
j = j - 1;
}
processList.set(j + 1, processList.get(i));
System.out.println("Queue Sorted");
System.out.println(processList.get(0).name);
System.out.println(processList.get(1).name);
}
Use List#set to set an element at an index.
processList.get(j+1).priority = processList.get(j).priority;
becomes
processList.set(j+1, processList.get(j));
And
processList.get(j + 1).priority = key;
becomes
processList.set(j+1, processList.get(i));
with java 8+ you can try this code below:
import java.util.*;
import java.util.stream.Collectors;
public static void InsertionSort()
{
System.out.println(processList.get(0).name);
System.out.println(processList.get(1).name);
System.out.println(processList.get(2).name);
processList=processList.stream()
.sorted((o1,o2)->{return o1.priority-o2.priority;})
.collect(Collectors.toList());
System.out.println(processList.get(0).name);
System.out.println(processList.get(1).name);
System.out.println(processList.get(2).name);
}

How can I build this tree with O(n) space complexity?

The Problem
Given a set of integers, find a subset of those integers which sum to 100,000,000.
Solution
I am attempting to build a tree containing all the combinations of the given set along with the sum. For example, if the given set looked like 0,1,2, I would build the following tree, checking the sum at each node:
{}
{} {0}
{} {1} {0} {0,1}
{} {2} {1} {1,2} {0} {2} {0,1} {0,1,2}
Since I keep both the array of integers at each node and the sum, I should only need the bottom (current) level of the tree in memory.
Issues
My current implementation will maintain the entire tree in memory and therefore uses way too much heap space.
How can I change my current implementation so that the GC will take care of my upper tree levels?
(At the moment I am just throwing a RuntimeException when I have found the target sum but this is obviously just for playing around)
public class RecursiveSolver {
static final int target = 100000000;
static final int[] set = new int[]{98374328, 234234123, 2341234, 123412344, etc...};
Tree initTree() {
return nextLevel(new Tree(null), 0);
}
Tree nextLevel(Tree currentLocation, int current) {
if (current == set.length) { return null; }
else if (currentLocation.sum == target) throw new RuntimeException(currentLocation.getText());
else {
currentLocation.left = nextLevel(currentLocation.copy(), current + 1);
Tree right = currentLocation.copy();
right.value = add(currentLocation.value, set[current]);
right.sum = currentLocation.sum + set[current];
currentLocation.right = nextLevel(right, current + 1);
return currentLocation;
}
}
int[] add(int[] array, int digit) {
if (array == null) {
return new int[]{digit};
}
int[] newValue = new int[array.length + 1];
for (int i = 0; i < array.length; i++) {
newValue[i] = array[i];
}
newValue[array.length] = digit;
return newValue;
}
public static void main(String[] args) {
RecursiveSolver rs = new RecursiveSolver();
Tree subsetTree = rs.initTree();
}
}
class Tree {
Tree left;
Tree right;
int[] value;
int sum;
Tree(int[] value) {
left = null;
right = null;
sum = 0;
this.value = value;
if (value != null) {
for (int i = 0; i < value.length; i++) sum += value[i];
}
}
Tree copy() {
return new Tree(this.value);
}
}
The time and space you need for building the tree here is absolutely nothing at all.
The reason is because, if you're given
A node of the tree
The depth of the node
The ordered array of input elements
you can simply compute its parent, left, and right children nodes using O(1) operations. And you have access to each of those things while you're traversing the tree, so you don't need anything else.
The problem is NP-complete.
If you really want to improve performance, then you have to forget about your tree implementation. You either have to just generate all the subsets and sum them up or to use dynamic programming.
The choice depends on the number of elements to sum and the sum you want to achieve. You know the sum it is 100,000,000, bruteforce exponential algorithm runs in O(2^n * n) time, so for number below 22 it makes sense.
In python you can achieve this with a simple:
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
You can significantly improve this complexity (sacrificing the memory) by using meet in the middle technique (read the wiki article). This will decrease it to O(2^(n/2)), which means that it will perform better than DP solution for n <~ 53
After thinking more about erip's comments, I realized he is correct - I shouldn't be using a tree to implement this algorithm.
Brute force usually is O(n*2^n) because there are n additions for 2^n subsets. Because I only do one addition per node, the solution I came up with is O(2^n) where n is the size of the given set. Also, this algorithm is only O(n) space complexity. Since the number of elements in the original set in my particular problem is small (around 25) O(2^n) complexity is not too much of a problem.
The dynamic solution to this problem is O(t*n) where t is the target sum and n is the number of elements. Because t is very large in my problem, the dynamic solution ends up with a very long runtime and a high memory usage.
This completes my particular solution in around 311 ms on my machine, which is a tremendous improvement over the dynamic programming solutions I have seen for this particular class of problem.
public class TailRecursiveSolver {
public static void main(String[] args) {
final long starttime = System.currentTimeMillis();
try {
step(new Subset(null, 0), 0);
}
catch (RuntimeException ex) {
System.out.println(ex.getMessage());
final long endtime = System.currentTimeMillis();
System.out.println(endtime - starttime);
}
}
static final int target = 100000000;
static final int[] set = new int[]{ . . . };
static void step(Subset current, int counter) {
if (current.sum == target) throw new RuntimeException(current.getText());
else if (counter == set.length) {}
else {
step(new Subset(add(current.subset, set[counter]), current.sum + set[counter]), counter + 1);
step(current, counter + 1);
}
}
static int[] add(int[] array, int digit) {
if (array == null) {
return new int[]{digit};
}
int[] newValue = new int[array.length + 1];
for (int i = 0; i < array.length; i++) {
newValue[i] = array[i];
}
newValue[array.length] = digit;
return newValue;
}
}
class Subset {
int[] subset;
int sum;
Subset(int[] subset, int sum) {
this.subset = subset;
this.sum = sum;
}
public String getText() {
String ret = "";
for (int i = 0; i < (subset == null ? 0 : subset.length); i++) {
ret += " + " + subset[i];
}
if (ret.startsWith(" ")) {
ret = ret.substring(3);
ret = ret + " = " + sum;
} else ret = "null";
return ret;
}
}
EDIT -
The above code still runs in O(n*2^n) time - since the add method runs in O(n) time. This following code will run in true O(2^n) time, and is MUCH more performant, completing in around 20 ms on my machine.
It is limited to sets less than 64 elements due to storing the current subset as the bits in a long.
public class SubsetSumSolver {
static boolean found = false;
static final int target = 100000000;
static final int[] set = new int[]{ . . . };
public static void main(String[] args) {
step(0,0,0);
}
static void step(long subset, int sum, int counter) {
if (sum == target) {
found = true;
System.out.println(getText(subset, sum));
}
else if (!found && counter != set.length) {
step(subset + (1 << counter), sum + set[counter], counter + 1);
step(subset, sum, counter + 1);
}
}
static String getText(long subset, int sum) {
String ret = "";
for (int i = 0; i < 64; i++) if((1 & (subset >> i)) == 1) ret += " + " + set[i];
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + sum;
else ret = "null";
return ret;
}
}
EDIT 2 -
Here is another version uses a meet in the middle attack, along with a little bit shifting in order to reduce the complexity from O(2^n) to O(2^(n/2)).
If you want to use this for sets with between 32 and 64 elements, you should change the int which represents the current subset in the step function to a long although performance will obviously drastically decrease as the set size increases. If you want to use this for a set with odd number of elements, you should add a 0 to the set to make it even numbered.
import java.util.ArrayList;
import java.util.List;
public class SubsetSumMiddleAttack {
static final int target = 100000000;
static final int[] set = new int[]{ ... };
static List<Subset> evens = new ArrayList<>();
static List<Subset> odds = new ArrayList<>();
static int[][] split(int[] superSet) {
int[][] ret = new int[2][superSet.length / 2];
for (int i = 0; i < superSet.length; i++) ret[i % 2][i / 2] = superSet[i];
return ret;
}
static void step(int[] superSet, List<Subset> accumulator, int subset, int sum, int counter) {
accumulator.add(new Subset(subset, sum));
if (counter != superSet.length) {
step(superSet, accumulator, subset + (1 << counter), sum + superSet[counter], counter + 1);
step(superSet, accumulator, subset, sum, counter + 1);
}
}
static void printSubset(Subset e, Subset o) {
String ret = "";
for (int i = 0; i < 32; i++) {
if (i % 2 == 0) {
if ((1 & (e.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
else {
if ((1 & (o.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
}
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + (e.sum + o.sum);
System.out.println(ret);
}
public static void main(String[] args) {
int[][] superSets = split(set);
step(superSets[0], evens, 0,0,0);
step(superSets[1], odds, 0,0,0);
for (Subset e : evens) {
for (Subset o : odds) {
if (e.sum + o.sum == target) printSubset(e, o);
}
}
}
}
class Subset {
int subset;
int sum;
Subset(int subset, int sum) {
this.subset = subset;
this.sum = sum;
}
}

Storing values of a Fibonacci sequence w/ recursion with minimal runtime

I know my code has a lot of issues right now, but I just want to get the ideas correct before trying anything. I need to have a method which accepts an integer n that returns the nth number in the Fibonacci sequence. While solving it normally with recursion, I have to minimize runtime so when it gets something like the 45th integer, it will still run fairly quickly. Also, I can't use class constants and globals.
The normal way w/ recursion.
public static int fibonacci(int n) {
if (n <= 2) { // to indicate the first two elems in the sequence
return 1;
} else { // goes back to very first integer to calculate (n-1) and (n+1) for (n)
return fibonacci(n-1) + fibonacci(n-2);
}
}
I believe the issue is that there is a lot of redundancy in this process. I figure that I can create a List to calculate up to nth elements so it only run through once before i return the nth element. However, I am having trouble seeing how to use recursion in that case though.
If I am understanding it correctly, the standard recursive method is slow because there are a lot of repeats:
fib(6) = fib(5) + fib(4)
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + 1
fib(3) = 1 + 1
Is this the correct way of approaching this? Is it needed to have some form of container to have a faster output while still being recursive? Should I use a helper method? I just recently got into recursive programming and I am having a hard time wrapping my head around this since I've been so used to iterative approaches. Thanks.
Here's my flawed and unfinished code:
public static int fasterFib(int n) {
ArrayList<Integer> results = new ArrayList<Integer>();
if (n <= 2) { // if
return 1;
} else if (results.size() <= n){ // If the list has fewer elems than
results.add(0, 1);
results.add(0, 1);
results.add(results.get(results.size() - 1 + results.get(results.size() - 2)));
return fasterFib(n); // not sure what to do with this yet
} else if (results.size() == n) { // base case if reached elems
return results.get(n);
}
return 0;
}
I think you want to use a Map<Integer, Integer> instead of a List. You should probably move that collection outside of your method (so it can cache the results) -
private static Map<Integer, Integer> results = new HashMap<>();
public static int fasterFib(int n) {
if (n == 0) {
return 0;
} else if (n <= 2) { // if
return 1;
}
if (results.get(n) != null) {
return results.get(n);
} else {
int v = fasterFib(n - 1) + fasterFib(n - 2);
results.put(n, v);
return v;
}
}
This optimization is called memoization, from the Wikipedia article -
In computing, memoization is an optimization technique used primarily to speed up computer programs by keeping the results of expensive function calls and returning the cached result when the same inputs occur again.
You can use Map::computeIfAbsent method (since 1.8) to re-use the already calculated numbers.
import java.util.HashMap;
import java.util.Map;
public class Fibonacci {
private final Map<Integer, Integer> cache = new HashMap<>();
public int fib(int n) {
if (n <= 2) {
return n;
} else {
return cache.computeIfAbsent(n, (key) -> fib(n - 1) + fib(n - 2));
}
}
}
The other way to do this is to use a helper method.
static private int fibonacci(int a, int b, int n) {
if(n == 0) return a;
else return fibonacci(b, a+b, n-1);
}
static public int fibonacci(int n) {
return fibonacci(0, 1, n);
}
How about a class and a private static HashMap?
import java.util.HashMap;
public class Fibonacci {
private static HashMap<Integer,Long> cache = new HashMap<Integer,Long>();
public Long get(Integer n) {
if ( n <= 2 ) {
return 1L;
} else if (cache.containsKey(n)) {
return cache.get(n);
} else {
Long result = get(n-1) + get(n-2);
cache.put(n, result);
System.err.println("Calculate once for " + n);
return result;
}
}
/**
* #param args
*/
public static void main(String[] args) {
Fibonacci f = new Fibonacci();
System.out.println(f.get(10));
System.out.println(f.get(15));
}
}
public class Fibonacci {
private Map<Integer, Integer> cache = new HashMap<>();
private void addToCache(int index, int value) {
cache.put(index, value);
}
private int getFromCache(int index) {
return cache.computeIfAbsent(index, this::fibonacci);
}
public int fibonacci(int i) {
if (i == 1)
addToCache(i, 0);
else if (i == 2)
addToCache(i, 1);
else
addToCache(i, getFromCache(i - 1) + getFromCache(i - 2));
return getFromCache(i);
}
}
You can use memoization (store the values you already have in an array, if the value at a given index of this array is not a specific value you have given to ignore --> return that).
Code:
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int n = Integer.parseInt(s.nextLine());
int[] memo = new int[n+1];
for (int i = 0; i < n+1 ; i++) {
memo[i] = -1;
}
System.out.println(fib(n,memo));
}
static int fib(int n, int[] memo){
if (n<=1){
return n;
}
if(memo[n] != -1){
return memo[n];
}
memo[n] = fib(n-1,memo) + fib(n-2,memo);
return memo[n];
}
Explaination:
memo :
-> int array (all values -1)
-> length (n+1) // easier for working on index
You assign a value to a given index of memo ex: memo[2]
memo will look like [-1,-1, 1, ..... ]
Every time you need to know the fib of 2 it will return memo[2] -> 1
Which saves a lot of computing time on bigger numbers.
private static Map<Integer, Integer> cache = new HashMap<Integer, Integer(){
{
put(0, 1);
put(1, 1);
}
};
/**
* Smallest fibonacci sequence program using dynamic programming.
* #param n
* #return
*/
public static int fibonacci(int n){
return n < 2 ? n : cache.computeIfAbsent(n, (key) -> fibonacci( n - 1) + fibonacci(n - 2));
}
public static long Fib(int n, Dictionary<int, long> dict)
{
if (n <= 1)
return n;
if (dict.ContainsKey(n))
return dict[n];
var value = Fib(n - 1,dict) + Fib(n - 2,dict);
dict[n] = value;
return value;
}

Hash by Chaining VS Double Probing

I'm trying to compare between Chaining and Double probing.
I need to insert 40 integers to table size 100,
when I measure the time with nanotime (in java)
I get that the Double is faster.
thats because in the Insert methood of Chaining, I create every time LinkedListEntry,
and it's add time.
how can it be that Chaining is more faster than Double probing ? (that's what i read in wikipedia)
Thanks!!
this is the code of chaining:
public class LastChain
{
int tableSize;
Node[] st;
LastChain(int size) {
tableSize = size;
st = new Node[tableSize];
for (int i = 0; i < tableSize; i++)
st[i] = null;
}
private class Node
{
int key;
Node next;
Node(int key, Node next)
{
this.key = key;
this.next = next;
}
}
public void put(Integer key)
{
int i = hash(key);
Node first=st[i];
for (Node x = st[i]; x != null; x = x.next)
if (key.equals(x.key))
{
return;
}
st[i] = new Node(key, first);
}
private int hash(int key)
{ return key%tableSize;
}
}
}
and this is the relevant code from double probing:
public class HashDouble1 {
private Integer[] hashArray;
private int arraySize;
private Integer bufItem; // for deleted items
HashDouble1(int size) {
arraySize = size;
hashArray = new Integer[arraySize];
bufItem = new Integer(-1);
}
public int hashFunc1(int key) {
return key % arraySize;
}
public int hashFunc2(int key) {
return 7 - key % 7;
}
public void insert(Integer key) {
int hashVal = hashFunc1(key); // hash the key
int stepSize = hashFunc2(key); // get step size
// until empty cell or -1
while (hashArray[hashVal] != null && hashArray[hashVal] != -1) {
hashVal += stepSize; // add the step
hashVal %= arraySize; // for wraparound
}
hashArray[hashVal] = key; // insert item
}
}
in this way the insert in Double is more faster than Chaining.
how can i fix it?
Chaining works best with high load factors. Trying using 90 strings (not a well places selection of integers) in a table of 100.
Also chaining is much easier to implement removal/delete for.
Note: In HashMap, an Entry object is created whether it is chained or not, not there is no saving there.
Java has the special "feature" Objects take up a lot of memory.
Thus, for large datasets (where this will have any relevance) double probing will be good.
But as a very first thing, please change your Integer[] into int[] -> the memory usage will be one fourth or so and the performance will jump nicely.
But always with performance questions: measure, measure, measure, as your case will always be special.

Find closest value in an ordered list

I am wondering how you would write a simple java method finding the closest Integer to a given value in a sorted Integer list.
Here is my first attempt:
public class Closest {
private static List<Integer> integers = new ArrayList<Integer>();
static {
for (int i = 0; i <= 10; i++) {
integers.add(Integer.valueOf(i * 10));
}
}
public static void main(String[] args) {
Integer closest = null;
Integer arg = Integer.valueOf(args[0]);
int index = Collections.binarySearch(
integers, arg);
if (index < 0) /*arg doesn't exist in integers*/ {
index = -index - 1;
if (index == integers.size()) {
closest = integers.get(index - 1);
} else if (index == 0) {
closest = integers.get(0);
} else {
int previousDate = integers.get(index - 1);
int nextDate = integers.get(index);
if (arg - previousDate < nextDate - arg) {
closest = previousDate;
} else {
closest = nextDate;
}
}
} else /*arg exists in integers*/ {
closest = integers.get(index);
}
System.out.println("The closest Integer to " + arg + " in " + integers
+ " is " + closest);
}
}
What do you think about this solution ? I am sure there is a cleaner way to do this job.
Maybe such method exists somewhere in the Java libraries and I missed it ?
try this little method:
public int closest(int of, List<Integer> in) {
int min = Integer.MAX_VALUE;
int closest = of;
for (int v : in) {
final int diff = Math.abs(v - of);
if (diff < min) {
min = diff;
closest = v;
}
}
return closest;
}
some testcases:
private final static List<Integer> list = Arrays.asList(10, 20, 30, 40, 50);
#Test
public void closestOf21() {
assertThat(closest(21, list), is(20));
}
#Test
public void closestOf19() {
assertThat(closest(19, list), is(20));
}
#Test
public void closestOf20() {
assertThat(closest(20, list), is(20));
}
Kotlin is so helpful
fun List<Int>.closestValue(value: Int) = minBy { abs(value - it) }
val values = listOf(1, 8, 4, -6)
println(values.closestValue(-7)) // -6
println(values.closestValue(2)) // 1
println(values.closestValue(7)) // 8
List doesn't need to be sorted BTW
Edit: since kotlin 1.4, minBy is deprecated. Prefer minByOrNull
#Deprecated("Use minByOrNull instead.", ReplaceWith("this.minByOrNull(selector)"))
#DeprecatedSinceKotlin(warningSince = "1.4")
A solution without binary search (takes advantage of list being sorted):
public int closest(int value, int[] sorted) {
if(value < sorted[0])
return sorted[0];
int i = 1;
for( ; i < sorted.length && value > sorted[i] ; i++);
if(i >= sorted.length)
return sorted[sorted.length - 1];
return Math.abs(value - sorted[i]) < Math.abs(value - sorted[i-1]) ?
sorted[i] : sorted[i-1];
}
To solve the problem, I'd extend the Comparable Interface by a distanceTo method. The implementation of distanceTo returns a double value that represents the intended distance and which is compatible with the result of the compareTo implementation.
The following example illustrates the idea with just apples. You can exchange diameter by weight, volume or sweetness. The bag will always return the 'closest' apple (most similiar in size, wight or taste)
public interface ExtComparable<T> extends Comparable<T> {
public double distanceTo(T other);
}
public class Apple implements Comparable<Apple> {
private Double diameter;
public Apple(double diameter) {
this.diameter = diameter;
}
public double distanceTo(Apple o) {
return diameter - o.diameter;
}
public int compareTo(Apple o) {
return (int) Math.signum(distanceTo(o));
}
}
public class AppleBag {
private List<Apple> bag = new ArrayList<Apple>();
public addApples(Apple...apples){
bag.addAll(Arrays.asList(apples));
Collections.sort(bag);
}
public removeApples(Apple...apples){
bag.removeAll(Arrays.asList(apples));
}
public Apple getClosest(Apple apple) {
Apple closest = null;
boolean appleIsInBag = bag.contains(apple);
if (!appleIsInBag) {
bag.addApples(apple);
}
int appleIndex = bag.indexOf(apple);
if (appleIndex = 0) {
closest = bag.get(1);
} else if(appleIndex = bag.size()-1) {
closest = bag.get(bag.size()-2);
} else {
double absDistToPrev = Math.abs(apple.distanceTo(bag.get(appleIndex-1));
double absDistToNext = Math.abs(apple.distanceTo(bag.get(appleIndex+1));
closest = bag.get(absDistToNext < absDistToPrev ? next : previous);
}
if (!appleIsInBag) {
bag.removeApples(apple);
}
return closest;
}
}
Certainly you can simply use a for loop to go through the and keep track of the difference between the value you are on and the value. It would look cleaner, but be much slower.
See: Finding closest match in collection of numbers
I think what you have is about the simplest and most efficient way to do it. Finding the "closest" item in a sorted list isn't something that is commonly encountered in programming (you typically look for the one that is bigger, or the one that is smaller). The problem only makes sense for numeric types, so is not very generalizable, and thus it would be unusual to have a library function for it.
Not tested
int[] randomArray; // your array you want to find the closest
int theValue; // value the closest should be near to
for (int i = 0; i < randomArray.length; i++) {
int compareValue = randomArray[i];
randomArray[i] -= theValue;
}
int indexOfClosest = 0;
for (int i = 1; i < randomArray.length; i++) {
int compareValue = randomArray[i];
if(Math.abs(randomArray[indexOfClosest] > Math.abs(randomArray[i]){
indexOfClosest = i;
}
}
I think your answer is probably the most efficient way to return a single result.
However, the problem with your approach is that there are 0 (if there is no list), 1, or 2 possible solutions. It's when you have two possible solutions to a function that your problems really start: What if this is not the final answer, but only the first in a series of steps to determine an optimal course of action, and the answer that you didn't return would have provided a better solution? The only correct thing to do would be to consider both answers and compare the results of further processing only at the end.
Think of the square root function as a somewhat analogous problem to this.
If you're not massively concerned on performance (given that the set is searched twice), I think using a Navigable set leads to clearer code:
public class Closest
{
private static NavigableSet<Integer> integers = new TreeSet<Integer>();
static
{
for (int i = 0; i <= 10; i++)
{
integers.add(Integer.valueOf(i * 10));
}
}
public static void main(String[] args)
{
final Integer arg = Integer.valueOf(args[0]);
final Integer lower = integers.lower(arg);
final Integer higher = integers.higher(arg);
final Integer closest;
if (lower != null)
{
if (higher != null)
closest = (higher - arg > arg - lower) ? lower : higher;
else
closest = lower;
}
else
closest = higher;
System.out.println("The closest Integer to " + arg + " in " + integers + " is " + closest);
}
}
Your solution appears to be asymptotically optimal. It might be slightly faster (though probably less maintainable) if it used Math.min/max. A good JIT likely has intrinsics that make these fast.
int index = Collections.binarySearch(integers, arg);
if (index < 0) {
int previousDate = integers.get(Math.max(0, -index - 2));
int nextDate = integers.get(Math.min(integers.size() - 1, -index - 1));
closest = arg - previousDate < nextDate - arg ? previousDate : nextDate;
} else {
closest = integers.get(index);
}
Probably a bit late, but this WILL work, this is a data structure binary search:
Kotlin:
fun binarySearch(list: List<Int>, valueToCompare: Int): Int {
var central: Int
var initialPosition = 0
var lastPosition: Int
var centralValue: Int
lastPosition = list.size - 1
while (initialPosition <= lastPosition) {
central = (initialPosition + lastPosition) / 2 //Central index
centralValue = list[central] //Central index value
when {
valueToCompare == centralValue -> {
return centralValue //found; returns position
}
valueToCompare < centralValue -> {
lastPosition = central - 1 //position changes to the previous index
}
else -> {
initialPosition = central + 1 //position changes to next index
}
}
}
return -1 //element not found
}
Java:
public int binarySearch(int list[], int valueToCompare) {
int central;
int centralValue;
int initialPosition = 0;
int lastPosition = list . length -1;
while (initialPosition <= lastPosition) {
central = (initialPosition + lastPosition) / 2; //central index
centralValue = list[central]; //central index value
if (valueToCompare == centralValue) {
return centralValue; //element found; returns position
} else if (valueToCompare < centralValue) {
lastPosition = central - 1; //Position changes to the previous index
} else {
initialPosition = central + 1; //Position changes to the next index
}
return -1; //element not found
}
}
I hope this helps, happy coding.

Categories