Archived
0

Improved segment search

This commit is contained in:
iMoHax
2014-09-19 14:50:38 +04:00
parent ece5389f6f
commit d2196c6be1
11 changed files with 460 additions and 174 deletions

View File

@@ -12,8 +12,8 @@ public class MarketAnalyzer {
private Market market;
private double tank;
private double maxDistance;
private double segment = 50;
private int count = 100;
private int segmentSize;
private int limit;
private int jumps;
private int cargo;
@@ -21,16 +21,18 @@ public class MarketAnalyzer {
public MarketAnalyzer(Market market) {
this.market = market;
this.limit = 100;
this.segmentSize = 0;
}
public Collection<Order> getTop(double balance){
LOG.debug("Get top {}", count);
LOG.debug("Get top {}", limit);
Collection<Vendor> vendors = market.get();
List<Order> top = new ArrayList<>(count);
List<Order> top = new ArrayList<>(limit);
for (Vendor vendor : vendors) {
LOG.trace("Check vendor {}", vendor);
Collection<Order> orders = getOrders(vendor, balance, top.isEmpty() ? 0 : top.get(top.size()-1).getProfit());
RouteGraph.addAllToTop(top, orders, count, orderComparator);
TopList.addAllToTop(top, orders, limit, orderComparator);
}
return top;
}
@@ -101,22 +103,22 @@ public class MarketAnalyzer {
}
public Collection<PathRoute> getPaths(Vendor from, double balance){
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segment);
return searcher.getPaths(from, market.get(), jumps, balance, cargo, count);
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize);
return searcher.getPaths(from, market.get(), jumps, balance, cargo, limit);
}
public Collection<PathRoute> getPaths(Vendor from, Vendor to, double balance){
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segment);
return searcher.getPaths(from, to, market.get(), jumps, balance, cargo, count);
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize);
return searcher.getPaths(from, to, market.get(), jumps, balance, cargo, limit);
}
public Collection<PathRoute> getTopPaths(double balance){
List<PathRoute> top = new ArrayList<>(count);
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segment);
List<PathRoute> top = new ArrayList<>(limit);
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize);
Collection<Vendor> vendors = market.get();
for (Vendor vendor : vendors) {
Collection<PathRoute> paths = searcher.getPaths(vendor, vendor, vendors, jumps, balance, cargo, 3);
RouteGraph.addAllToTop(top, paths, count, RouteGraph.comparator);
TopList.addAllToTop(top, paths, limit, RouteGraph.byProfitComparator);
}
return top;
}
@@ -138,5 +140,11 @@ public class MarketAnalyzer {
this.cargo = cargo;
}
public void setSegmentSize(int segmentSize) {
this.segmentSize = segmentSize;
}
public void setPathsCount(int count) {
this.limit = count;
}
}

View File

