Added BinarySearch function. Fixed up license info
This commit is contained in:
parent
744de6eed8
commit
96e9e022d9
54
Dorkbox-Util/src/dorkbox/util/collections/Bias.java
Normal file
54
Dorkbox-Util/src/dorkbox/util/collections/Bias.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2013 Tim Boudreau.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package dorkbox.util.collections;
|
||||
|
||||
/**
|
||||
* Bias used to decide how to resolve ambiguity, for example in a binary search
|
||||
* with a list of timestamps - if the requested timestamp lies between two
|
||||
* actual data snapshots, should we return the next, previous, nearest one, or
|
||||
* none unless there is an exact match.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public enum Bias {
|
||||
|
||||
/**
|
||||
* If a search result falls between two elements, prefer the next element
|
||||
*/
|
||||
FORWARD,
|
||||
/**
|
||||
* If a search result falls between two elements, prefer the previous element
|
||||
*/
|
||||
BACKWARD,
|
||||
/**
|
||||
* If a search result falls between two elements, prefer the element with
|
||||
* the minimum distance
|
||||
*/
|
||||
NEAREST,
|
||||
/**
|
||||
* If a search result falls between two elements, return no element unless
|
||||
* there is an exact match
|
||||
*/
|
||||
NONE;
|
||||
}
|
190
Dorkbox-Util/src/dorkbox/util/collections/BinarySearch.java
Normal file
190
Dorkbox-Util/src/dorkbox/util/collections/BinarySearch.java
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2013 Tim Boudreau.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package dorkbox.util.collections;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* General-purpose binary search algorithm; you pass in an array or list
|
||||
* wrapped in an instance of <code>Indexed</code>, and an
|
||||
* <code>Evaluator</code> which converts the contents of the list into
|
||||
* numbers used by the binary search algorithm. Note that the data
|
||||
* (as returned by the Indexed array/list) <b><i>must be in order from
|
||||
* low to high</i></b>. The indices need not be contiguous (presumably
|
||||
* they are not or you wouldn't be using this class), but they must be
|
||||
* sorted. If assertions are enabled, this is enforced; if not, very
|
||||
* bad things (endless loops, etc.) can happen as a consequence of passing
|
||||
* unsorted data in.
|
||||
* <p/>
|
||||
* This class is not thread-safe and the size and contents of the <code>Indexed</code>
|
||||
* should not change while a search is being performed.
|
||||
*
|
||||
* @author Tim Boudreau
|
||||
*/
|
||||
public class BinarySearch<T> {
|
||||
|
||||
private final Evaluator<T> eval;
|
||||
private final Indexed<T> indexed;
|
||||
|
||||
/**
|
||||
* Create a new binary search.
|
||||
*
|
||||
* @param eval The thing which converts elements into numbers
|
||||
* @param indexed A collection, list or array
|
||||
*/
|
||||
public BinarySearch(Evaluator<T> eval, Indexed<T> indexed) {
|
||||
this.eval = eval;
|
||||
this.indexed = indexed;
|
||||
assert checkSorted();
|
||||
}
|
||||
|
||||
public BinarySearch(Evaluator<T> eval, List<T> l) {
|
||||
this (eval, new ListWrap<T>(l));
|
||||
}
|
||||
|
||||
private boolean checkSorted() {
|
||||
long val = Long.MIN_VALUE;
|
||||
long sz = this.indexed.size();
|
||||
for (long i=0; i < sz; i++) {
|
||||
T t = this.indexed.get(i);
|
||||
long nue = this.eval.getValue(t);
|
||||
if (val != Long.MIN_VALUE) {
|
||||
if (nue < val) {
|
||||
throw new IllegalArgumentException("Collection is not sorted at " + i + " - " + this.indexed);
|
||||
}
|
||||
}
|
||||
val = nue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public long search(long value, Bias bias) {
|
||||
return search(0, this.indexed.size()-1, value, bias);
|
||||
}
|
||||
|
||||
public T match(T prototype, Bias bias) {
|
||||
long value = this.eval.getValue(prototype);
|
||||
long index = search(value, bias);
|
||||
return index == -1 ? null : this.indexed.get(index);
|
||||
}
|
||||
|
||||
public T searchFor(long value, Bias bias) {
|
||||
long index = search(value, bias);
|
||||
return index == -1 ? null : this.indexed.get(index);
|
||||
}
|
||||
|
||||
private long search(long start, long end, long value, Bias bias) {
|
||||
long range = end - start;
|
||||
if (range == 0) {
|
||||
return start;
|
||||
}
|
||||
if (range == 1) {
|
||||
T ahead = this.indexed.get(end);
|
||||
T behind = this.indexed.get(start);
|
||||
long v1 = this.eval.getValue(behind);
|
||||
long v2 = this.eval.getValue(ahead);
|
||||
switch (bias) {
|
||||
case BACKWARD:
|
||||
return start;
|
||||
case FORWARD:
|
||||
return end;
|
||||
case NEAREST:
|
||||
if (v1 == value) {
|
||||
return start;
|
||||
} else if (v2 == value) {
|
||||
return end;
|
||||
} else {
|
||||
if (Math.abs(v1 - value) < Math.abs(v2 - value)) {
|
||||
return start;
|
||||
} else {
|
||||
return end;
|
||||
}
|
||||
}
|
||||
case NONE:
|
||||
if (v1 == value) {
|
||||
return start;
|
||||
} else if (v2 == value) {
|
||||
return end;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
throw new AssertionError(bias);
|
||||
|
||||
}
|
||||
}
|
||||
long mid = start + range / 2;
|
||||
long vm = this.eval.getValue(this.indexed.get(mid));
|
||||
if (value >= vm) {
|
||||
return search(mid, end, value, bias);
|
||||
} else {
|
||||
return search(start, mid, value, bias);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object into a numeric value that is used to
|
||||
* perform binary search
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Evaluator<T> {
|
||||
|
||||
public long getValue(T obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction for list-like things which have a length and indices
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Indexed<T> {
|
||||
|
||||
public T get(long index);
|
||||
|
||||
public long size();
|
||||
}
|
||||
|
||||
private static final class ListWrap<T> implements Indexed<T> {
|
||||
|
||||
private final List<T> l;
|
||||
|
||||
ListWrap(List<T> l) {
|
||||
this.l = l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long index) {
|
||||
return this.l.get((int) index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
return this.l.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + '{' + this.l + '}';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
package dorkbox.util;
|
||||
package dorkbox.util.collections;
|
||||
|
||||
import java.io.ObjectStreamField;
|
||||
import java.io.Serializable;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
// from libGDX
|
||||
|
||||
package dorkbox.util.primativeCollections;
|
||||
package dorkbox.util.collections;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
// slightly tweaked from libGDX
|
||||
|
||||
package dorkbox.util.primativeCollections;
|
||||
package dorkbox.util.collections;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
package dorkbox.util.primativeCollections;
|
||||
package dorkbox.util.collections;
|
||||
|
||||
import com.esotericsoftware.kryo.util.ObjectMap;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user