Archived
0

modify crawler implementation

This commit is contained in:
iMoHax
2015-05-20 16:04:55 +03:00
parent 0fd99b0ac2
commit eaee1b190d
9 changed files with 527 additions and 247 deletions

View 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);
}
}
}

View 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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
LOG.debug("Last edge find, path {}", res);
onFoundFunc.accept(res);
found++;
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;
}
protected Iterator<Edge<T>> getIteratorInstance(){
return vertex.getEdges().iterator();
}
private Iterable<Edge<T>> getEdges(){
return this::iterator;
}
}
private class CostTraversalEntry implements Comparable<CostTraversalEntry>{
private final List<Edge<T>> head;
protected class CostTraversalEntry extends TraversalEntry implements Comparable<CostTraversalEntry>{
private final Edge<T> edge;
private final Iterator<Edge<T>> iterator;
private final double cost;
private Double weight;
private CostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
this.head = head;
this.iterator = vertex.getEdges().iterator();
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());
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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());
@@ -227,7 +237,7 @@ public class CrawlerTest extends Assert {
PPath.of(x5, x4, x3, x2), PPath.of(x5, x4, x2), PPath.of(x5, x3, x5, x4, x2),
PPath.of(x5, x6, x4, x2), PPath.of(x5, x6, x4, x3, x2), PPath.of(x5, x4, x3, x4, x2),
PPath.of(x5, x4, x5, x4, x2), PPath.of(x5, x6, x5, x4, x2), PPath.of(x5, x3, x4, x3, x2),
PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2));
PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2), PPath.of(x5, x3, x4, x3, x2));
paths.clear();
crawler.findMin(x6, 30);
@@ -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());
}
}
}