@@ -12,6 +12,7 @@ import java.util.function.Predicate;
public class Graph<T extends Connectable<T>> {
private final static ForkJoinPool POOL = new ForkJoinPool();
private final static int THRESHOLD = 4;
private final static int DEFAULT_COUNT = 200;
@FunctionalInterface
public interface PathConstructor<E extends Connectable<E>> {
@@ -20,14 +21,14 @@ public class Graph<T extends Connectable<T>> {
private final static Logger LOG = LoggerFactory.getLogger(Graph.class);
private final Vertex<T> root;
private final Map<T,Vertex<T>> vertexes;
protected final Vertex<T> root;
protected final Map<T,Vertex<T>> vertexes;
private final double stock;
private final double maxDistance;
private final boolean withRefill;
protected final double stock;
protected final double maxDistance;
protected final boolean withRefill;
private final PathConstructor<T> pathFabric;
private int minJumps;
protected int minJumps;
public Graph(T start, Collection<T> set, double stock, int maxDeep) {
@@ -79,42 +80,45 @@ public class Graph<T extends Connectable<T>> {
return vertexes.get(entry);
}
private void findPathsTo(Vertex<T> target, int max, List<Path<T>> res){
POOL.invoke(new PathFinder(res, max, pathFabric.build(root), target, root.getLevel() - 1, stock));
private void findPathsTo(Vertex<T> target, TopList<Path<T>> res, int deep){
POOL.invoke(new PathFinder(res, pathFabric.build(root), target, deep-1, stock));
}
public List<Path<T>> getPathsTo(T entry){
return getPathsTo(entry, 200);
return getPathsTo(entry, DEFAULT_COUNT);
}
public List<Path<T>> getPathsTo(T entry, int max){
return getPathsTo(entry, max, root.getLevel()).getList();
}
public TopList<Path<T>> getPathsTo(T entry, int max, int deep){
Vertex<T> target = getVertex(entry);
ArrayList<Path<T>> paths = new ArrayList<>(max);
findPathsTo(target, max, paths);
TopList<Path<T>> paths = newTopList(max);
findPathsTo(target, paths, deep);
return paths;
}
public List<Path<T>> getPaths(int count){
ArrayList<Path<T>> paths = new ArrayList<>(vertexes.size()*count);
return getPaths(count, root.getLevel()).getList();
}
public TopList<Path<T>> getPaths(int count, int deep){
TopList<Path<T>> paths = newTopList(count);
for (Vertex<T> target : vertexes.values()) {
ArrayList<Path<T>> p = new ArrayList<>(count);
findPathsTo(target, count, p);
for (Path<T> path : p) {
TopList<Path<T>> p = newTopList(minJumps);
findPathsTo(target, p, deep);
for (Path<T> path : p.getList()) {
paths.add(path);
}
}
return paths;
}
// if is true, then break search
protected boolean onFindPath(List<Path<T>> paths, int max, Path<T> path){
if (paths.size() >= max) return true;
paths.add(path);
return paths.size() >= max;
protected TopList<Path<T>> newTopList(int count){
return new TopList<>(count);
}
public Path<T> getFastPathTo(T entry){
Vertex<T> target = getVertex(entry);
if (target == null) return null;
@@ -246,17 +250,15 @@ public class Graph<T extends Connectable<T>> {
}
private class PathFinder extends RecursiveAction {
private final List<Path<T>> paths;
private final int max;
private final TopList<Path<T>> paths;
private final Path<T> head;
private final Vertex<T> target;
private final int deep;
private final double limit;
private final DistanceFilter distanceFilter;
private PathFinder(List<Path<T>> paths, int max, Path<T> head, Vertex<T> target, int deep, double limit) {
private PathFinder(TopList<Path<T>> paths, Path<T> head, Vertex<T> target, int deep, double limit) {
this.paths = paths;
this.max = max;
this.head = head;
this.target = target;
this.deep = deep;
@@ -276,7 +278,7 @@ public class Graph<T extends Connectable<T>> {
path.finish();
LOG.trace("Last edge find, add path {}", path);
synchronized (paths){
if (onFindPath(paths, max, path)) complete(null);
if (!paths.add(path)) complete(null);
}
}
}
@@ -296,7 +298,7 @@ public class Graph<T extends Connectable<T>> {
// refill
if (nextLimit < 0) nextLimit = stock - next.getLength();
//Recursive search
PathFinder task = new PathFinder(paths, max, path, target, deep - 1, nextLimit);
PathFinder task = new PathFinder(paths, path, target, deep - 1, nextLimit);
task.fork();
subTasks.add(task);
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){

View File

@@ -106,5 +106,9 @@ public class Path<T extends Connectable<T>> {
return head;
}
public int getLength(){
return isRoot() ? 0 : 1 + getPrevious().getLength();
}
}

View File

@@ -12,19 +12,33 @@ public class PathRoute extends Path<Vendor> {
private final static Logger LOG = LoggerFactory.getLogger(PathRoute.class);
private final ArrayList<Order> orders = new ArrayList<>();
private final boolean byAvg;
private double profit = 0;
private double balance = 0;
private double distance = 0;
private int landsCount = 0;
private PathRoute tail;
public final static Order TRANSIT = null;
public PathRoute(Vertex<Vendor> source) {
super(source);
this(source, false);
}
public static PathRoute buildAvg(Vertex<Vendor> source){
return new PathRoute(source, true);
}
private PathRoute(Vertex<Vendor> source, boolean byAvg) {
super(source);
this.byAvg = byAvg;
}
private PathRoute(PathRoute head, Vertex<Vendor> vertex, boolean refill) {
super(head, vertex, refill);
assert head.tail == null;
head.tail = this;
byAvg = head.byAvg;
//transit
orders.add(TRANSIT);
}
@@ -90,6 +104,22 @@ public class PathRoute extends Path<Vendor> {
fillOrders();
getPrevious().finish();
}
updateDistance();
}
private void update(){
PathRoute p = this;
p.updateBalance();
while (p.hasNext()){
p = p.getNext();
p.updateBalance();
}
while (p != this){
p.updateProfit();
p.updateLandsCount();
p = p.getPrevious();
}
getRoot().updateDistance();
}
private void fillOrders(){
@@ -128,38 +158,26 @@ public class PathRoute extends Path<Vendor> {
return tail != null;
}
private void update(){
PathRoute p = this;
p.updateBalance();
while (p.hasNext()){
p = p.getNext();
p.updateBalance();
}
while (p != this){
p.updateProfit();
p = p.getPrevious();
}
}
public void sort(double balance, long limit){
public void sort(double balance, long cargo){
// start on root only
if (isRoot()){
this.balance = balance;
if (hasNext())
getNext().forwardSort(limit);
if (hasNext()){
getNext().forwardSort(cargo);
}
} else {
getPrevious().sort(balance, limit);
getPrevious().sort(balance, cargo);
}
}
private void forwardSort(long limit){
private void forwardSort(long cargo){
updateBalance();
boolean needSort = false;
for (Order order : orders) {
if (order == TRANSIT) continue;
if (order.getCount() < limit){
if (order.getCount() < cargo){
needSort = true;
order.setMax(balance, limit);
order.setMax(balance, cargo);
}
}
if (needSort){
@@ -168,20 +186,22 @@ public class PathRoute extends Path<Vendor> {
LOG.trace("New order of orders {}", orders);
}
if (hasNext()){
getNext().forwardSort(limit);
getNext().forwardSort(cargo);
} else {
LOG.trace("Start back sort");
Order best = orders.get(0);
profit = best == TRANSIT ? 0 : best.getProfit();
LOG.trace("Max profit from {} = {}",getPrevious().get(), profit);
LOG.trace("Max profit from {} = {}", getPrevious().get(), profit);
updateLandsCount();
getPrevious().backwardSort();
}
}
private void backwardSort(){
orders.sort(this::compareOrders);
orders.sort(byAvg ? this::compareByAvgProfit : this::compareOrders);
LOG.trace("New order of orders {}", orders);
updateProfit();
updateLandsCount();
if (!isRoot())
getPrevious().backwardSort();
}
@@ -222,6 +242,10 @@ public class PathRoute extends Path<Vendor> {
return profit;
}
public double getAvgProfit(){
return isRoot()? profit/landsCount : getPrevious().getAvgProfit();
}
public double getBalance() {
return balance;
}
@@ -249,6 +273,17 @@ public class PathRoute extends Path<Vendor> {
return Double.compare(profit2, profit1);
}
private int compareByAvgProfit(Order o1, Order o2){
if (o1 != TRANSIT && o2 != TRANSIT){
if (!hasNext() || o1.isBuyer(o2.getBuyer()))
return o2.compareTo(o1);
}
double profit1 = getProfit(o1)/computeLandsCount(o1);
double profit2 = getProfit(o2)/computeLandsCount(o2);
return Double.compare(profit2, profit1);
}
@Override
public PathRoute getRoot() {
return (PathRoute) super.getRoot();
@@ -263,19 +298,67 @@ public class PathRoute extends Path<Vendor> {
return orders.get(0);
}
public double getDistance(){
private double computeDistance(){
if (isRoot()){
double res = 0;
PathRoute p = this;
while (p.hasNext()){
p = p.getNext();
res += p.getDistance();
res += p.computeDistance();
}
return res;
}
else return getPrevious().get().getDistance(get());
}
private void updateDistance(){
this.distance = computeDistance();
}
public double getDistance(){
return distance;
}
private int computeLandsCount(Order order){
int res = 0;
PathRoute p = isRoot()? getNext() : this;
while (p.hasNext()){
p = p.getNext();
// lands for sell
if (order != null && p.isPathFrom(order.getBuyer())){
LOG.trace("{} is lands for sell by order {}", p, order);
return res + p.getLandsCount() + 1;
} else {
if (order == null){
order = p.getBest();
if (order != null){
LOG.trace("{} is lands for buy by order {}", p, order);
return res + p.getLandsCount() + 1;
}
} else {
if (p.isRefill()){
LOG.trace("{} is lands for refill", p);
res++;
}
}
}
}
LOG.trace("{} is end, landing", p);
res++;
return res;
}
private void updateLandsCount(){
Order best = isRoot() ? getNext().getBest() : getBest();
landsCount = computeLandsCount(best);
LOG.trace("Lands count from {} = {}", isRoot() ? get() : getPrevious().get(), landsCount);
}
public int getLandsCount() {
return landsCount;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -303,38 +386,6 @@ public class PathRoute extends Path<Vendor> {
orders.set(0, order);
}
public int getLandsCount(){
int res = 0;
PathRoute p = this.isRoot() ? getNext() : this;
Order o = p.getBest();
while (p.hasNext()){
p = p.getNext();
// lands for sell
if (o != null && p.isPathFrom(o.getBuyer())){
LOG.trace("{} is lands for sell by order {}", p, o);
o = p.getBest();
res++;
} else {
if (o == null){
o = p.getBest();
if (o!= null){
LOG.trace("{} is lands for buy by order {}", p, o);
res++;
}
} else {
if (p.isRefill()){
LOG.trace("{} is lands for refill", p);
res++;
}
}
}
}
LOG.trace("{} is end, landing", p);
res++;
return res;
}
public PathRoute dropTo(Vendor vendor){
PathRoute p = getCopy(true).getEnd();
while (!p.isRoot() && !p.get().equals(vendor)){

View File

@@ -7,12 +7,13 @@ import java.util.*;
public class RouteGraph extends Graph<Vendor> {
private double balance;
private int limit;
private int cargo;
private boolean groupRes;
public static Comparator<PathRoute> comparator = (p1, p2) -> {
public static Comparator<PathRoute> byProfitComparator = (p1, p2) -> {
PathRoute r1 = p1.getRoot();
PathRoute r2 = p2.getRoot();
int cmp = Double.compare(r2.getProfit()/r2.getLandsCount(), r1.getProfit()/r1.getLandsCount());
int cmp = Double.compare(r2.getAvgProfit(), r1.getAvgProfit());
if (cmp != 0 ) return cmp;
cmp = Double.compare(r1.getDistance(), r2.getDistance());
if (cmp != 0) return cmp;
@@ -21,61 +22,65 @@ public class RouteGraph extends Graph<Vendor> {
return cmp;
};
public static Comparator<PathRoute> groupByLengthComparator = (p1, p2) -> {
int cmp = Integer.compare(p1.getLength(), p2.getLength());
if (cmp != 0 ) return cmp;
return byProfitComparator.compare(p1, p2);
};
public RouteGraph(Vendor start, Collection<Vendor> set, double stock, double maxDistance, boolean withRefill, int maxDeep) {
super(start, set, stock, maxDistance, withRefill, maxDeep, PathRoute::new);
this(start, set, stock, maxDistance, withRefill, maxDeep, false);
}
public RouteGraph(Vendor start, Collection<Vendor> set, double stock, double maxDistance, boolean withRefill, int maxDeep, boolean groupRes) {
super(start, set, stock, maxDistance, withRefill, maxDeep, groupRes ? PathRoute::buildAvg : PathRoute::new);
if (groupRes){
this.groupRes = maxDeep > minJumps;
}
}
public void setBalance(double balance) {
this.balance = balance;
}
public void setLimit(int limit) {
this.limit = limit;
public void setCargo(int cargo) {
this.cargo = cargo;
}
@Override
protected boolean onFindPath(List<Path<Vendor>> paths, int max, Path<Vendor> path) {
PathRoute route = (PathRoute) path;
route.sort(balance, limit);
addToTop(paths, route, max, (r1, r2) -> comparator.compare((PathRoute)r1, (PathRoute)r2));
return false;
}
public static <T> void addToTop(List<T> list, T entry, int limit, Comparator<T> comparator){
if (list.size() == limit){
int index = Collections.binarySearch(list, entry, comparator);
if (index < 0) index = -1 - index;
if (index == limit) return;
list.add(index, entry);
list.remove(limit);
} else {
if (list.size() < limit-1){
list.add(entry);
} else {
list.add(entry);
list.sort(comparator);
}
protected TopList<Path<Vendor>> newTopList(int count) {
int groupSize = 0;
if (groupRes && getMinJumps() > 1){
groupSize = Math.floorDiv(count, root.getLevel());
}
return new TopRoutes(count, groupSize);
}
public static <T> void addAllToTop(List<T> list, Collection<T> sortEntries, int limit, Comparator<T> comparator){
for (T entry : sortEntries) {
if (list.size() == limit){
int index = Collections.binarySearch(list, entry, comparator);
if (index < 0) index = -1 - index;
if (index == limit) return;
list.add(index, entry);
list.remove(limit);
private class TopRoutes extends TopList<Path<Vendor>> {
private final int groupSize;
public TopRoutes(int limit, int groupSize) {
super(limit, (p1, p2) -> groupSize > 0 ? groupByLengthComparator.compare((PathRoute)p1, (PathRoute)p2) : RouteGraph.byProfitComparator.compare((PathRoute)p1, (PathRoute)p2));
this.groupSize = groupSize;
}
@Override
public boolean add(Path<Vendor> entry) {
if (comparator != null){
((PathRoute)entry).sort(balance, cargo);
}
if (groupSize>0){
addToGroupTop(list, entry, limit, comparator, (e) -> e.getLength()-1, groupSize);
} else {
if (list.size() < limit-1){
list.add(entry);
if (comparator != null){
addToTop(list, entry, limit, comparator);
} else {
if (list.size() >= limit) return false;
list.add(entry);
list.sort(comparator);
if (list.size() >= limit) return false;
}
}
return true;
}
}

View File

@@ -16,40 +16,35 @@ public class RouteSearcher {
private final static ForkJoinPool POOL = new ForkJoinPool();
private final static int THRESHOLD = (int) Math.ceil(Runtime.getRuntime().availableProcessors()/2.0);
private double maxDistance;
private double stock;
private int segmentJump;
private final double maxDistance;
private final double stock;
private final int segmentSize;
public RouteSearcher(double maxDistance, double stock, double segment) {
public RouteSearcher(double maxDistance, double stock) {
this(maxDistance, stock, 0);
}
public RouteSearcher(double maxDistance, double stock, int segmentSize) {
this.maxDistance = maxDistance;
this.stock = stock;
this.segmentJump = (int) Math.floor(segment/maxDistance);
this.segmentSize = segmentSize;
}
public List<PathRoute> getPaths(Vendor from, Vendor to, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit){
if (segmentJump == 0){
RouteGraph sGraph = new RouteGraph(from, vendors, stock, maxDistance, true, jumps);
segmentJump = sGraph.getMinJumps() > 1 ? sGraph.getMinJumps()-1 : sGraph.getMinJumps();
}
return POOL.invoke(new SegmentSearcher(from, to, vendors, jumps, balance, cargo, limit));
}
public List<PathRoute> getPaths(Vendor from, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit){
if (segmentJump == 0){
RouteGraph sGraph = new RouteGraph(from, vendors, stock, maxDistance, true, jumps);
segmentJump = sGraph.getMinJumps() > 1 ? sGraph.getMinJumps()-1 : sGraph.getMinJumps();
}
return POOL.invoke(new SegmentSearcher(from, null, vendors, jumps, balance, cargo, limit));
}
public class SegmentSearcher extends RecursiveTask<List<PathRoute>> {
private final Vendor source;
private final Vendor target;
private final Collection<Vendor> vendors;
private final int jumps;
private final double balance;
private final int cargo;
private int limit;
protected final Vendor source;
protected final Vendor target;
protected final Collection<Vendor> vendors;
protected final int jumps;
protected final double balance;
protected final int cargo;
protected int limit;
public SegmentSearcher(Vendor source, Vendor target, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit) {
this.source = source;
@@ -64,15 +59,17 @@ public class RouteSearcher {
@Override
protected List<PathRoute> compute() {
LOG.trace("Start search route to {} from {}, jumps {}", source, target, jumps);
RouteGraph sGraph = new RouteGraph(source, vendors, stock, maxDistance, true, Math.min(jumps, segmentJump));
sGraph.setLimit(cargo);
RouteGraph sGraph = new RouteGraph(source, vendors, stock, maxDistance, true, jumps, true);
int jumpsToAll = sGraph.getMinJumps();
LOG.trace("Segment jumps {}", jumpsToAll);
sGraph.setCargo(cargo);
sGraph.setBalance(balance);
List<PathRoute> res = new ArrayList<>(limit);
if (jumps <= segmentJump){
if (jumps <= jumpsToAll){
LOG.trace("Is last segment");
List<Path<Vendor>> paths;
if (target == null){
paths = sGraph.getPaths(10);
paths = sGraph.getPaths(limit);
} else {
paths = sGraph.getPathsTo(target, limit);
}
@@ -81,7 +78,7 @@ public class RouteSearcher {
}
} else {
LOG.trace("Split to segments");
List<Path<Vendor>> paths = sGraph.getPaths(1);
List<Path<Vendor>> paths = sGraph.getPaths(getPathsOnSegmentCount(sGraph), jumpsToAll-1).getList();
int i = 0;
ArrayList<SegmentSearcher> subTasks = new ArrayList<>(THRESHOLD);
while (i < paths.size()) {
@@ -89,7 +86,7 @@ public class RouteSearcher {
for (int taskIndex = 0; taskIndex < THRESHOLD && i+taskIndex < paths.size(); taskIndex++) {
PathRoute path = (PathRoute) paths.get(i+taskIndex);
double newBalance = balance + path.getRoot().getProfit();
SegmentSearcher task = new SegmentSearcher(path.get(), target, vendors, jumps - segmentJump, newBalance, cargo, (int) Math.ceil(limit / 2.0));
SegmentSearcher task = new SegmentSearcher(path.get(), target, vendors, jumps - path.getLength(), newBalance, cargo, 1);
task.fork();
subTasks.add(task);
}
@@ -100,9 +97,18 @@ public class RouteSearcher {
i+=subTasks.size();
}
}
finish(res);
return res;
}
private int getPathsOnSegmentCount(RouteGraph graph){
if (segmentSize ==0){
return graph.vertexes.size()*graph.minJumps;
} else {
return segmentSize;
}
}
private void add(SegmentSearcher task, PathRoute path, List<PathRoute> res){
List<PathRoute> tail = task.join();
@@ -111,10 +117,15 @@ public class RouteSearcher {
} else {
path.add(tail.get(0), false);
path.sort(balance, cargo);
RouteGraph.addToTop(res, path.getEnd(), limit, RouteGraph.comparator);
TopList.addToTop(res, path.getEnd(), limit, RouteGraph.byProfitComparator);
}
}
private void finish(List<PathRoute> res){
if (res.size() < limit)
res.sort(RouteGraph.byProfitComparator);
}
}

View File

@@ -0,0 +1,91 @@
package ru.trader.graph;
import java.util.*;
import java.util.function.Function;
public class TopList<T> {
protected final List<T> list;
protected final int limit;
protected final Comparator<T> comparator;
public TopList(int limit) {
this(limit, null);
}
public TopList(int limit, Comparator<T> comparator) {
this.list = new ArrayList<>(limit);
this.limit = limit;
this.comparator = comparator;
}
//return true if is last entry or list is full
public boolean add(T entry){
if (comparator != null){
addToTop(list, entry, limit, comparator);
} else {
if (list.size() >= limit) return false;
list.add(entry);
if (list.size() >= limit) return false;
}
return true;
}
public List<T> getList() {
return list;
}
public static <T> void addToGroupTop(List<T> list, T entry, int limit, Comparator<T> comparator, Function<T, Integer> getGroup, int groupSize) {
boolean isFull = list.size() >= limit;
int group = getGroup.apply(entry);
int groupStart = groupSize * group;
int groupEnd = groupSize * (group + 1);
if (!isFull){
if (groupStart >= list.size()) groupStart = list.size();
if (groupEnd >= list.size()) groupEnd = list.size();
}
List<T> groupList = list.subList(groupStart, groupEnd);
T removeEntry = addToTop(groupList, entry, groupSize, comparator);
if (!isFull && removeEntry != null && group != getGroup.apply(removeEntry)){
addToGroupTop(list, removeEntry, limit, comparator, getGroup, groupSize);
}
}
public static <T> T addToTop(List<T> list, T entry, int limit, Comparator<T> comparator) {
if (list.size() == limit) {
int index = Collections.binarySearch(list, entry, comparator);
if (index < 0) index = -1 - index;
if (index == limit) return null;
list.add(index, entry);
return list.remove(limit);
} else {
if (list.size() < limit - 1) {
list.add(entry);
} else {
list.add(entry);
list.sort(comparator);
}
}
return null;
}
public static <T> void addAllToTop(List<T> list, Collection<T> sortEntries, int limit, Comparator<T> comparator) {
for (T entry : sortEntries) {
if (list.size() == limit) {
int index = Collections.binarySearch(list, entry, comparator);
if (index < 0) index = -1 - index;
if (index == limit) return;
list.add(index, entry);
list.remove(limit);
} else {
if (list.size() < limit - 1) {
list.add(entry);
} else {
list.add(entry);
list.sort(comparator);
}
}
}
}
}

View File

@@ -8,6 +8,7 @@ import ru.trader.graph.PathRoute;
import ru.trader.store.Store;
import java.io.InputStream;
import java.util.Collection;
import java.util.Optional;
public class MarketAnalyzerTest2 extends Assert {
private static MarketAnalyzer analyzer;
@@ -32,11 +33,13 @@ public class MarketAnalyzerTest2 extends Assert {
analyzer.setCargo(440);analyzer.setTank(40);analyzer.setMaxDistance(13.4);analyzer.setJumps(6);
Collection<PathRoute> paths = analyzer.getPaths(ithaca, ithaca, 6000000);
PathRoute expect = PathRoute.toPathRoute(ithaca, morgor, lhs3006, lhs3262, lhs3006, morgor, ithaca);
PathRoute actual = paths.stream().filter((p)->p.equals(expect)).findFirst().get().getRoot();
Optional<PathRoute> path = paths.stream().filter((p)->p.equals(expect)).findFirst();
assertTrue(path.isPresent());
PathRoute actual = path.get().getRoot();
TestUtil.assertCollectionContain(paths, expect);
assertEquals(981200, actual.getProfit(), 0.00001);
assertEquals(72.42, actual.getDistance(), 0.01);
assertEquals(2, actual.getLandsCount());
assertEquals(490600, actual.getProfit()/actual.getLandsCount() , 0.00001);
assertEquals(490600, actual.getAvgProfit() , 0.00001);
}
}

View File

@@ -43,7 +43,7 @@ public class RouteGraphTest extends Assert {
public void testRoutes() throws Exception {
RouteGraph graph = new RouteGraph(v1, market.get(), 1, 1, true, 4);
graph.setBalance(500);
graph.setLimit(5);
graph.setCargo(5);
//Profit: 150 180 200 230 670 620 950 890 620 950 1015 1180 890 950 930
//Landings: 1 2 3 4 4 2 3 3 2 3 4 4 3 3 4
//Prof: 150 90 66.66 57.5 167.5 310 316.66 296.66 310 316.66 253.75 295 296.66 316.66 232.5

View File

@@ -25,11 +25,10 @@ public class RouteSearcherTest extends Assert {
// Ithaca (Palladium to LHS 3262) -> Morgor -> LHS 3006 -> LHS 3262 (Consumer Technology to Ithaca) -> LHS 3006 -> Morgor -> Ithaca
// Profit: 981200, avg: 490600, distance: 67.5, lands: 2
Vendor ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get();
Vendor lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get();
RouteSearcher searcher = new RouteSearcher(13.4, 40, 50);
RouteSearcher searcher = new RouteSearcher(13.4, 40);
RouteGraph graph = new RouteGraph(ithaca, market.get(), 40, 13.4, true, 6);
graph.setLimit(440);
graph.setCargo(440);
graph.setBalance(6000000);
@@ -40,13 +39,38 @@ public class RouteSearcherTest extends Assert {
PathRoute actual = apaths.stream().findFirst().get();
assertTrue("Routes is different",expect.isRoute(actual));
graph = new RouteGraph(lhs3262, market.get(), 40, 13.4, true, 6);
graph.setLimit(440);
}
@Test
public void testRoutes2() throws Exception {
// Balance: 6000000, cargo: 440, tank: 40, distance: 13.6, jumps: 6
// Ithaca (Palladium to LHS 3262) -> Morgor -> LHS 3006 -> LHS 3262 (Consumer Technology to Ithaca) -> LHS 3006 -> Morgor -> Ithaca
// Profit: 981200, avg: 490600, distance: 67.5, lands: 2
Vendor ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get();
Vendor lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get();
RouteSearcher searcher = new RouteSearcher(13.6, 40);
RouteGraph graph = new RouteGraph(ithaca, market.get(), 40, 13.6, true, 6);
graph.setCargo(440);
graph.setBalance(6000000);
List<Path<Vendor>> epaths = graph.getPathsTo(ithaca, 10);
PathRoute expect = epaths.stream().map(p -> (PathRoute) p).findFirst().get();
List<PathRoute> apaths = searcher.getPaths(ithaca, ithaca, market.get(), 6, 6000000, 440, 10);
PathRoute actual = apaths.stream().findFirst().get();
assertTrue("Routes is different",expect.isRoute(actual));
graph = new RouteGraph(lhs3262, market.get(), 40, 13.6, true, 6);
graph.setCargo(440);
graph.setBalance(6000000);
expect = graph.getPathsTo(lhs3262, 10).stream().map(p -> (PathRoute)p).findFirst().get();
actual = searcher.getPaths(lhs3262, lhs3262, market.get(), 6, 6000000, 440, 10).stream().findFirst().get();
assertTrue("Routes is different",expect.isRoute(actual));
apaths = searcher.getPaths(lhs3262, lhs3262, market.get(), 6, 6000000, 440, 10);
actual = apaths.stream().findFirst().get();
assertEquals("Routes is different",expect.getAvgProfit(), actual.getAvgProfit(), 0.00001);
}
}

View File

@@ -0,0 +1,87 @@
package ru.trader.graph;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
public class TopListTest extends Assert {
private static final Function<Integer, Integer> getGroup = (o1) -> Math.floorDiv(o1, 10);
private static final Comparator<Integer> groupcomp = (o1, o2) -> {
int cmp = Integer.compare(getGroup.apply(o1), getGroup.apply(o2));
if (cmp !=0 ) return cmp;
return o1.compareTo(o2);
};
private void add(List<Integer> list, Integer entry){
TopList.addToGroupTop(list, entry, 10, groupcomp, getGroup, 1);
}
@Test
public void testAddToGroup() throws Exception {
ArrayList<Integer> top = new ArrayList<>(10);
add(top, 5);
add(top, 15);
add(top, 22);
add(top, 34);
add(top, 36);
add(top, 21);
add(top, 7);
add(top, 6);
add(top, 3);
assertEquals(4, top.size());
assertEquals(3, top.get(0).intValue());
assertEquals(15, top.get(1).intValue());
assertEquals(21, top.get(2).intValue());
assertEquals(34, top.get(3).intValue());
}
@Test
public void testAddToGroup2() throws Exception {
ArrayList<Integer> top = new ArrayList<>(10);
add(top, 36);
add(top, 15);
add(top, 22);
add(top, 6);
add(top, 34);
add(top, 5);
add(top, 21);
add(top, 7);
add(top, 3);
assertEquals(4, top.size());
assertEquals(3, top.get(0).intValue());
assertEquals(15, top.get(1).intValue());
assertEquals(21, top.get(2).intValue());
assertEquals(34, top.get(3).intValue());
}
@Test
public void testAddToGroup3() throws Exception {
ArrayList<Integer> top = new ArrayList<>(10);
add(top, 21);
add(top, 34);
add(top, 36);
add(top, 3);
add(top, 15);
add(top, 22);
add(top, 6);
add(top, 34);
add(top, 5);
add(top, 7);
assertEquals(4, top.size());
assertEquals(3, top.get(0).intValue());
assertEquals(15, top.get(1).intValue());
assertEquals(21, top.get(2).intValue());
assertEquals(34, top.get(3).intValue());
}
}