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 Market market;
private double tank; private double tank;
private double maxDistance; private double maxDistance;
private double segment = 50; private int segmentSize;
private int count = 100; private int limit;
private int jumps; private int jumps;
private int cargo; private int cargo;
@@ -21,16 +21,18 @@ public class MarketAnalyzer {
public MarketAnalyzer(Market market) { public MarketAnalyzer(Market market) {
this.market = market; this.market = market;
this.limit = 100;
this.segmentSize = 0;
} }
public Collection<Order> getTop(double balance){ public Collection<Order> getTop(double balance){
LOG.debug("Get top {}", count); LOG.debug("Get top {}", limit);
Collection<Vendor> vendors = market.get(); Collection<Vendor> vendors = market.get();
List<Order> top = new ArrayList<>(count); List<Order> top = new ArrayList<>(limit);
for (Vendor vendor : vendors) { for (Vendor vendor : vendors) {
LOG.trace("Check vendor {}", vendor); LOG.trace("Check vendor {}", vendor);
Collection<Order> orders = getOrders(vendor, balance, top.isEmpty() ? 0 : top.get(top.size()-1).getProfit()); 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; return top;
} }
@@ -101,22 +103,22 @@ public class MarketAnalyzer {
} }
public Collection<PathRoute> getPaths(Vendor from, double balance){ public Collection<PathRoute> getPaths(Vendor from, double balance){
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segment); RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize);
return searcher.getPaths(from, market.get(), jumps, balance, cargo, count); return searcher.getPaths(from, market.get(), jumps, balance, cargo, limit);
} }
public Collection<PathRoute> getPaths(Vendor from, Vendor to, double balance){ public Collection<PathRoute> getPaths(Vendor from, Vendor to, double balance){
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segment); RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize);
return searcher.getPaths(from, to, market.get(), jumps, balance, cargo, count); return searcher.getPaths(from, to, market.get(), jumps, balance, cargo, limit);
} }
public Collection<PathRoute> getTopPaths(double balance){ public Collection<PathRoute> getTopPaths(double balance){
List<PathRoute> top = new ArrayList<>(count); List<PathRoute> top = new ArrayList<>(limit);
RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segment); RouteSearcher searcher = new RouteSearcher(maxDistance, tank, segmentSize);
Collection<Vendor> vendors = market.get(); Collection<Vendor> vendors = market.get();
for (Vendor vendor : vendors) { for (Vendor vendor : vendors) {
Collection<PathRoute> paths = searcher.getPaths(vendor, vendor, vendors, jumps, balance, cargo, 3); 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; return top;
} }
@@ -138,5 +140,11 @@ public class MarketAnalyzer {
this.cargo = cargo; 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>> { public class Graph<T extends Connectable<T>> {
private final static ForkJoinPool POOL = new ForkJoinPool(); private final static ForkJoinPool POOL = new ForkJoinPool();
private final static int THRESHOLD = 4; private final static int THRESHOLD = 4;
private final static int DEFAULT_COUNT = 200;
@FunctionalInterface @FunctionalInterface
public interface PathConstructor<E extends Connectable<E>> { 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 static Logger LOG = LoggerFactory.getLogger(Graph.class);
private final Vertex<T> root; protected final Vertex<T> root;
private final Map<T,Vertex<T>> vertexes; protected final Map<T,Vertex<T>> vertexes;
private final double stock; protected final double stock;
private final double maxDistance; protected final double maxDistance;
private final boolean withRefill; protected final boolean withRefill;
private final PathConstructor<T> pathFabric; private final PathConstructor<T> pathFabric;
private int minJumps; protected int minJumps;
public Graph(T start, Collection<T> set, double stock, int maxDeep) { 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); return vertexes.get(entry);
} }
private void findPathsTo(Vertex<T> target, int max, List<Path<T>> res){ private void findPathsTo(Vertex<T> target, TopList<Path<T>> res, int deep){
POOL.invoke(new PathFinder(res, max, pathFabric.build(root), target, root.getLevel() - 1, stock)); POOL.invoke(new PathFinder(res, pathFabric.build(root), target, deep-1, stock));
} }
public List<Path<T>> getPathsTo(T entry){ 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){ 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); Vertex<T> target = getVertex(entry);
ArrayList<Path<T>> paths = new ArrayList<>(max); TopList<Path<T>> paths = newTopList(max);
findPathsTo(target, max, paths); findPathsTo(target, paths, deep);
return paths; return paths;
} }
public List<Path<T>> getPaths(int count){ 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()) { for (Vertex<T> target : vertexes.values()) {
ArrayList<Path<T>> p = new ArrayList<>(count); TopList<Path<T>> p = newTopList(minJumps);
findPathsTo(target, count, p); findPathsTo(target, p, deep);
for (Path<T> path : p) { for (Path<T> path : p.getList()) {
paths.add(path); paths.add(path);
} }
} }
return paths; return paths;
} }
protected TopList<Path<T>> newTopList(int count){
// if is true, then break search return new TopList<>(count);
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;
} }
public Path<T> getFastPathTo(T entry){ public Path<T> getFastPathTo(T entry){
Vertex<T> target = getVertex(entry); Vertex<T> target = getVertex(entry);
if (target == null) return null; if (target == null) return null;
@@ -246,17 +250,15 @@ public class Graph<T extends Connectable<T>> {
} }
private class PathFinder extends RecursiveAction { private class PathFinder extends RecursiveAction {
private final List<Path<T>> paths; private final TopList<Path<T>> paths;
private final int max;
private final Path<T> head; private final Path<T> head;
private final Vertex<T> target; private final Vertex<T> target;
private final int deep; private final int deep;
private final double limit; private final double limit;
private final DistanceFilter distanceFilter; 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.paths = paths;
this.max = max;
this.head = head; this.head = head;
this.target = target; this.target = target;
this.deep = deep; this.deep = deep;
@@ -276,7 +278,7 @@ public class Graph<T extends Connectable<T>> {
path.finish(); path.finish();
LOG.trace("Last edge find, add path {}", path); LOG.trace("Last edge find, add path {}", path);
synchronized (paths){ 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 // refill
if (nextLimit < 0) nextLimit = stock - next.getLength(); if (nextLimit < 0) nextLimit = stock - next.getLength();
//Recursive search //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(); task.fork();
subTasks.add(task); subTasks.add(task);
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){ if (subTasks.size() == THRESHOLD || !iterator.hasNext()){

View File

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

View File

@@ -7,12 +7,13 @@ import java.util.*;
public class RouteGraph extends Graph<Vendor> { public class RouteGraph extends Graph<Vendor> {
private double balance; 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 r1 = p1.getRoot();
PathRoute r2 = p2.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; if (cmp != 0 ) return cmp;
cmp = Double.compare(r1.getDistance(), r2.getDistance()); cmp = Double.compare(r1.getDistance(), r2.getDistance());
if (cmp != 0) return cmp; if (cmp != 0) return cmp;
@@ -21,61 +22,65 @@ public class RouteGraph extends Graph<Vendor> {
return cmp; 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) { 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) { public void setBalance(double balance) {
this.balance = balance; this.balance = balance;
} }
public void setLimit(int limit) { public void setCargo(int cargo) {
this.limit = limit; this.cargo = cargo;
} }
@Override @Override
protected boolean onFindPath(List<Path<Vendor>> paths, int max, Path<Vendor> path) { protected TopList<Path<Vendor>> newTopList(int count) {
PathRoute route = (PathRoute) path; int groupSize = 0;
route.sort(balance, limit); if (groupRes && getMinJumps() > 1){
addToTop(paths, route, max, (r1, r2) -> comparator.compare((PathRoute)r1, (PathRoute)r2)); groupSize = Math.floorDiv(count, root.getLevel());
return false; }
return new TopRoutes(count, groupSize);
} }
public static <T> void addToTop(List<T> list, T entry, int limit, Comparator<T> comparator){ private class TopRoutes extends TopList<Path<Vendor>> {
if (list.size() == limit){ private final int groupSize;
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 { public TopRoutes(int limit, int groupSize) {
if (list.size() < limit-1){ super(limit, (p1, p2) -> groupSize > 0 ? groupByLengthComparator.compare((PathRoute)p1, (PathRoute)p2) : RouteGraph.byProfitComparator.compare((PathRoute)p1, (PathRoute)p2));
list.add(entry); this.groupSize = groupSize;
} else {
list.add(entry);
list.sort(comparator);
}
}
} }
public static <T> void addAllToTop(List<T> list, Collection<T> sortEntries, int limit, Comparator<T> comparator){ @Override
for (T entry : sortEntries) { public boolean add(Path<Vendor> entry) {
if (list.size() == limit){ if (comparator != null){
int index = Collections.binarySearch(list, entry, comparator); ((PathRoute)entry).sort(balance, cargo);
if (index < 0) index = -1 - index; }
if (index == limit) return; if (groupSize>0){
list.add(index, entry); addToGroupTop(list, entry, limit, comparator, (e) -> e.getLength()-1, groupSize);
list.remove(limit);
} else { } else {
if (list.size() < limit-1){ if (comparator != null){
list.add(entry); addToTop(list, entry, limit, comparator);
} else { } else {
if (list.size() >= limit) return false;
list.add(entry); 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 ForkJoinPool POOL = new ForkJoinPool();
private final static int THRESHOLD = (int) Math.ceil(Runtime.getRuntime().availableProcessors()/2.0); private final static int THRESHOLD = (int) Math.ceil(Runtime.getRuntime().availableProcessors()/2.0);
private double maxDistance; private final double maxDistance;
private double stock; private final double stock;
private int segmentJump; 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.maxDistance = maxDistance;
this.stock = stock; 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){ 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)); 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){ 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)); return POOL.invoke(new SegmentSearcher(from, null, vendors, jumps, balance, cargo, limit));
} }
public class SegmentSearcher extends RecursiveTask<List<PathRoute>> { public class SegmentSearcher extends RecursiveTask<List<PathRoute>> {
private final Vendor source; protected final Vendor source;
private final Vendor target; protected final Vendor target;
private final Collection<Vendor> vendors; protected final Collection<Vendor> vendors;
private final int jumps; protected final int jumps;
private final double balance; protected final double balance;
private final int cargo; protected final int cargo;
private int limit; protected int limit;
public SegmentSearcher(Vendor source, Vendor target, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit) { public SegmentSearcher(Vendor source, Vendor target, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit) {
this.source = source; this.source = source;
@@ -64,15 +59,17 @@ public class RouteSearcher {
@Override @Override
protected List<PathRoute> compute() { protected List<PathRoute> compute() {
LOG.trace("Start search route to {} from {}, jumps {}", source, target, jumps); LOG.trace("Start search route to {} from {}, jumps {}", source, target, jumps);
RouteGraph sGraph = new RouteGraph(source, vendors, stock, maxDistance, true, Math.min(jumps, segmentJump)); RouteGraph sGraph = new RouteGraph(source, vendors, stock, maxDistance, true, jumps, true);
sGraph.setLimit(cargo); int jumpsToAll = sGraph.getMinJumps();
LOG.trace("Segment jumps {}", jumpsToAll);
sGraph.setCargo(cargo);
sGraph.setBalance(balance); sGraph.setBalance(balance);
List<PathRoute> res = new ArrayList<>(limit); List<PathRoute> res = new ArrayList<>(limit);
if (jumps <= segmentJump){ if (jumps <= jumpsToAll){
LOG.trace("Is last segment"); LOG.trace("Is last segment");
List<Path<Vendor>> paths; List<Path<Vendor>> paths;
if (target == null){ if (target == null){
paths = sGraph.getPaths(10); paths = sGraph.getPaths(limit);
} else { } else {
paths = sGraph.getPathsTo(target, limit); paths = sGraph.getPathsTo(target, limit);
} }
@@ -81,7 +78,7 @@ public class RouteSearcher {
} }
} else { } else {
LOG.trace("Split to segments"); 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; int i = 0;
ArrayList<SegmentSearcher> subTasks = new ArrayList<>(THRESHOLD); ArrayList<SegmentSearcher> subTasks = new ArrayList<>(THRESHOLD);
while (i < paths.size()) { while (i < paths.size()) {
@@ -89,7 +86,7 @@ public class RouteSearcher {
for (int taskIndex = 0; taskIndex < THRESHOLD && i+taskIndex < paths.size(); taskIndex++) { for (int taskIndex = 0; taskIndex < THRESHOLD && i+taskIndex < paths.size(); taskIndex++) {
PathRoute path = (PathRoute) paths.get(i+taskIndex); PathRoute path = (PathRoute) paths.get(i+taskIndex);
double newBalance = balance + path.getRoot().getProfit(); 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(); task.fork();
subTasks.add(task); subTasks.add(task);
} }
@@ -100,9 +97,18 @@ public class RouteSearcher {
i+=subTasks.size(); i+=subTasks.size();
} }
} }
finish(res);
return 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){ private void add(SegmentSearcher task, PathRoute path, List<PathRoute> res){
List<PathRoute> tail = task.join(); List<PathRoute> tail = task.join();
@@ -111,10 +117,15 @@ public class RouteSearcher {
} else { } else {
path.add(tail.get(0), false); path.add(tail.get(0), false);
path.sort(balance, cargo); 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 ru.trader.store.Store;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collection; import java.util.Collection;
import java.util.Optional;
public class MarketAnalyzerTest2 extends Assert { public class MarketAnalyzerTest2 extends Assert {
private static MarketAnalyzer analyzer; 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); analyzer.setCargo(440);analyzer.setTank(40);analyzer.setMaxDistance(13.4);analyzer.setJumps(6);
Collection<PathRoute> paths = analyzer.getPaths(ithaca, ithaca, 6000000); Collection<PathRoute> paths = analyzer.getPaths(ithaca, ithaca, 6000000);
PathRoute expect = PathRoute.toPathRoute(ithaca, morgor, lhs3006, lhs3262, lhs3006, morgor, ithaca); 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); TestUtil.assertCollectionContain(paths, expect);
assertEquals(981200, actual.getProfit(), 0.00001); assertEquals(981200, actual.getProfit(), 0.00001);
assertEquals(72.42, actual.getDistance(), 0.01); assertEquals(72.42, actual.getDistance(), 0.01);
assertEquals(2, actual.getLandsCount()); 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 { public void testRoutes() throws Exception {
RouteGraph graph = new RouteGraph(v1, market.get(), 1, 1, true, 4); RouteGraph graph = new RouteGraph(v1, market.get(), 1, 1, true, 4);
graph.setBalance(500); 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 //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 //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 //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 // 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 // Profit: 981200, avg: 490600, distance: 67.5, lands: 2
Vendor ithaca = market.get().stream().filter((v)->v.getName().equals("Ithaca")).findFirst().get(); 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); RouteGraph graph = new RouteGraph(ithaca, market.get(), 40, 13.4, true, 6);
graph.setLimit(440); graph.setCargo(440);
graph.setBalance(6000000); graph.setBalance(6000000);
@@ -40,13 +39,38 @@ public class RouteSearcherTest extends Assert {
PathRoute actual = apaths.stream().findFirst().get(); PathRoute actual = apaths.stream().findFirst().get();
assertTrue("Routes is different",expect.isRoute(actual)); 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); graph.setBalance(6000000);
expect = graph.getPathsTo(lhs3262, 10).stream().map(p -> (PathRoute)p).findFirst().get(); 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(); apaths = searcher.getPaths(lhs3262, lhs3262, market.get(), 6, 6000000, 440, 10);
assertTrue("Routes is different",expect.isRoute(actual)); 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());
}
}