Improved segment search
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()){
|
||||
|
||||
@@ -106,5 +106,9 @@ public class Path<T extends Connectable<T>> {
|
||||
return head;
|
||||
}
|
||||
|
||||
public int getLength(){
|
||||
return isRoot() ? 0 : 1 + getPrevious().getLength();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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)){
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
91
core/src/main/java/ru/trader/graph/TopList.java
Normal file
91
core/src/main/java/ru/trader/graph/TopList.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
87
core/src/test/java/ru/trader/graph/TopListTest.java
Normal file
87
core/src/test/java/ru/trader/graph/TopListTest.java
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user