modify crawler implementation
This commit is contained in:
155
core/src/main/java/ru/trader/analysis/graph/AbstractGraph.java
Normal file
155
core/src/main/java/ru/trader/analysis/graph/AbstractGraph.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
|
||||
public abstract class AbstractGraph<T> implements Graph<T> {
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = 4;
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(AbstractGraph.class);
|
||||
|
||||
protected Vertex<T> root;
|
||||
protected final Map<T, Vertex<T>> vertexes;
|
||||
private final GraphCallBack callback;
|
||||
|
||||
protected int minJumps;
|
||||
|
||||
protected AbstractGraph() {
|
||||
this(new AnalysisCallBack());
|
||||
}
|
||||
|
||||
protected AbstractGraph(AnalysisCallBack callback) {
|
||||
this.callback = new GraphCallBack(callback);
|
||||
vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD);
|
||||
}
|
||||
|
||||
protected abstract GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
|
||||
|
||||
public void build(T start, Collection<T> set, int maxDeep, double limit) {
|
||||
callback.startBuild(start);
|
||||
root = getInstance(start, maxDeep);
|
||||
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
||||
if (set.size() > vertexes.size()){
|
||||
minJumps = maxDeep;
|
||||
} else {
|
||||
minJumps = 1;
|
||||
for (Vertex<T> vertex : vertexes.values()) {
|
||||
int jumps = maxDeep - vertex.getLevel();
|
||||
if (jumps > minJumps) minJumps = jumps;
|
||||
}
|
||||
}
|
||||
callback.endBuild();
|
||||
}
|
||||
|
||||
private Vertex<T> getInstance(T entry, int deep){
|
||||
Vertex<T> vertex = vertexes.get(entry);
|
||||
if (vertex == null) {
|
||||
LOG.trace("Is new vertex");
|
||||
vertex = new Vertex<>(entry);
|
||||
vertex.setLevel(deep);
|
||||
vertexes.put(entry, vertex);
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(T entry){
|
||||
return vertexes.containsKey(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vertex<T> getVertex(T entry){
|
||||
return vertexes.get(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vertex<T> getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinJumps() {
|
||||
return minJumps;
|
||||
}
|
||||
|
||||
protected abstract class GraphBuilder extends RecursiveAction {
|
||||
protected final Vertex<T> vertex;
|
||||
protected final Collection<T> set;
|
||||
protected final int deep;
|
||||
protected final double limit;
|
||||
|
||||
protected GraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
this.vertex = vertex;
|
||||
this.set = set;
|
||||
this.deep = deep;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
protected abstract double onConnect(T entry);
|
||||
protected abstract Edge<T> createEdge(Vertex<T> target);
|
||||
protected GraphBuilder createSubTask(Vertex<T> vertex, Collection<T> set, int deep, double limit){
|
||||
return createGraphBuilder(vertex, set, deep, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
ArrayList<GraphBuilder> subTasks = new ArrayList<>(THRESHOLD);
|
||||
Iterator<T> iterator = set.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (callback.isCancel()) break;
|
||||
T entry = iterator.next();
|
||||
if (entry == vertex.getEntry()) continue;
|
||||
double nextLimit = onConnect(entry);
|
||||
if (nextLimit >= 0) {
|
||||
LOG.trace("Connect {} to {}", vertex, entry);
|
||||
Vertex<T> next = getInstance(entry, 0);
|
||||
vertex.connect(createEdge(next));
|
||||
// If level > deep when vertex already added on upper deep
|
||||
if (next.getLevel() < deep) {
|
||||
next.setLevel(vertex.getLevel() - 1);
|
||||
if (deep > 0) {
|
||||
//Recursive build
|
||||
GraphBuilder task = createSubTask(next, set, deep - 1, nextLimit);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Vertex {} is far away", entry);
|
||||
}
|
||||
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
|
||||
for (GraphBuilder subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
}
|
||||
if (!subTasks.isEmpty()){
|
||||
for (GraphBuilder subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
LOG.trace("End build graph from {} on deep {}", vertex, deep);
|
||||
}
|
||||
}
|
||||
}
|
||||
112
core/src/main/java/ru/trader/analysis/graph/CCrawler.java
Normal file
112
core/src/main/java/ru/trader/analysis/graph/CCrawler.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(CCrawler.class);
|
||||
|
||||
public CCrawler(ConnectibleGraph<T> graph, Consumer<List<Edge<T>>> onFoundFunc) {
|
||||
super(graph, onFoundFunc);
|
||||
}
|
||||
|
||||
private Ship getShip(){
|
||||
return ((ConnectibleGraph<T>)graph).getProfile().getShip();
|
||||
}
|
||||
|
||||
private Profile getProfile(){
|
||||
return ((ConnectibleGraph<T>)graph).getProfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Crawler<T>.TraversalEntry start(Vertex<T> vertex) {
|
||||
double fuel = getShip().getTank();
|
||||
return new CTraversalEntry(new ArrayList<>(), vertex, fuel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Crawler<T>.CostTraversalEntry costStart(Vertex<T> vertex) {
|
||||
double fuel = getShip().getTank();
|
||||
return new CCostTraversalEntry(new ArrayList<>(), vertex, fuel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Crawler<T>.TraversalEntry travers(TraversalEntry entry, Edge<T> edge) {
|
||||
T source = entry.vertex.getEntry();
|
||||
double distance = source.getDistance(edge.target.getEntry());
|
||||
double fuelCost = getShip().getFuelCost(((CTraversalEntry)entry).fuel, distance);
|
||||
double nextLimit = getProfile().withRefill() ? ((CTraversalEntry)entry).fuel - fuelCost : getShip().getTank();
|
||||
boolean refill = nextLimit < 0 && source.canRefill();
|
||||
if (refill) {
|
||||
LOG.trace("Refill");
|
||||
refill = true;
|
||||
nextLimit = getShip().getTank() - getShip().getFuelCost(distance);
|
||||
}
|
||||
edge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget(), refill, fuelCost);
|
||||
return new CTraversalEntry(getCopyList(entry.head, edge), edge.getTarget(), nextLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Crawler<T>.CostTraversalEntry costTravers(Crawler<T>.CostTraversalEntry entry, List<Edge<T>> head, Edge<T> edge) {
|
||||
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) {
|
||||
LOG.trace("Refill");
|
||||
refill = true;
|
||||
fuelCost = getShip().getFuelCost(distance);
|
||||
nextLimit = getShip().getTank() - fuelCost;
|
||||
}
|
||||
edge = new ConnectibleEdge<>(edge.getSource(), edge.getTarget(), refill, fuelCost);
|
||||
return new CCostTraversalEntry(head, edge, entry.getWeight(), nextLimit);
|
||||
}
|
||||
|
||||
protected class CTraversalEntry extends TraversalEntry {
|
||||
private final double fuel;
|
||||
|
||||
protected CTraversalEntry(List<Edge<T>> head, Vertex<T> vertex, double fuel) {
|
||||
super(head, vertex);
|
||||
this.fuel = fuel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<Edge<T>> getIteratorInstance() {
|
||||
return vertex.getEdges().stream().filter(e -> {
|
||||
ConnectibleEdge<T> edge = (ConnectibleEdge<T>) e;
|
||||
return edge.getFuel() <= fuel || e.getSource().getEntry().canRefill();
|
||||
}).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
protected class CCostTraversalEntry extends CostTraversalEntry {
|
||||
private final double fuel;
|
||||
|
||||
protected CCostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex, double fuel) {
|
||||
super(head, vertex);
|
||||
this.fuel = fuel;
|
||||
}
|
||||
|
||||
protected CCostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost, double fuel) {
|
||||
super(head, edge, cost);
|
||||
this.fuel = fuel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<Edge<T>> getIteratorInstance() {
|
||||
return vertex.getEdges().stream().filter(e -> {
|
||||
ConnectibleEdge<T> edge = (ConnectibleEdge<T>) e;
|
||||
return edge.getFuel() <= fuel || e.getSource().getEntry().canRefill();
|
||||
}).iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import ru.trader.graph.Connectable;
|
||||
|
||||
public class ConnectibleEdge<T extends Connectable<T>> extends Edge<T> {
|
||||
protected final boolean refill;
|
||||
protected final double fuel;
|
||||
|
||||
public ConnectibleEdge(Vertex<T> source, Vertex<T> target, boolean refill, double fuel) {
|
||||
super(source, target);
|
||||
this.refill = refill;
|
||||
this.fuel = fuel;
|
||||
}
|
||||
|
||||
public boolean isRefill() {
|
||||
return refill;
|
||||
}
|
||||
|
||||
public double getFuel() {
|
||||
return fuel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeWeight() {
|
||||
T s = source.getEntry();
|
||||
T t = target.getEntry();
|
||||
return s.getDistance(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return source.getEntry().toString() + " - "+ weight
|
||||
+ (refill ? "R" : "")
|
||||
+" -> " + target.getEntry().toString();
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ import ru.trader.graph.Connectable;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
|
||||
public class ConnectibleGraph<T extends Connectable<T>> extends AbstractGraph<T> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ConnectibleGraph.class);
|
||||
|
||||
private final Profile profile;
|
||||
protected final Profile profile;
|
||||
|
||||
public ConnectibleGraph(Profile profile) {
|
||||
super();
|
||||
@@ -24,6 +24,10 @@ public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public Profile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
return new ConnectibleGraphBuilder(vertex, set, deep, limit);
|
||||
@@ -48,11 +52,12 @@ public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectibleGraphBuilder extends GraphBuilder {
|
||||
protected class ConnectibleGraphBuilder extends GraphBuilder {
|
||||
private final DistanceFilter distanceFilter;
|
||||
protected boolean refill;
|
||||
protected double fuelCost;
|
||||
|
||||
private ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
protected ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
super(vertex, set, deep, limit);
|
||||
distanceFilter = new DistanceFilter(limit, vertex.getEntry());
|
||||
}
|
||||
@@ -64,12 +69,13 @@ public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
|
||||
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
||||
return -1;
|
||||
}
|
||||
double costFuel = profile.getShip().getFuelCost(limit, distance);
|
||||
double nextLimit = profile.withRefill() ? limit - costFuel : profile.getShip().getTank();
|
||||
fuelCost = profile.getShip().getFuelCost(limit, distance);
|
||||
double nextLimit = profile.withRefill() ? limit - fuelCost : profile.getShip().getTank();
|
||||
if (nextLimit < 0) {
|
||||
LOG.trace("Refill");
|
||||
refill = true;
|
||||
nextLimit = profile.getShip().getTank() - profile.getShip().getFuelCost(distance);
|
||||
fuelCost = profile.getShip().getFuelCost(distance);
|
||||
nextLimit = profile.getShip().getTank() - fuelCost;
|
||||
} else {
|
||||
refill = false;
|
||||
}
|
||||
@@ -77,35 +83,9 @@ public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectibleEdge createEdge(Vertex<T> target) {
|
||||
return new ConnectibleEdge(vertex, target, refill);
|
||||
protected ConnectibleEdge<T> createEdge(Vertex<T> target) {
|
||||
return new ConnectibleEdge<>(vertex, target, refill, fuelCost);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ConnectibleEdge extends Edge<T> {
|
||||
private final boolean refill;
|
||||
|
||||
protected ConnectibleEdge(Vertex<T> source, Vertex<T> target, boolean refill) {
|
||||
super(source, target);
|
||||
this.refill = refill;
|
||||
}
|
||||
|
||||
public boolean isRefill() {
|
||||
return refill;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeWeight() {
|
||||
T s = source.getEntry();
|
||||
T t = target.getEntry();
|
||||
return s.getDistance(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return source.getEntry().toString() + " - "+ weight
|
||||
+ (refill ? "R" : "")
|
||||
+" -> " + target.getEntry().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,21 +13,27 @@ public class Crawler<T> {
|
||||
private final static int THRESHOLD = 4;
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Crawler.class);
|
||||
|
||||
private final Graph<T> graph;
|
||||
protected final Graph<T> graph;
|
||||
private final Consumer<List<Edge<T>>> onFoundFunc;
|
||||
private int maxSize;
|
||||
|
||||
public Crawler(Graph<T> graph, Consumer<List<Edge<T>>> onFoundFunc) {
|
||||
this.graph = graph;
|
||||
maxSize = graph.getRoot().getLevel();
|
||||
this.onFoundFunc = onFoundFunc;
|
||||
}
|
||||
|
||||
private List<Edge<T>> getCopyList(List<Edge<T>> head, Edge<T> tail){
|
||||
protected List<Edge<T>> getCopyList(List<Edge<T>> head, Edge<T> tail){
|
||||
List<Edge<T>> res = new ArrayList<>(20);
|
||||
res.addAll(head);
|
||||
res.add(tail);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void setMaxSize(int maxSize) {
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
public void findFast(T target){
|
||||
findFast(target, 1);
|
||||
}
|
||||
@@ -37,9 +43,11 @@ public class Crawler<T> {
|
||||
int found = 0;
|
||||
if (t != null) {
|
||||
if (count > 1) {
|
||||
found = bfs(new ArrayList<>(), graph.root, target, 0, count);
|
||||
Vertex<T> s = graph.getRoot();
|
||||
s.sortEdges();
|
||||
found = bfs(start(s), target, 0, count);
|
||||
} else {
|
||||
found = dfs(new ArrayList<>(), graph.root, target, t.getLevel() + 1, count);
|
||||
found = dfs(start(graph.getRoot()), target, t.getLevel() + 1, count);
|
||||
}
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
@@ -53,31 +61,53 @@ public class Crawler<T> {
|
||||
Vertex<T> t = graph.getVertex(target);
|
||||
int found = 0;
|
||||
if (t != null) {
|
||||
found = ucs(new ArrayList<>(), graph.root, target, 0, count);
|
||||
found = ucs(costStart(graph.getRoot()), target, 0, count);
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
}
|
||||
|
||||
private int dfs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("DFS from {} to {}, deep {}, count {}, head {}", source, target, deep, count, head);
|
||||
protected TraversalEntry start(Vertex<T> vertex){
|
||||
return new TraversalEntry(new ArrayList<>(), vertex);
|
||||
}
|
||||
|
||||
protected CostTraversalEntry costStart(Vertex<T> vertex){
|
||||
return new CostTraversalEntry(new ArrayList<>(), vertex);
|
||||
}
|
||||
|
||||
protected TraversalEntry travers(TraversalEntry entry, Edge<T> edge){
|
||||
return new TraversalEntry(getCopyList(entry.head, edge), edge.getTarget());
|
||||
}
|
||||
|
||||
private CostTraversalEntry costTravers(CostTraversalEntry entry, Edge<T> edge){
|
||||
return costTravers(entry, getCopyList(entry.head, edge), edge);
|
||||
}
|
||||
|
||||
protected CostTraversalEntry costTravers(CostTraversalEntry entry, List<Edge<T>> head, Edge<T> edge){
|
||||
return new CostTraversalEntry(head, edge, entry.getWeight());
|
||||
}
|
||||
|
||||
private int dfs(TraversalEntry 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);
|
||||
if (deep == source.getLevel()){
|
||||
Optional<Edge<T>> last = source.getEdges().parallelStream()
|
||||
.filter(next -> next.isConnect(target))
|
||||
.findFirst();
|
||||
if (last.isPresent()){
|
||||
List<Edge<T>> res = getCopyList(head, last.get());
|
||||
for (Edge<T> next : entry.getEdges()) {
|
||||
if (next.isConnect(target)){
|
||||
List<Edge<T>> res = getCopyList(head, next);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found < count){
|
||||
if (deep < source.getLevel()) {
|
||||
if (deep < source.getLevel() && head.size() < maxSize-1) {
|
||||
LOG.trace("Search around");
|
||||
for (Edge<T> edge : source.getEdges()) {
|
||||
for (Edge<T> edge : entry.getEdges()) {
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
found += dfs(getCopyList(head, edge), edge.getTarget(), target, deep, count-found);
|
||||
found += dfs(travers(entry, edge), target, deep, count-found);
|
||||
if (found >= count) break;
|
||||
}
|
||||
}
|
||||
@@ -85,18 +115,23 @@ public class Crawler<T> {
|
||||
return found;
|
||||
}
|
||||
|
||||
private int bfs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("BFS from {} to {}, deep {}, count {}", source, target, deep, count);
|
||||
private int bfs(TraversalEntry root, T target, int deep, int count) {
|
||||
LOG.trace("BFS from {} to {}, deep {}, count {}", root.vertex, target, deep, count);
|
||||
int found = 0;
|
||||
LinkedList<TraversalEntry> queue = new LinkedList<>();
|
||||
queue.add(new TraversalEntry(head, source));
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
TraversalEntry entry = queue.poll();
|
||||
head = entry.head;
|
||||
source = entry.vertex;
|
||||
List<Edge<T>> head = entry.head;
|
||||
Vertex<T> source = entry.vertex;
|
||||
if (head.size() >= maxSize){
|
||||
LOG.trace("Is limit deep");
|
||||
continue;
|
||||
}
|
||||
LOG.trace("Search from {} to {}, head {}", source, target, head);
|
||||
source.sortEdges();
|
||||
for (Edge<T> edge : source.getEdges()) {
|
||||
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);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
@@ -106,25 +141,26 @@ public class Crawler<T> {
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel()) {
|
||||
queue.add(new TraversalEntry(getCopyList(head, edge), edge.getTarget()));
|
||||
edge.getTarget().sortEdges();
|
||||
queue.add(travers(entry, edge));
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private int ucs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("UCS from {} to {}, deep {}, count {}", source, target, deep, count);
|
||||
private int ucs(CostTraversalEntry root, T target, int deep, int count) {
|
||||
LOG.trace("UCS from {} to {}, deep {}, count {}", root.vertex, target, deep, count);
|
||||
int found = 0;
|
||||
PriorityQueue<CostTraversalEntry> queue = new PriorityQueue<>();
|
||||
queue.add(new CostTraversalEntry(head, source));
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.poll();
|
||||
LOG.trace("Check path head {}, edge {}, cost {}", entry.head, entry.edge, entry.cost);
|
||||
head = entry.head;
|
||||
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;
|
||||
if (edge != null) {
|
||||
source = edge.getSource();
|
||||
if (edge.isConnect(target)) {
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
LOG.debug("Path found {}", res);
|
||||
@@ -137,13 +173,17 @@ public class Crawler<T> {
|
||||
}
|
||||
head = getCopyList(entry.head, edge);
|
||||
}
|
||||
Iterator<Edge<T>> iterator = entry.iterator;
|
||||
if (head.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)) {
|
||||
LOG.trace("Add edge {} to queue", edge);
|
||||
queue.add(new CostTraversalEntry(head, edge, entry.cost));
|
||||
queue.add(costTravers(entry, head, edge));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,17 +191,17 @@ public class Crawler<T> {
|
||||
}
|
||||
|
||||
//last edge don't compare
|
||||
private int ucs2(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("UCS2 from {} to {}, deep {}, count {}", source, target, deep, count);
|
||||
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<>();
|
||||
source.sortEdges();
|
||||
queue.add(new CostTraversalEntry(head, source));
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.peek();
|
||||
head = entry.edge != null ? getCopyList(entry.head, entry.edge) : entry.head;
|
||||
Iterator<Edge<T>> iterator = entry.iterator;
|
||||
LOG.trace("Check path head {}, cost {}", head, entry.cost);
|
||||
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){
|
||||
@@ -175,9 +215,9 @@ public class Crawler<T> {
|
||||
}
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel()) {
|
||||
if (deep < source.getLevel() && head.size() < maxSize-1) {
|
||||
edge.getTarget().sortEdges();
|
||||
queue.add(new CostTraversalEntry(head, edge, entry.cost));
|
||||
queue.add(costTravers(entry, head, edge));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -186,39 +226,59 @@ public class Crawler<T> {
|
||||
return found;
|
||||
}
|
||||
|
||||
private class TraversalEntry {
|
||||
private final List<Edge<T>> head;
|
||||
private final Vertex<T> vertex;
|
||||
protected class TraversalEntry {
|
||||
protected final List<Edge<T>> head;
|
||||
protected final Vertex<T> vertex;
|
||||
private Iterator<Edge<T>> iterator;
|
||||
|
||||
private TraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
protected TraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
this.head = head;
|
||||
this.vertex = vertex;
|
||||
}
|
||||
|
||||
public Iterator<Edge<T>> iterator(){
|
||||
if (iterator == null){
|
||||
iterator = getIteratorInstance();
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
private class CostTraversalEntry implements Comparable<CostTraversalEntry>{
|
||||
private final List<Edge<T>> head;
|
||||
private final Edge<T> edge;
|
||||
private final Iterator<Edge<T>> iterator;
|
||||
private final double cost;
|
||||
protected Iterator<Edge<T>> getIteratorInstance(){
|
||||
return vertex.getEdges().iterator();
|
||||
}
|
||||
|
||||
private CostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
this.head = head;
|
||||
this.iterator = vertex.getEdges().iterator();
|
||||
private 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;
|
||||
}
|
||||
|
||||
private CostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost) {
|
||||
this.head = head;
|
||||
protected CostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost) {
|
||||
super(head, edge.getTarget());
|
||||
this.edge = edge;
|
||||
this.iterator = edge.getTarget().getEdges().iterator();
|
||||
this.cost = cost + edge.getWeight();
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
protected double getWeight(){
|
||||
if (weight == null){
|
||||
weight = cost + (edge !=null ? edge.getWeight() : 0);
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull CostTraversalEntry other) {
|
||||
int cmp = Double.compare(cost, other.cost);
|
||||
int cmp = Double.compare(getWeight(), other.getWeight());
|
||||
if (cmp != 0) return cmp;
|
||||
return Integer.compare(head.size(), other.head.size());
|
||||
}
|
||||
|
||||
@@ -1,144 +1,11 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
public interface Graph<T> {
|
||||
boolean isAccessible(T entry);
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
Vertex<T> getVertex(T entry);
|
||||
|
||||
public abstract class Graph<T> {
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = 4;
|
||||
Vertex<T> getRoot();
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Graph.class);
|
||||
|
||||
protected Vertex<T> root;
|
||||
protected final Map<T, Vertex<T>> vertexes;
|
||||
private final GraphCallBack callback;
|
||||
|
||||
protected int minJumps;
|
||||
|
||||
protected Graph() {
|
||||
this(new AnalysisCallBack());
|
||||
}
|
||||
|
||||
protected Graph(AnalysisCallBack callback) {
|
||||
this.callback = new GraphCallBack(callback);
|
||||
vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD);
|
||||
}
|
||||
|
||||
protected abstract GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
|
||||
|
||||
public void build(T start, Collection<T> set, int maxDeep, double limit) {
|
||||
callback.startBuild(start);
|
||||
root = getInstance(start, maxDeep);
|
||||
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
||||
if (set.size() > vertexes.size()){
|
||||
minJumps = maxDeep;
|
||||
} else {
|
||||
minJumps = 1;
|
||||
for (Vertex<T> vertex : vertexes.values()) {
|
||||
int jumps = maxDeep - vertex.getLevel();
|
||||
if (jumps > minJumps) minJumps = jumps;
|
||||
}
|
||||
}
|
||||
callback.endBuild();
|
||||
}
|
||||
|
||||
private Vertex<T> getInstance(T entry, int deep){
|
||||
Vertex<T> vertex = new Vertex<>(entry);
|
||||
vertex.setLevel(deep);
|
||||
Vertex<T> old = vertexes.get(entry);
|
||||
if (old == null || old.getLevel() < deep) {
|
||||
LOG.trace("Is top vertex");
|
||||
vertexes.put(entry, vertex);
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
|
||||
public boolean isAccessible(T entry){
|
||||
return vertexes.containsKey(entry);
|
||||
}
|
||||
|
||||
public Vertex<T> getVertex(T entry){
|
||||
return vertexes.get(entry);
|
||||
}
|
||||
|
||||
public T getRoot() {
|
||||
return root.getEntry();
|
||||
}
|
||||
|
||||
public int getMinJumps() {
|
||||
return minJumps;
|
||||
}
|
||||
|
||||
protected abstract class GraphBuilder extends RecursiveAction {
|
||||
protected final Vertex<T> vertex;
|
||||
protected final Collection<T> set;
|
||||
protected final int deep;
|
||||
protected final double limit;
|
||||
|
||||
protected GraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
this.vertex = vertex;
|
||||
this.set = set;
|
||||
this.deep = deep;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
protected abstract double onConnect(T entry);
|
||||
protected abstract Edge<T> createEdge(Vertex<T> target);
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
ArrayList<GraphBuilder> subTasks = new ArrayList<>(THRESHOLD);
|
||||
Iterator<T> iterator = set.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (callback.isCancel()) break;
|
||||
T entry = iterator.next();
|
||||
if (entry == vertex.getEntry()) continue;
|
||||
double nextLimit = onConnect(entry);
|
||||
if (nextLimit >= 0) {
|
||||
LOG.trace("Connect {} to {}", vertex, entry);
|
||||
Vertex<T> next = getInstance(entry, vertex.getLevel() - 1);
|
||||
vertex.connect(createEdge(next));
|
||||
if (deep > 0) {
|
||||
//Recursive build
|
||||
GraphBuilder task = createGraphBuilder(next, set, deep - 1, nextLimit);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Vertex {} is far away", entry);
|
||||
}
|
||||
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
|
||||
for (GraphBuilder subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
}
|
||||
if (!subTasks.isEmpty()){
|
||||
for (GraphBuilder subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
LOG.trace("End build graph from {} on deep {}", vertex, deep);
|
||||
}
|
||||
}
|
||||
int getMinJumps();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class Vertex<T> {
|
||||
return this.entry.equals(entry);
|
||||
}
|
||||
|
||||
void setLevel(int level) {
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,21 @@ public class Profile {
|
||||
private int jumps;
|
||||
private Ship ship;
|
||||
private boolean refill;
|
||||
//Scorer multipliers
|
||||
private int scoreOrdersCount;
|
||||
private double distanceMult;
|
||||
private double jumpMult;
|
||||
private double landMult;
|
||||
private double fuelPrice;
|
||||
|
||||
public Profile(Ship ship) {
|
||||
this.ship = ship;
|
||||
refill = true;
|
||||
scoreOrdersCount = 5;
|
||||
distanceMult = 1;
|
||||
landMult = 1;
|
||||
fuelPrice = 1;
|
||||
jumpMult = 0.01;
|
||||
}
|
||||
|
||||
public double getBalance() {
|
||||
@@ -43,4 +54,44 @@ public class Profile {
|
||||
public void setRefill(boolean refill) {
|
||||
this.refill = refill;
|
||||
}
|
||||
|
||||
public int getScoreOrdersCount() {
|
||||
return scoreOrdersCount;
|
||||
}
|
||||
|
||||
public void setScoreOrdersCount(int scoreOrdersCount) {
|
||||
this.scoreOrdersCount = scoreOrdersCount;
|
||||
}
|
||||
|
||||
public double getDistanceMult() {
|
||||
return distanceMult;
|
||||
}
|
||||
|
||||
public void setDistanceMult(double distanceMult) {
|
||||
this.distanceMult = distanceMult;
|
||||
}
|
||||
|
||||
public double getJumpMult() {
|
||||
return jumpMult;
|
||||
}
|
||||
|
||||
public void setJumpMult(double jumpMult) {
|
||||
this.jumpMult = jumpMult;
|
||||
}
|
||||
|
||||
public double getLandMult() {
|
||||
return landMult;
|
||||
}
|
||||
|
||||
public void setLandMult(double landMult) {
|
||||
this.landMult = landMult;
|
||||
}
|
||||
|
||||
public double getFuelPrice() {
|
||||
return fuelPrice;
|
||||
}
|
||||
|
||||
public void setFuelPrice(double fuelPrice) {
|
||||
this.fuelPrice = fuelPrice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CrawlerTest extends Assert {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(CrawlerTest.class);
|
||||
@@ -89,7 +90,7 @@ public class CrawlerTest extends Assert {
|
||||
// x5 <-> x4, x5 <-> x6
|
||||
|
||||
SimpleCollector paths = new SimpleCollector();
|
||||
Crawler<Point> crawler = new Crawler<>(graph, paths::add);
|
||||
Crawler<Point> crawler = new CCrawler<>(graph, paths::add);
|
||||
crawler.findMin(x4, 10);
|
||||
assertPaths(paths.get(), PPath.of(x5, x4));
|
||||
paths.clear();
|
||||
@@ -118,7 +119,7 @@ public class CrawlerTest extends Assert {
|
||||
// x5 <-> x4 <-> x3 <-> x2, x5 <-> x6 <-> x7 <-> x8
|
||||
// x5 <-> x3, x4 <-> x2, x3 <-> x6, x4 <-> x6
|
||||
SimpleCollector paths = new SimpleCollector();
|
||||
Crawler<Point> crawler = new Crawler<>(graph, paths::add);
|
||||
Crawler<Point> crawler = new CCrawler<>(graph, paths::add);
|
||||
|
||||
crawler.findMin(x8, 10);
|
||||
assertPaths(paths.get(), PPath.of(x5, x6, x7, x8));
|
||||
@@ -135,11 +136,16 @@ public class CrawlerTest extends Assert {
|
||||
|
||||
crawler.findMin(x4, 20);
|
||||
assertPaths(true, paths.get(), PPath.of(x5, x4), PPath.of(x5, x3, x4), PPath.of(x5, x6, x4),
|
||||
PPath.of(x5, x6, x5, x4), PPath.of(x5, x4, x3, x4), PPath.of(x5, x4, x5, x4),
|
||||
PPath.of(x5, x6, x3, x4), PPath.of(x5, x4, x6, x4),
|
||||
PPath.of(x5, x3, x5, x4), PPath.of(x5, x3, x2, x4),
|
||||
PPath.of(x5, x4, x2, x4), PPath.of(x5, x3, x6, x4)
|
||||
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)
|
||||
);
|
||||
TestUtil.assertCollectionEquals(paths.getWeights(), 5.0, 15.0, 15.0,
|
||||
15.0, 15.0, 15.0,
|
||||
25.0, 25.0,
|
||||
25.0, 35.0,
|
||||
35.0, 35.0);
|
||||
paths.clear();
|
||||
|
||||
crawler.findMin(x5, 20);
|
||||
@@ -177,7 +183,7 @@ public class CrawlerTest extends Assert {
|
||||
// x5 <-> x4 <- refill -> x3 <- refill -> x2, x5 <-> x6
|
||||
// x5 <-> x3 <- refill -> x2, x5 <-> x4 <- refill -> x6
|
||||
SimpleCollector paths = new SimpleCollector();
|
||||
Crawler<Point> crawler = new Crawler<>(graph, paths::add);
|
||||
Crawler<Point> crawler = new CCrawler<>(graph, paths::add);
|
||||
|
||||
crawler.findMin(x1, 10);
|
||||
assertTrue(paths.get().isEmpty());
|
||||
@@ -188,10 +194,14 @@ public class CrawlerTest extends Assert {
|
||||
paths.clear();
|
||||
|
||||
crawler.findMin(x6, 10);
|
||||
assertPaths(true, paths.get(), PPath.of(x5, x6), PPath.of(x5, x4, x6),
|
||||
PPath.of(x5, x6, x5, x6), PPath.of(x5, x4, x5, x6),
|
||||
PPath.of(x5, x3, x5, x6), PPath.of(x5, x3, x4, x6),
|
||||
assertPaths(paths.get(), PPath.of(x5, x6), PPath.of(x5, x4, x6),
|
||||
PPath.of(x5, x4, x5, x6), PPath.of(x5, x6, x5, x6),
|
||||
PPath.of(x5, x3, x4, x6), PPath.of(x5, x3, x5, x6),
|
||||
PPath.of(x5, x6, x4, x6));
|
||||
TestUtil.assertCollectionEquals(paths.getWeights(), 5.0, 15.0,
|
||||
15.0, 15.0,
|
||||
25.0, 25.0,
|
||||
25.0);
|
||||
paths.clear();
|
||||
|
||||
crawler.findFast(x2);
|
||||
@@ -216,7 +226,7 @@ public class CrawlerTest extends Assert {
|
||||
// x5 <-> x3 <- refill -> x2
|
||||
// x5 <-> x4 <- refill -> x6
|
||||
SimpleCollector paths = new SimpleCollector();
|
||||
Crawler<Point> crawler = new Crawler<>(graph, paths::add);
|
||||
Crawler<Point> crawler = new CCrawler<>(graph, paths::add);
|
||||
|
||||
crawler.findMin(x1, 10);
|
||||
assertTrue(paths.get().isEmpty());
|
||||
@@ -269,13 +279,22 @@ public class CrawlerTest extends Assert {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public List<Edge<Point>> get(int indx) {
|
||||
if (indx >= paths.size()) return Collections.emptyList();
|
||||
return paths.get(indx);
|
||||
public List<Edge<Point>> get(int index) {
|
||||
if (index >= paths.size()) return Collections.emptyList();
|
||||
return paths.get(index);
|
||||
}
|
||||
public void clear(){
|
||||
paths.clear();
|
||||
}
|
||||
|
||||
public double getWeight(int index){
|
||||
if (index >= paths.size()) return 0;
|
||||
return paths.get(index).stream().mapToDouble(Edge::getWeight).sum();
|
||||
}
|
||||
|
||||
public Collection<Double> getWeights(){
|
||||
return paths.stream().map(p -> p.stream().mapToDouble(Edge::getWeight).sum()).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user