Improve Crawler implementation
This commit is contained in:
@@ -7,8 +7,10 @@ import ru.trader.core.Order;
|
||||
import ru.trader.core.TransitVendor;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
@@ -54,17 +56,15 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
|
||||
@Override
|
||||
protected ConnectibleEdge<Vendor> createEdge(Vertex<Vendor> target) {
|
||||
return new VendorsEdge(vertex, target, refill, fuelCost, false);
|
||||
return new VendorsEdge(vertex, target, refill, fuelCost);
|
||||
}
|
||||
}
|
||||
|
||||
public class VendorsEdge extends ConnectibleEdge<Vendor> {
|
||||
private List<Order> orders;
|
||||
private boolean isTarget;
|
||||
|
||||
protected VendorsEdge(Vertex<Vendor> source, Vertex<Vendor> target, boolean refill, double fuel, boolean isTarget) {
|
||||
protected VendorsEdge(Vertex<Vendor> source, Vertex<Vendor> target, boolean refill, double fuel) {
|
||||
super(source, target, refill, fuel);
|
||||
this.isTarget = isTarget;
|
||||
}
|
||||
|
||||
protected void setOrders(List<Order> orders){
|
||||
@@ -87,7 +87,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
@Override
|
||||
protected double computeWeight() {
|
||||
int jumps = source.getEntry().getPlace().equals(target.getEntry().getPlace())? 0 : 1;
|
||||
int lands = refill || !orders.isEmpty() || isTarget ? 1 : 0;
|
||||
int lands = !(target.getEntry() instanceof TransitVendor) ? 1 : 0;
|
||||
boolean transit = lands == 0 && source.getEntry() instanceof TransitVendor || target.getEntry() instanceof TransitVendor;
|
||||
double profit = getProfit();
|
||||
double score = transit ? scorer.getTransitScore(fuel) :
|
||||
@@ -105,50 +105,79 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CostTraversalEntry start(Vertex<Vendor> vertex) {
|
||||
double balance = scorer.getProfile().getBalance();
|
||||
return new VendorsTraversalEntry((CCostTraversalEntry) super.start(vertex), balance);
|
||||
protected VendorsTraversalEntry start(Vertex<Vendor> vertex) {
|
||||
double balance = getProfile().getBalance();
|
||||
return new VendorsTraversalEntry(super.start(vertex), balance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CostTraversalEntry travers(final CostTraversalEntry entry, final List<Edge<Vendor>> head, final Edge<Vendor> edge, final Vendor target) {
|
||||
VendorsTraversalEntry ve = (VendorsTraversalEntry)entry;
|
||||
double balance = ve.balance;
|
||||
Vendor buyer = edge.getTarget().getEntry();
|
||||
List<Order> orders = ((VendorsEdge) edge).getOrders();
|
||||
if (edge.getSource().getEntry() instanceof TransitVendor &&
|
||||
!(buyer instanceof TransitVendor)){
|
||||
LOG.trace("{} is transit, search seller", edge.getSource().getEntry());
|
||||
for (int i = head.size() - 1; i >= 0; i--) {
|
||||
Vendor seller = head.get(i).getSource().getEntry();
|
||||
if (!(seller instanceof TransitVendor)){
|
||||
orders = MarketUtils.getOrders(seller, buyer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
orders = MarketUtils.getStack(orders, balance, scorer.getProfile().getShip().getCargo());
|
||||
|
||||
CCostTraversalEntry ce = (CCostTraversalEntry) super.travers(entry, head, edge, target);
|
||||
ConnectibleEdge<Vendor> cedge = (ConnectibleEdge<Vendor>) ce.getEdge();
|
||||
VendorsEdge addingEdge = new VendorsEdge(cedge.getSource(), cedge.getTarget(), cedge.isRefill(), cedge.getFuel(), target.equals(buyer));
|
||||
addingEdge.setOrders(orders);
|
||||
return new VendorsTraversalEntry(head, addingEdge, entry.getWeight(), ce.getFuel(), balance+addingEdge.getProfit());
|
||||
protected VendorsTraversalEntry travers(final CostTraversalEntry entry, final Edge<Vendor> edge, final Vendor target) {
|
||||
VendorsEdge vEdge = (VendorsEdge) edge;
|
||||
CCostTraversalEntry ce = super.travers(entry, edge, target);
|
||||
return new VendorsTraversalEntry((VendorsTraversalEntry) entry, edge, ce.getFuel(), ((VendorsTraversalEntry)entry).balance + vEdge.getProfit());
|
||||
}
|
||||
|
||||
protected class VendorsTraversalEntry extends CCostTraversalEntry {
|
||||
private final double balance;
|
||||
|
||||
protected VendorsTraversalEntry(CCostTraversalEntry entry, double balance) {
|
||||
super(entry.getHead(), entry.getVertex(), entry.getFuel());
|
||||
super(entry.getTarget(), entry.getFuel());
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
protected VendorsTraversalEntry(List<Edge<Vendor>> head, Edge<Vendor> edge, double cost, double fuel, double balance) {
|
||||
super(head, edge, cost, fuel);
|
||||
protected VendorsTraversalEntry(VendorsTraversalEntry head, Edge<Vendor> edge, double fuel, double balance) {
|
||||
super(head, edge, fuel);
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean check(Edge<Vendor> e) {
|
||||
boolean good = super.check(e);
|
||||
// remove transit cicles
|
||||
if (good && e.getSource().getEntry() instanceof TransitVendor && !(e.getTarget().getEntry() instanceof TransitVendor)){
|
||||
Optional<Vendor> seller = getSeller();
|
||||
good = seller.isPresent() && !e.getTarget().isEntry(seller.get());
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VendorsEdge wrap(Edge<Vendor> edge) {
|
||||
ConnectibleEdge<Vendor> cEdge = super.wrap(edge);
|
||||
Vendor buyer = edge.getTarget().getEntry();
|
||||
List<Order> orders = new ArrayList<>();
|
||||
orders.addAll(((VendorsEdge) edge).getOrders());
|
||||
if (edge.getSource().getEntry() instanceof TransitVendor && !(buyer instanceof TransitVendor)){
|
||||
LOG.trace("{} is transit, search seller", edge.getSource().getEntry());
|
||||
Optional<Vendor> seller = getSeller();
|
||||
if (seller.isPresent()){
|
||||
orders = MarketUtils.getOrders(seller.get(), buyer);
|
||||
}
|
||||
}
|
||||
orders = MarketUtils.getStack(orders, balance, getShip().getCargo());
|
||||
VendorsEdge res = new VendorsEdge(edge.getSource(), edge.getTarget(), cEdge.isRefill(), cEdge.getFuel());
|
||||
res.setOrders(orders);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Optional<Vendor> getSeller(){
|
||||
Vendor res = null;
|
||||
Edge<Vendor> e = getEdge();
|
||||
Vendor seller = e.getSource().getEntry();
|
||||
if (!(seller instanceof TransitVendor)){
|
||||
res = seller;
|
||||
} else {
|
||||
for (int i = head.size() - 1; i >= 0; i--) {
|
||||
e = head.getEdge();
|
||||
seller = e.getSource().getEntry();
|
||||
if (!(seller instanceof TransitVendor)){
|
||||
res = seller;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.ofNullable(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import ru.trader.core.Profile;
|
||||
import ru.trader.core.Ship;
|
||||
import ru.trader.graph.Connectable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(CCrawler.class);
|
||||
@@ -18,47 +18,45 @@ public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
||||
super(graph, onFoundFunc);
|
||||
}
|
||||
|
||||
private Ship getShip(){
|
||||
protected Ship getShip(){
|
||||
return ((ConnectibleGraph<T>)graph).getProfile().getShip();
|
||||
}
|
||||
|
||||
private Profile getProfile(){
|
||||
protected Profile getProfile(){
|
||||
return ((ConnectibleGraph<T>)graph).getProfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CostTraversalEntry start(Vertex<T> vertex) {
|
||||
protected CCostTraversalEntry start(Vertex<T> vertex) {
|
||||
double fuel = getShip().getTank();
|
||||
return new CCostTraversalEntry(new ArrayList<>(), vertex, fuel);
|
||||
return new CCostTraversalEntry(vertex, fuel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CostTraversalEntry travers(CostTraversalEntry entry, List<Edge<T>> head, Edge<T> edge, T target) {
|
||||
T source = entry.vertex.getEntry();
|
||||
double distance = source.getDistance(edge.target.getEntry());
|
||||
double fuelCost = getShip().getFuelCost(((CCostTraversalEntry)entry).fuel, distance);
|
||||
double nextLimit = getProfile().withRefill() ? ((CCostTraversalEntry)entry).fuel - fuelCost : getShip().getTank();
|
||||
boolean refill = nextLimit < 0 && source.canRefill();
|
||||
if (refill) {
|
||||
protected CCostTraversalEntry travers(CostTraversalEntry entry, Edge<T> edge, T target) {
|
||||
@SuppressWarnings("unchecked")
|
||||
CCostTraversalEntry cEntry = (CCostTraversalEntry)entry;
|
||||
ConnectibleEdge<T> cEdge = (ConnectibleEdge<T>) edge;
|
||||
double nextLimit;
|
||||
if (cEdge.isRefill()) {
|
||||
LOG.trace("Refill");
|
||||
refill = true;
|
||||
fuelCost = getShip().getFuelCost(distance);
|
||||
nextLimit = getShip().getTank() - fuelCost;
|
||||
nextLimit = getShip().getTank() - cEdge.getFuel();
|
||||
} else {
|
||||
nextLimit = getProfile().withRefill() ? cEntry.getFuel() - cEdge.getFuel() : getShip().getTank();
|
||||
}
|
||||
edge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget(), refill, fuelCost);
|
||||
return new CCostTraversalEntry(head, edge, entry.getWeight(), nextLimit);
|
||||
return new CCostTraversalEntry(cEntry, edge, nextLimit);
|
||||
}
|
||||
|
||||
protected class CCostTraversalEntry extends CostTraversalEntry {
|
||||
private final double fuel;
|
||||
|
||||
protected CCostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex, double fuel) {
|
||||
super(head, vertex);
|
||||
protected CCostTraversalEntry(Vertex<T> vertex, double fuel) {
|
||||
super(vertex);
|
||||
this.fuel = fuel;
|
||||
}
|
||||
|
||||
protected CCostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost, double fuel) {
|
||||
super(head, edge, cost);
|
||||
protected CCostTraversalEntry(CCostTraversalEntry head, Edge<T> edge, double fuel) {
|
||||
super(head, edge);
|
||||
this.fuel = fuel;
|
||||
}
|
||||
|
||||
@@ -67,11 +65,26 @@ public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<Edge<T>> getIteratorInstance() {
|
||||
return vertex.getEdges().stream().filter(e -> {
|
||||
public List<Edge<T>> collect(Collection<Edge<T>> src) {
|
||||
return src.stream().filter(this::check).map(this::wrap).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected boolean check(Edge<T> e){
|
||||
ConnectibleEdge<T> edge = (ConnectibleEdge<T>) e;
|
||||
return edge.getFuel() <= fuel || e.getSource().getEntry().canRefill();
|
||||
}).iterator();
|
||||
return edge.getFuel() <= fuel || edge.getSource().getEntry().canRefill();
|
||||
}
|
||||
|
||||
protected ConnectibleEdge<T> wrap(Edge<T> edge){
|
||||
T source = edge.source.getEntry();
|
||||
double distance = source.getDistance(edge.target.getEntry());
|
||||
double fuelCost = getShip().getFuelCost(fuel, distance);
|
||||
double nextLimit = getProfile().withRefill() ? fuel - fuelCost : getShip().getTank();
|
||||
boolean refill = nextLimit < 0 && source.canRefill();
|
||||
if (refill) {
|
||||
refill = true;
|
||||
fuelCost = getShip().getFuelCost(distance);
|
||||
}
|
||||
return new ConnectibleEdge<>(edge.getSource(), edge.getTarget(), refill, fuelCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
public interface CostTraversal<T> extends Traversal<T> {
|
||||
double getWeight();
|
||||
|
||||
}
|
||||
@@ -5,10 +5,15 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Crawler<T> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Crawler.class);
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = 12 * (Runtime.getRuntime().availableProcessors() > 1 ? Runtime.getRuntime().availableProcessors() - 1 : 1);
|
||||
private final static int SPLIT_SIZE = 3;
|
||||
|
||||
protected final Graph<T> graph;
|
||||
private final Consumer<List<Edge<T>>> onFoundFunc;
|
||||
@@ -20,9 +25,9 @@ public class Crawler<T> {
|
||||
this.onFoundFunc = onFoundFunc;
|
||||
}
|
||||
|
||||
protected List<Edge<T>> getCopyList(List<Edge<T>> head, Edge<T> tail){
|
||||
List<Edge<T>> res = new ArrayList<>(20);
|
||||
res.addAll(head);
|
||||
protected List<Edge<T>> getCopyList(Traversal<T> head, Edge<T> tail){
|
||||
List<Edge<T>> res = new ArrayList<>(head.size()+ 1);
|
||||
res.addAll(head.toEdges());
|
||||
res.add(tail);
|
||||
return res;
|
||||
}
|
||||
@@ -36,15 +41,14 @@ public class Crawler<T> {
|
||||
}
|
||||
|
||||
public void findFast(T target, int count){
|
||||
Vertex<T> t = graph.getVertex(target);
|
||||
Optional<Vertex<T>> t = graph.getVertex(target);
|
||||
int found = 0;
|
||||
if (t != null) {
|
||||
if (t.isPresent()) {
|
||||
if (count > 1) {
|
||||
Vertex<T> s = graph.getRoot();
|
||||
s.sortEdges();
|
||||
found = bfs(start(s), target, graph.getMinLevel(), count);
|
||||
int maxDeep = maxSize - (graph.getRoot().isEntry(target) ? graph.getMinJumps() * 2 : graph.getMinJumps());
|
||||
found = bfs(start(graph.getRoot()), target, maxDeep, count);
|
||||
} else {
|
||||
found = dfs(start(graph.getRoot()), target, t.getLevel() + 1, count);
|
||||
found = dfs(start(graph.getRoot()), target, t.get().getLevel() + 1, count);
|
||||
}
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
@@ -55,31 +59,36 @@ public class Crawler<T> {
|
||||
}
|
||||
|
||||
public void findMin(T target, int count){
|
||||
Vertex<T> t = graph.getVertex(target);
|
||||
Optional<Vertex<T>> t = graph.getVertex(target);
|
||||
int found = 0;
|
||||
if (t != null) {
|
||||
found = ucs(start(graph.getRoot()), target, graph.getMinLevel(), count);
|
||||
if (t.isPresent()) {
|
||||
int maxDeep = graph.getRoot().isEntry(target) ? maxSize - graph.getMinJumps() * 2 : graph.getMinLevel();
|
||||
if (maxDeep < 0) maxDeep = 0;
|
||||
if (maxSize - maxDeep <= SPLIT_SIZE){
|
||||
found = ucs(start(graph.getRoot()), target, maxDeep, count);
|
||||
} else {
|
||||
found = ucs2(start(graph.getRoot()), target, maxDeep, count);
|
||||
}
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
}
|
||||
|
||||
protected CostTraversalEntry start(Vertex<T> vertex){
|
||||
return new CostTraversalEntry(new ArrayList<>(), vertex);
|
||||
return new CostTraversalEntry(vertex);
|
||||
}
|
||||
|
||||
protected CostTraversalEntry travers(CostTraversalEntry entry, List<Edge<T>> head, Edge<T> edge, T target){
|
||||
return new CostTraversalEntry(head, edge, entry.getWeight());
|
||||
protected CostTraversalEntry travers(CostTraversalEntry entry, Edge<T> edge, T target){
|
||||
return new CostTraversalEntry(entry, edge);
|
||||
}
|
||||
|
||||
private int dfs(CostTraversalEntry entry, T target, int deep, int count) {
|
||||
int found = 0;
|
||||
List<Edge<T>> head = entry.head;
|
||||
Vertex<T> source = entry.vertex;
|
||||
LOG.trace("DFS from {} to {}, deep {}, count {}, head {}", source, target, deep, count, head);
|
||||
LOG.trace("DFS from {} to {}, deep {}, count {}, entry {}", source, target, deep, count, entry);
|
||||
if (deep == source.getLevel()){
|
||||
for (Edge<T> next : entry.getEdges()) {
|
||||
if (next.isConnect(target)){
|
||||
List<Edge<T>> res = getCopyList(head, next);
|
||||
List<Edge<T>> res = getCopyList(entry, next);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
@@ -88,11 +97,11 @@ public class Crawler<T> {
|
||||
}
|
||||
}
|
||||
if (found < count){
|
||||
if (deep < source.getLevel() && head.size() < maxSize-1) {
|
||||
if (deep < source.getLevel() && entry.size() < maxSize-1) {
|
||||
LOG.trace("Search around");
|
||||
for (Edge<T> edge : entry.getEdges()) {
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
found += dfs(travers(entry, getCopyList(head, edge), edge, target), target, deep, count-found);
|
||||
found += dfs(travers(entry, edge, target), target, deep, count-found);
|
||||
if (found >= count) break;
|
||||
}
|
||||
}
|
||||
@@ -104,21 +113,21 @@ public class Crawler<T> {
|
||||
LOG.trace("BFS from {} to {}, deep {}, count {}", root.vertex, target, deep, count);
|
||||
int found = 0;
|
||||
LinkedList<CostTraversalEntry> queue = new LinkedList<>();
|
||||
root.sort();
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.poll();
|
||||
List<Edge<T>> head = entry.head;
|
||||
Vertex<T> source = entry.vertex;
|
||||
if (head.size() >= maxSize){
|
||||
if (entry.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
continue;
|
||||
}
|
||||
LOG.trace("Search from {} to {}, head {}", source, target, head);
|
||||
LOG.trace("Search from {} to {}, entry {}", source, target, entry);
|
||||
Iterator<Edge<T>> iterator = entry.iterator();
|
||||
while (iterator.hasNext()){
|
||||
Edge<T> edge = iterator.next();
|
||||
if (edge.isConnect(target)){
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
List<Edge<T>> res = getCopyList(entry, edge);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
@@ -126,8 +135,9 @@ public class Crawler<T> {
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel()) {
|
||||
edge.getTarget().sortEdges();
|
||||
queue.add(travers(entry, getCopyList(head, edge), edge, target));
|
||||
CostTraversalEntry nextEntry = travers(entry, edge, target);
|
||||
nextEntry.sort();
|
||||
queue.add(nextEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,13 +151,11 @@ public class Crawler<T> {
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.poll();
|
||||
LOG.trace("Check path head {}, edge {}, weight {}", entry.head, entry.edge, entry.weight);
|
||||
List<Edge<T>> head = entry.head;
|
||||
Vertex<T> source = entry.vertex;
|
||||
Edge<T> edge = entry.edge;
|
||||
LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
|
||||
Edge<T> edge = entry.getEdge();
|
||||
if (edge != null) {
|
||||
if (edge.isConnect(target)) {
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
List<Edge<T>> res = entry.toEdges();
|
||||
LOG.debug("Path found {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
@@ -156,128 +164,353 @@ public class Crawler<T> {
|
||||
if (edge.getTarget().isSingle()){
|
||||
continue;
|
||||
}
|
||||
head = getCopyList(entry.head, edge);
|
||||
}
|
||||
if (head.size() >= maxSize){
|
||||
if (entry.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
continue;
|
||||
}
|
||||
Iterator<Edge<T>> iterator = entry.iterator();
|
||||
//put only 2 entry for iterate
|
||||
while (iterator.hasNext()){
|
||||
edge = iterator.next();
|
||||
if (deep <= source.getLevel() && !edge.getTarget().isSingle() || edge.isConnect(target)) {
|
||||
boolean isTarget = edge.isConnect(target);
|
||||
boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel();
|
||||
if (canDeep || isTarget){
|
||||
LOG.trace("Add edge {} to queue", edge);
|
||||
queue.add(travers(entry, head, edge, target));
|
||||
queue.add(travers(entry, edge, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
//last edge don't compare
|
||||
private int ucs2(CostTraversalEntry root, T target, int deep, int count) {
|
||||
LOG.trace("UCS2 from {} to {}, deep {}, count {}", root.vertex, target, deep, count);
|
||||
int found = 0;
|
||||
PriorityQueue<CostTraversalEntry> queue = new PriorityQueue<>();
|
||||
queue.add(root);
|
||||
double limit = Double.MAX_VALUE;
|
||||
PriorityQueue<Double> limitQueue = new PriorityQueue<>();
|
||||
PriorityQueue<CTEntrySupport> queue = new PriorityQueue<>();
|
||||
root.sort();
|
||||
queue.add(new CTEntrySupport(root));
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.peek();
|
||||
List<Edge<T>> head = entry.edge != null ? getCopyList(entry.head, entry.edge) : entry.head;
|
||||
Vertex<T> source = entry.vertex;
|
||||
Iterator<Edge<T>> iterator = entry.iterator();
|
||||
LOG.trace("Check path head {}, weight {}", head, entry.weight);
|
||||
int i = 0;
|
||||
//put only 2 entry for iterate
|
||||
while (iterator.hasNext() && i < 2){
|
||||
Edge<T> edge = iterator.next();
|
||||
LOG.trace("Check edge {}", edge);
|
||||
if (edge.isConnect(target)) {
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
CTEntrySupport curr = queue.poll();
|
||||
CostTraversalEntry entry = curr.entry;
|
||||
LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
|
||||
if (entry.isConnect(target)) {
|
||||
List<Edge<T>> res = entry.toEdges();
|
||||
LOG.trace("Path found {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
}
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel() && head.size() < maxSize-1) {
|
||||
edge.getTarget().sortEdges();
|
||||
queue.add(travers(entry, head, edge, target));
|
||||
i++;
|
||||
limit = limitQueue.poll();
|
||||
}
|
||||
if (limitQueue.size() + found < count){
|
||||
LOG.trace("Continue search, limit {}", limit);
|
||||
} else {
|
||||
LOG.trace("Already {} found, extracting", limitQueue.size());
|
||||
continue;
|
||||
}
|
||||
if (!iterator.hasNext()) queue.poll();
|
||||
if (deep >= entry.getTarget().getLevel() || entry.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
continue;
|
||||
}
|
||||
DFS task = new DFS(curr, target, deep, count - found, limit);
|
||||
POOL.invoke(task);
|
||||
limitQueue.addAll(task.getLimits());
|
||||
queue.addAll(task.getResult());
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
protected class TraversalEntry {
|
||||
protected final List<Edge<T>> head;
|
||||
private class CTEntrySupport implements Comparable<CTEntrySupport>, Iterator<Edge<T>>{
|
||||
private final CTEntrySupport parent;
|
||||
private final Iterator<Edge<T>> iterator;
|
||||
private final CostTraversalEntry entry;
|
||||
private Edge<T> next;
|
||||
|
||||
private CTEntrySupport(CostTraversalEntry entry) {
|
||||
this(null, entry);
|
||||
}
|
||||
|
||||
private CTEntrySupport(CTEntrySupport parent, CostTraversalEntry entry) {
|
||||
this.parent = parent;
|
||||
this.iterator = entry.iterator();
|
||||
this.entry = entry;
|
||||
next = iterator.hasNext() ? next = iterator.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull CTEntrySupport o) {
|
||||
int cmp = entry.compareTo(o.entry);
|
||||
if (cmp != 0) return cmp;
|
||||
cmp = Integer.compare(entry.size(), o.entry.size());
|
||||
if (cmp != 0) return cmp;
|
||||
CostTraversal<T> cur = entry;
|
||||
CostTraversal<T> oCur = o.entry;
|
||||
while (!cur.isRoot()){
|
||||
Edge<T> edge = cur.getEdge();
|
||||
Edge<T> oEdge = oCur.getEdge();
|
||||
cmp = Double.compare(oEdge.weight, edge.weight);
|
||||
if (cmp != 0) return cmp;
|
||||
cur = (CostTraversal<T>) cur.getHead().get();
|
||||
oCur = (CostTraversal<T>) oCur.getHead().get();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Edge<T> next(){
|
||||
Edge<T> res = next;
|
||||
next = iterator.hasNext() ? next = iterator.next() : null;
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("{");
|
||||
sb.append("entry=").append(entry);
|
||||
sb.append(", next=").append(next);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private class DFS extends RecursiveAction {
|
||||
private final CTEntrySupport root;
|
||||
private final int count;
|
||||
private final int deep;
|
||||
private final T target;
|
||||
private final Collection<CTEntrySupport> res;
|
||||
private final Collection<Double> limits;
|
||||
private final ArrayList<DFS> subTasks;
|
||||
private final boolean isSubTask;
|
||||
private double limit;
|
||||
|
||||
private DFS(CTEntrySupport root, T target, int deep, int count, double limit) {
|
||||
this(root, target, deep, count, limit, false);
|
||||
}
|
||||
|
||||
private DFS(CTEntrySupport root, T target, int deep, int count, double limit, boolean subtask) {
|
||||
this.root = root;
|
||||
this.target = target;
|
||||
this.deep = deep;
|
||||
this.count = count;
|
||||
this.limit = limit;
|
||||
res = new ArrayList<>(count);
|
||||
limits = new ArrayList<>(count);
|
||||
subTasks = new ArrayList<>(THRESHOLD);
|
||||
isSubTask = subtask;
|
||||
}
|
||||
|
||||
private boolean isRoot(CTEntrySupport entry){
|
||||
return entry.parent == null || isSubTask && entry == root;
|
||||
}
|
||||
|
||||
private Collection<Double> getLimits() {
|
||||
if (!isDone())
|
||||
throw new IllegalStateException();
|
||||
return limits;
|
||||
}
|
||||
|
||||
private Collection<CTEntrySupport> getResult() {
|
||||
if (!isDone())
|
||||
throw new IllegalStateException();
|
||||
return res;
|
||||
}
|
||||
|
||||
private void search(){
|
||||
CTEntrySupport curr = root;
|
||||
LOG.trace("Start {}", root);
|
||||
while (curr.hasNext()){
|
||||
Edge<T> edge = curr.next();
|
||||
CostTraversalEntry entry = curr.entry;
|
||||
LOG.trace("Check edge {}, entry {}, weight {}", edge, entry, entry.weight);
|
||||
boolean isTarget = edge.isConnect(target);
|
||||
boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel() && entry.size() < maxSize-1;
|
||||
if (canDeep || isTarget){
|
||||
CostTraversalEntry nextEntry = travers(entry, edge, target);
|
||||
nextEntry.sort();
|
||||
curr = new CTEntrySupport(curr, nextEntry);
|
||||
if (isTarget){
|
||||
LOG.trace("Found, add entry {} to queue", nextEntry);
|
||||
res.add(curr);
|
||||
limits.add(limit);
|
||||
limit = nextEntry.getWeight();
|
||||
curr = curr.parent;
|
||||
} else {
|
||||
if (nextEntry.getWeight() >= limit && limits.size() > 0){
|
||||
if (limits.size() < count){
|
||||
LOG.trace("Not found, limit {}, add entry {} to queue", limit, nextEntry);
|
||||
res.add(curr);
|
||||
} else {
|
||||
LOG.trace("Not found, limit {}, don't add entry {} to queue", limit, nextEntry);
|
||||
}
|
||||
if (!isRoot(curr.parent)){
|
||||
curr = curr.parent.parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isRoot(curr) && maxSize-nextEntry.size() < SPLIT_SIZE){
|
||||
if (addSubTask(curr))
|
||||
curr = curr.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Is limit deep");
|
||||
}
|
||||
while (!curr.hasNext() && !isRoot(curr)){
|
||||
LOG.trace("Level complete, return to prev level");
|
||||
curr = curr.parent;
|
||||
}
|
||||
}
|
||||
LOG.trace("Done {}", root);
|
||||
joinSubTasks();
|
||||
}
|
||||
|
||||
private boolean addSubTask(CTEntrySupport entry){
|
||||
if (subTasks.size() < THRESHOLD){
|
||||
DFS subTask = new DFS(entry, target, deep, count, limit, true);
|
||||
subTask.fork();
|
||||
subTasks.add(subTask);
|
||||
return true;
|
||||
} else {
|
||||
joinSubTasks();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void joinSubTasks(){
|
||||
for (DFS subTask : subTasks) {
|
||||
subTask.join();
|
||||
fill(subTask);
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
|
||||
private void fill(DFS subTask){
|
||||
LOG.trace("Sub task is done");
|
||||
limits.addAll(subTask.getLimits());
|
||||
res.addAll(subTask.getResult());
|
||||
limit = Math.min(limit, subTask.limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
search();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected class TraversalEntry implements Traversal<T> {
|
||||
protected final Traversal<T> head;
|
||||
protected final Vertex<T> vertex;
|
||||
private Iterator<Edge<T>> iterator;
|
||||
private final Edge<T> edge;
|
||||
private List<Edge<T>> edges;
|
||||
private Integer size;
|
||||
|
||||
protected TraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
this.head = head;
|
||||
protected TraversalEntry(Vertex<T> vertex) {
|
||||
this.vertex = vertex;
|
||||
this.head = null;
|
||||
this.edge = null;
|
||||
edges = null;
|
||||
}
|
||||
|
||||
public List<Edge<T>> getHead() {
|
||||
return head;
|
||||
protected TraversalEntry(Traversal<T> head, Edge<T> edge) {
|
||||
this.head = head;
|
||||
this.vertex = edge.getTarget();
|
||||
this.edge = edge;
|
||||
edges = null;
|
||||
}
|
||||
|
||||
public Vertex<T> getVertex() {
|
||||
@Override
|
||||
public Vertex<T> getTarget() {
|
||||
return vertex;
|
||||
}
|
||||
|
||||
public Iterator<Edge<T>> iterator(){
|
||||
if (iterator == null){
|
||||
iterator = getIteratorInstance();
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
protected Iterator<Edge<T>> getIteratorInstance(){
|
||||
return vertex.getEdges().iterator();
|
||||
}
|
||||
|
||||
protected Iterable<Edge<T>> getEdges(){
|
||||
return this::iterator;
|
||||
}
|
||||
}
|
||||
|
||||
protected class CostTraversalEntry extends TraversalEntry implements Comparable<CostTraversalEntry>{
|
||||
private final Edge<T> edge;
|
||||
private final double cost;
|
||||
private Double weight;
|
||||
|
||||
protected CostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
super(head, vertex);
|
||||
this.edge = null;
|
||||
this.cost = 0;
|
||||
}
|
||||
|
||||
protected CostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost) {
|
||||
super(head, edge.getTarget());
|
||||
this.edge = edge;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
public double getWeight(){
|
||||
if (weight == null){
|
||||
weight = cost + (edge !=null ? edge.getWeight() : 0);
|
||||
}
|
||||
return weight;
|
||||
@Override
|
||||
public Optional<Traversal<T>> getHead() {
|
||||
return Optional.ofNullable(head);
|
||||
}
|
||||
|
||||
public Edge<T> getEdge() {
|
||||
return edge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<Edge<T>> getEdges(){
|
||||
if (edges == null){
|
||||
edges = collect(vertex.getEdges());
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterator<Edge<T>> iterator(){
|
||||
return getEdges().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sort(){
|
||||
getEdges().sort(Comparator.<Edge<T>>naturalOrder());
|
||||
}
|
||||
|
||||
protected List<Edge<T>> collect(Collection<Edge<T>> src){
|
||||
return new ArrayList<>(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if (size == null){
|
||||
size = Traversal.super.size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("{");
|
||||
sb.append("head=");
|
||||
if (!isRoot()){
|
||||
sb.append(getHead().get().toEdges());
|
||||
}
|
||||
sb.append(", edge=").append(edge);
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected class CostTraversalEntry extends TraversalEntry implements CostTraversal<T>, Comparable<CostTraversalEntry>{
|
||||
private Double weight;
|
||||
|
||||
protected CostTraversalEntry(Vertex<T> vertex) {
|
||||
super(vertex);
|
||||
}
|
||||
|
||||
protected CostTraversalEntry(CostTraversalEntry head, Edge<T> edge) {
|
||||
super(head, edge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWeight(){
|
||||
if (weight == null){
|
||||
Edge<T> edge = getEdge();
|
||||
Optional<Traversal<T>> head = getHead();
|
||||
weight = (head.isPresent() ? ((CostTraversalEntry)head.get()).getWeight() : 0) + (edge != null ? edge.getWeight() : 0);
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull CostTraversalEntry other) {
|
||||
int cmp = Double.compare(getWeight(), other.getWeight());
|
||||
if (cmp != 0) return cmp;
|
||||
return Integer.compare(head.size(), other.head.size());
|
||||
return Integer.compare(size(), other.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
core/src/main/java/ru/trader/analysis/graph/Traversal.java
Normal file
47
core/src/main/java/ru/trader/analysis/graph/Traversal.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public interface Traversal<T> {
|
||||
Vertex<T> getTarget();
|
||||
Optional<Traversal<T>> getHead();
|
||||
Edge<T> getEdge();
|
||||
List<Edge<T>> getEdges();
|
||||
Iterator<Edge<T>> iterator();
|
||||
void sort();
|
||||
|
||||
default boolean isConnect(T target){
|
||||
Edge<T> edge = getEdge();
|
||||
return edge != null && edge.isConnect(target);
|
||||
}
|
||||
|
||||
default boolean isRoot(){
|
||||
return !getHead().isPresent();
|
||||
}
|
||||
|
||||
default int size(){
|
||||
int s = 0;
|
||||
Optional<Traversal<T>> t = this.getHead();
|
||||
while (t.isPresent()){
|
||||
s++;
|
||||
t = t.get().getHead();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default List<Edge<T>> toEdges(){
|
||||
int s = size();
|
||||
Edge<T>[] res = new Edge[s];
|
||||
int i = s - 1;
|
||||
Traversal<T> entry = this;
|
||||
while (i >= 0){
|
||||
Edge<T> edge = entry.getEdge();
|
||||
res[i] = edge;
|
||||
if (i > 0)
|
||||
entry = entry.getHead().get();
|
||||
i--;
|
||||
}
|
||||
return Arrays.asList(res);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ public class RouteSearcherTest extends Assert{
|
||||
private Place lhs3262;
|
||||
private Place morgor;
|
||||
private Place lhs3006;
|
||||
private Place bd47;
|
||||
private Place aulin;
|
||||
private Place iBootis;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -31,6 +34,9 @@ public class RouteSearcherTest extends Assert{
|
||||
lhs3262 = world.get("LHS 3262");
|
||||
morgor = world.get("Morgor");
|
||||
lhs3006 = world.get("LHS 3006");
|
||||
bd47 = world.get("BD+47 2112");
|
||||
aulin = world.get("Aulin");
|
||||
iBootis = world.get("i Bootis");
|
||||
|
||||
MarketFilter filter = new MarketFilter();
|
||||
fWorld = new FilteredMarket(world, filter);
|
||||
@@ -45,6 +51,7 @@ public class RouteSearcherTest extends Assert{
|
||||
Vendor lhs3262_st = lhs3262.get().iterator().next();
|
||||
Vendor morgor_st = morgor.get().iterator().next();
|
||||
Vendor lhs3006_st = lhs3006.get().iterator().next();
|
||||
Vendor aulin_st = aulin.get().iterator().next();
|
||||
Ship ship = new Ship();
|
||||
ship.setCargo(440); ship.setTank(15);
|
||||
ship.setEngine(5, 'A'); ship.setMass(466);
|
||||
@@ -57,50 +64,22 @@ public class RouteSearcherTest extends Assert{
|
||||
RouteSearcher searcher = new RouteSearcher(scorer);
|
||||
|
||||
Route route = new Route(new RouteEntry(ithaca_st, false, 3.3789702637348586d, 0));
|
||||
route.add(new RouteEntry(morgor_st, false, 4.137765020523591d, 0));
|
||||
route.add(new RouteEntry(lhs3006_st, false, 4.0674474942172765d, 0));
|
||||
route.add(new RouteEntry(morgor.asTransit(), false, 4.137765020523591d, 0));
|
||||
route.add(new RouteEntry(lhs3006.asTransit(), false, 4.0674474942172765d, 0));
|
||||
route.add(new RouteEntry(lhs3262_st, true, 4.149937831634785d, 0));
|
||||
route.add(new RouteEntry(lhs3006_st, false, 4.1292528548103d, 0));
|
||||
route.add(new RouteEntry(morgor_st, false, 3.3050364899848566, 0));
|
||||
route.add(new RouteEntry(ithaca_st, false, 3.3483447506734136, 0));
|
||||
route.add(new RouteEntry(lhs3006.asTransit(), false, 4.1292528548103d, 0));
|
||||
route.add(new RouteEntry(morgor.asTransit(), false, 3.3050364899848566, 0));
|
||||
route.add(new RouteEntry(ithaca_st, false, 0, 0));
|
||||
RouteFiller filler = new RouteFiller(scorer);
|
||||
filler.fill(route);
|
||||
|
||||
assertEquals(route.getProfit(), 981200, 0);
|
||||
assertEquals(route.getLands(), 2);
|
||||
assertEquals(route.getDistance(), 72.42, 0.01);
|
||||
|
||||
List<Route> apaths = searcher.getRoutes(ithaca_st, ithaca_st, fWorld.getMarkets(true).collect(Collectors.toList()));
|
||||
Route actual = apaths.stream().findFirst().get();
|
||||
//assertTrue("Routes is different",expect.isRoute(actual));
|
||||
assertEquals("Routes is different", route, actual);
|
||||
|
||||
}
|
||||
/*
|
||||
@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().get().iterator().next();
|
||||
Vendor lhs3262 = market.get().stream().filter((v)->v.getName().equals("LHS 3262")).findFirst().get().get().iterator().next();
|
||||
|
||||
RouteSearcher searcher = new RouteSearcher(13.6, 40);
|
||||
RouteGraph graph = new RouteGraph(ithaca, market.getVendors(true), 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.getVendors(true), 6, 6000000, 440, 10);
|
||||
PathRoute actual = apaths.stream().findFirst().get();
|
||||
assertTrue("Routes is different",expect.isRoute(actual));
|
||||
|
||||
graph = new RouteGraph(lhs3262, market.getVendors(true), 40, 13.6, true, 6);
|
||||
graph.setCargo(440);
|
||||
graph.setBalance(6000000);
|
||||
|
||||
expect = graph.getPathsTo(lhs3262, 10).stream().map(p -> (PathRoute)p).findFirst().get();
|
||||
apaths = searcher.getPaths(lhs3262, lhs3262, market.getVendors(true), 6, 6000000, 440, 10);
|
||||
actual = apaths.stream().findFirst().get();
|
||||
assertEquals("Routes is different",expect.getAvgProfit(), actual.getAvgProfit(), 0.00001);
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ public class CrawlerTest extends Assert {
|
||||
PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x5, x4), PPath.of(x5, x4, x3, x4),
|
||||
PPath.of(x5, x4, x6, x4), PPath.of(x5, x6, x3, x4),
|
||||
PPath.of(x5, x3, x5, x4), PPath.of(x5, x4, x2, x4),
|
||||
PPath.of(x5, x3, x2, x4), PPath.of(x5, x3, x6, x4)
|
||||
PPath.of(x5, x3, x6, x4), PPath.of(x5, x3, x2, x4)
|
||||
);
|
||||
TestUtil.assertCollectionEquals(paths.getWeights(), 5.0, 15.0, 15.0,
|
||||
15.0, 15.0, 15.0,
|
||||
|
||||
Reference in New Issue
Block a user