improve loop search result
This commit is contained in:
@@ -1,34 +0,0 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
|
||||
import ru.trader.analysis.graph.Edge;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class AbstractCrawlerSpecification implements CrawlerSpecification {
|
||||
private final RouteSpecification<Vendor> routeSpecification;
|
||||
private final Consumer<List<Edge<Vendor>>> onFoundFunc;
|
||||
private final boolean loop;
|
||||
|
||||
protected AbstractCrawlerSpecification(RouteSpecification<Vendor> routeSpecification, Consumer<List<Edge<Vendor>>> onFoundFunc, boolean loop) {
|
||||
this.routeSpecification = routeSpecification;
|
||||
this.onFoundFunc = onFoundFunc;
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
protected boolean isLoop() {
|
||||
return loop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouteSpecification<Vendor> routeSpecification() {
|
||||
return routeSpecification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<List<Edge<Vendor>>> onFoundFunc() {
|
||||
return onFoundFunc;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
import ru.trader.analysis.graph.Edge;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface CrawlerSpecification {
|
||||
|
||||
public double computeWeight(VendorsCrawler.VendorsEdge edge);
|
||||
|
||||
public double computeWeight(VendorsCrawler.VendorsTraversalEntry entry);
|
||||
|
||||
public RouteSpecification<Vendor> routeSpecification();
|
||||
|
||||
public Consumer<List<Edge<Vendor>>> onFoundFunc();
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class CrawlerSpecificationByProfit extends AbstractCrawlerSpecification {
|
||||
public class CrawlerSpecificationByProfit extends SimpleCrawlerSpecification<Vendor> implements VendorsCrawlerSpecification {
|
||||
|
||||
public CrawlerSpecificationByProfit(Consumer<List<Edge<Vendor>>> onFoundFunc) {
|
||||
super(null, onFoundFunc, false);
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class CrawlerSpecificationByTime extends AbstractCrawlerSpecification {
|
||||
public class CrawlerSpecificationByTime extends SimpleCrawlerSpecification<Vendor> implements VendorsCrawlerSpecification {
|
||||
public CrawlerSpecificationByTime(Consumer<List<Edge<Vendor>>> onFoundFunc) {
|
||||
super(null, onFoundFunc, false);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public class CrawlerSpecificator {
|
||||
private final List<Vendor> containsAny;
|
||||
private final List<Vendor> all;
|
||||
private final Collection<Offer> offers;
|
||||
private int groupCount;
|
||||
private boolean byTime;
|
||||
|
||||
public CrawlerSpecificator() {
|
||||
@@ -64,7 +65,11 @@ public class CrawlerSpecificator {
|
||||
this.offers.addAll(offers);
|
||||
}
|
||||
|
||||
public CrawlerSpecification build(Consumer<List<Edge<Vendor>>> onFoundFunc, RouteSpecification<Vendor> andSpec, boolean loop){
|
||||
public void setGroupCount(int groupCount) {
|
||||
this.groupCount = groupCount;
|
||||
}
|
||||
|
||||
public VendorsCrawlerSpecification build(Consumer<List<Edge<Vendor>>> onFoundFunc, RouteSpecification<Vendor> andSpec, boolean loop){
|
||||
RouteSpecification<Vendor> spec;
|
||||
RouteSpecification<Vendor> res = null;
|
||||
if (!all.isEmpty()){
|
||||
@@ -94,13 +99,17 @@ public class CrawlerSpecificator {
|
||||
res = andSpec;
|
||||
}
|
||||
}
|
||||
SimpleCrawlerSpecification crawlerSpecification;
|
||||
if (byTime){
|
||||
return new CrawlerSpecificationByTime(res, onFoundFunc, loop);
|
||||
crawlerSpecification = new CrawlerSpecificationByTime(res, onFoundFunc, loop);
|
||||
} else {
|
||||
crawlerSpecification = new CrawlerSpecificationByProfit(res, onFoundFunc, loop);
|
||||
}
|
||||
return new CrawlerSpecificationByProfit(res, onFoundFunc, loop);
|
||||
crawlerSpecification.setGroupCount(groupCount);
|
||||
return (VendorsCrawlerSpecification) crawlerSpecification;
|
||||
}
|
||||
|
||||
public CrawlerSpecification build(Consumer<List<Edge<Vendor>>> onFoundFunc){
|
||||
public VendorsCrawlerSpecification build(Consumer<List<Edge<Vendor>>> onFoundFunc){
|
||||
return build(onFoundFunc, null, false);
|
||||
}
|
||||
|
||||
|
||||
310
core/src/main/java/ru/trader/analysis/GroupLimitedQueue.java
Normal file
310
core/src/main/java/ru/trader/analysis/GroupLimitedQueue.java
Normal file
@@ -0,0 +1,310 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class GroupLimitedQueue<E> implements Queue<E> {
|
||||
private final Comparator<? super E> comparator;
|
||||
private final Function<E, Object> groupGetter;
|
||||
private final ArrayList<QueueWrapper> queues;
|
||||
private final int limit;
|
||||
private boolean sorted = false;
|
||||
|
||||
public GroupLimitedQueue(int limit, Function<E, Object> groupGetter) {
|
||||
this(limit, null, groupGetter);
|
||||
}
|
||||
|
||||
public GroupLimitedQueue(int limit, Comparator<? super E> comparator, Function<E, Object> groupGetter) {
|
||||
this.limit = limit;
|
||||
this.comparator = comparator;
|
||||
this.groupGetter = groupGetter;
|
||||
queues = new ArrayList<>(10);
|
||||
}
|
||||
|
||||
private LimitedQueue<E> getGroup(Object group, boolean add){
|
||||
for (QueueWrapper q : queues) {
|
||||
if (q.group.equals(group)){
|
||||
return q.queue;
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
LimitedQueue<E> queue = new LimitedQueue<>(limit, comparator);
|
||||
queues.add(new QueueWrapper(group, queue));
|
||||
sorted = false;
|
||||
return queue;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private LimitedQueue<E> getGroup(int groupIndex){
|
||||
return queues.get(groupIndex).queue;
|
||||
}
|
||||
|
||||
private boolean add(Object group, E element){
|
||||
LimitedQueue<E> queue = getGroup(group, true);
|
||||
boolean res = queue.add(element);
|
||||
if (res) sorted = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean remove(Object group, E element){
|
||||
LimitedQueue<E> queue = getGroup(group, false);
|
||||
if (queue == null) return false;
|
||||
boolean res = queue.remove(element);
|
||||
if (res) sorted = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
private E remove(int groupIndex, int elementIndex){
|
||||
LimitedQueue<E> queue = getGroup(groupIndex);
|
||||
sorted = false;
|
||||
return queue.remove(elementIndex);
|
||||
}
|
||||
|
||||
private E get(int groupIndex, int elementIndex){
|
||||
LimitedQueue<E> queue = getGroup(groupIndex);
|
||||
return queue.get(elementIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E element) {
|
||||
return add(groupGetter.apply(element), element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E element) {
|
||||
return add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove() {
|
||||
sort();
|
||||
return remove(0, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean remove(Object element) {
|
||||
if (element == null){
|
||||
boolean res = false;
|
||||
for (QueueWrapper q : queues) {
|
||||
res = q.queue.remove(element) || res;
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
E e = (E) element;
|
||||
LimitedQueue<E> queue = getGroup(groupGetter.apply(e), false);
|
||||
return queue != null && remove(queue, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll() {
|
||||
if (isEmpty()) return null;
|
||||
sort();
|
||||
return remove(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E element() {
|
||||
sort();
|
||||
return get(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peek() {
|
||||
if (isEmpty()) return null;
|
||||
sort();
|
||||
return get(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (QueueWrapper q : queues) {
|
||||
size += q.queue.size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
if (o == null){
|
||||
for (QueueWrapper q : queues) {
|
||||
if (q.queue.contains(o)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LimitedQueue<E> queue = getGroup(groupGetter.apply((E) o), false);
|
||||
return queue != null && queue.contains(o);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
private final Iterator<QueueWrapper> qIter = queues.iterator();
|
||||
private Iterator<E> iterator;
|
||||
private E next;
|
||||
{
|
||||
nextQueue();
|
||||
}
|
||||
|
||||
private void nextQueue(){
|
||||
if (qIter.hasNext()) {
|
||||
iterator = qIter.next().queue.iterator();
|
||||
nextEntry();
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void nextEntry(){
|
||||
if (iterator.hasNext()){
|
||||
next = iterator.next();
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
E res = next;
|
||||
if (iterator.hasNext()){
|
||||
nextEntry();
|
||||
} else {
|
||||
nextQueue();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
Object[] r = new Object[size()];
|
||||
int index = 0;
|
||||
for (QueueWrapper q : queues) {
|
||||
Object[] a = q.queue.toArray();
|
||||
int s = a.length;
|
||||
System.arraycopy(a, 0, r, index, s);
|
||||
index += s;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T[] toArray(@NotNull T[] a) {
|
||||
int size = size();
|
||||
T[] r = a.length >= size ? a : (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
|
||||
int index = 0;
|
||||
for (QueueWrapper q : queues) {
|
||||
T[] qa = (T[]) q.queue.toArray();
|
||||
int s = qa.length;
|
||||
System.arraycopy(qa, 0, r, index, s);
|
||||
index += s;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> c) {
|
||||
for (Object element : c) {
|
||||
if (!contains(element)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends E> c) {
|
||||
boolean modified = false;
|
||||
if (c instanceof GroupLimitedQueue){
|
||||
GroupLimitedQueue<E> groupQueue = (GroupLimitedQueue<E>) c;
|
||||
for (QueueWrapper q : groupQueue.queues) {
|
||||
LimitedQueue<E> queue = getGroup(q.group, true);
|
||||
modified = queue.addAll(q.queue) || modified;
|
||||
}
|
||||
sorted = sorted && !modified;
|
||||
} else {
|
||||
for (E element : c) {
|
||||
modified = modified || add(element);
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
boolean modified = false;
|
||||
for (QueueWrapper q : queues) {
|
||||
modified = q.queue.removeAll(c) || modified;
|
||||
}
|
||||
sorted = sorted && !modified;
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
boolean modified = false;
|
||||
for (QueueWrapper q : queues) {
|
||||
modified = q.queue.retainAll(c) || modified;
|
||||
}
|
||||
sorted = sorted && !modified;
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
sorted = false;
|
||||
queues.clear();
|
||||
}
|
||||
|
||||
public void sort(){
|
||||
if (sorted || comparator == null) return;
|
||||
for (QueueWrapper q : queues) {
|
||||
q.queue.sort();
|
||||
}
|
||||
queues.sort((q1, q2) -> {
|
||||
E e1 = q1.queue.peek();
|
||||
E e2 = q2.queue.peek();
|
||||
if (e1 == null || e2 == null){
|
||||
return e1 == e2 ? 0 : e1 == null ? 1 : -1;
|
||||
}
|
||||
return comparator.compare(e1, e2);
|
||||
});
|
||||
sorted = true;
|
||||
}
|
||||
|
||||
private class QueueWrapper {
|
||||
private final Object group;
|
||||
private final LimitedQueue<E> queue;
|
||||
|
||||
private QueueWrapper(Object group, LimitedQueue<E> queue) {
|
||||
this.group = group;
|
||||
this.queue = queue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import ru.trader.analysis.graph.Traversal;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class LoopRouteSpecification<T> implements RouteSpecification<T> {
|
||||
public class LoopRouteSpecification<T> implements MutableRouteSpecification<T> {
|
||||
private final boolean unique;
|
||||
|
||||
public LoopRouteSpecification(boolean unique) {
|
||||
@@ -27,30 +27,27 @@ public class LoopRouteSpecification<T> implements RouteSpecification<T> {
|
||||
|
||||
@Override
|
||||
public boolean specified(Edge<T> edge, Traversal<T> entry) {
|
||||
return check(edge, entry, false);
|
||||
return check(edge, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateSpecified(Edge<T> edge, Traversal<T> entry) {
|
||||
return check(edge, entry, true);
|
||||
public void update(Traversal<T> entry) {
|
||||
setSkip(entry);
|
||||
}
|
||||
|
||||
private boolean check(Edge<T> edge, Traversal<T> entry, boolean update) {
|
||||
private boolean check(Edge<T> edge, Traversal<T> entry) {
|
||||
Optional<Traversal<T>> head = entry.getHead();
|
||||
if (!head.isPresent() || head.get().getEdge() == null) return false;
|
||||
Traversal<T> start = getStart(head.get());
|
||||
boolean found = edge.isConnect(start.getTarget().getEntry());
|
||||
if (found && unique){
|
||||
found = !start.isSkipped();
|
||||
if (update){
|
||||
start.setSkipped(true);
|
||||
setSkip(entry);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private void setSkip(Traversal<T> entry) {
|
||||
if (entry.isSkipped()) return;
|
||||
Traversal<T> curr = entry;
|
||||
Optional<Traversal<T>> head = entry.getHead();
|
||||
while (head.isPresent()) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
public interface MutableRouteSpecification<T> extends RouteSpecification<T> {
|
||||
|
||||
@Override
|
||||
public default boolean updateMutated(){return true;}
|
||||
@Override
|
||||
public default boolean mutable(){return true;}
|
||||
|
||||
}
|
||||
@@ -49,7 +49,9 @@ public class RouteSearcher {
|
||||
graph.build(source, places);
|
||||
LOG.trace("Graph is builds");
|
||||
List<List<Edge<Place>>> paths = new ArrayList<>();
|
||||
Crawler<Place> crawler = specification != null ? new CCrawler<>(graph, specification, paths::add, callback) : new CCrawler<>(graph, paths::add, callback);
|
||||
Crawler<Place> crawler = specification != null ?
|
||||
new CCrawler<>(graph, new SimpleCrawlerSpecification<>(specification, paths::add), callback) :
|
||||
new CCrawler<>(graph, paths::add, callback);
|
||||
crawler.setMaxSize(profile.getJumps());
|
||||
if (profile.getPathPriority() == Profile.PATH_PRIORITY.FAST){
|
||||
crawler.findFast(target, count);
|
||||
@@ -114,9 +116,10 @@ public class RouteSearcher {
|
||||
vGraph.build(source, vendors);
|
||||
LOG.trace("Graph is builds");
|
||||
RouteCollector collector = new RouteCollector();
|
||||
specificator.setGroupCount(vendors.size());
|
||||
Crawler<Vendor> crawler = vGraph.crawler(specificator.build(collector::add, new LoopRouteSpecification<>(true), true), callback);
|
||||
crawler.setMaxSize(scorer.getProfile().getLands());
|
||||
crawler.findMin(source, count);
|
||||
crawler.findMin(source, vendors.size());
|
||||
crawler = vGraph.crawler(specificator.build(collector::add, new RouteSpecificationByTarget<>(source), false), callback);
|
||||
crawler.setMaxSize(scorer.getProfile().getLands());
|
||||
crawler.findMin(source, 1);
|
||||
|
||||
@@ -6,16 +6,57 @@ import ru.trader.analysis.graph.Traversal;
|
||||
public interface RouteSpecification<T> {
|
||||
|
||||
public boolean specified(Edge<T> edge, Traversal<T> entry);
|
||||
public default boolean updateSpecified(Edge<T> edge, Traversal<T> entry){
|
||||
return specified(edge, entry);
|
||||
}
|
||||
public default boolean updateMutated(){return false;}
|
||||
public default boolean mutable(){return false;}
|
||||
public default void update(Traversal<T> entry){}
|
||||
|
||||
default RouteSpecification<T> and(final RouteSpecification<T> other){
|
||||
return (edge, entry) -> RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry);
|
||||
return new RouteSpecification<T>() {
|
||||
@Override
|
||||
public boolean specified(Edge<T> edge, Traversal<T> entry) {
|
||||
return RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateMutated() {
|
||||
return RouteSpecification.this.updateMutated() || other.updateMutated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mutable() {
|
||||
return RouteSpecification.this.mutable() || other.mutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Traversal<T> entry) {
|
||||
RouteSpecification.this.update(entry);
|
||||
other.update(entry);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
default RouteSpecification<T> or(final RouteSpecification<T> other){
|
||||
return (edge, entry) -> RouteSpecification.this.specified(edge, entry) || other.specified(edge, entry);
|
||||
return new RouteSpecification<T>() {
|
||||
@Override
|
||||
public boolean specified(Edge<T> edge, Traversal<T> entry) {
|
||||
return RouteSpecification.this.specified(edge, entry) || other.specified(edge, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateMutated() {
|
||||
return RouteSpecification.this.updateMutated() || other.updateMutated();
|
||||
}
|
||||
@Override
|
||||
public boolean mutable() {
|
||||
return RouteSpecification.this.mutable() || other.mutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Traversal<T> entry) {
|
||||
RouteSpecification.this.update(entry);
|
||||
other.update(entry);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
default RouteSpecification<T> negate(){
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
|
||||
import ru.trader.analysis.graph.CrawlerSpecification;
|
||||
import ru.trader.analysis.graph.Edge;
|
||||
import ru.trader.analysis.graph.Traversal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SimpleCrawlerSpecification<T> implements CrawlerSpecification<T> {
|
||||
private final RouteSpecification<T> routeSpecification;
|
||||
private final Consumer<List<Edge<T>>> onFoundFunc;
|
||||
private final boolean loop;
|
||||
private int groupCount;
|
||||
|
||||
public SimpleCrawlerSpecification(Consumer<List<Edge<T>>> onFoundFunc) {
|
||||
this(null, onFoundFunc, false);
|
||||
}
|
||||
|
||||
public SimpleCrawlerSpecification(RouteSpecification<T> routeSpecification, Consumer<List<Edge<T>>> onFoundFunc) {
|
||||
this(routeSpecification, onFoundFunc, false);
|
||||
}
|
||||
|
||||
public SimpleCrawlerSpecification(RouteSpecification<T> routeSpecification, Consumer<List<Edge<T>>> onFoundFunc, boolean loop) {
|
||||
this.routeSpecification = routeSpecification;
|
||||
this.onFoundFunc = onFoundFunc;
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
protected boolean isLoop() {
|
||||
return loop;
|
||||
}
|
||||
|
||||
public void setGroupCount(int groupCount) {
|
||||
this.groupCount = groupCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouteSpecification<T> routeSpecification() {
|
||||
return routeSpecification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<List<Edge<T>>> onFoundFunc() {
|
||||
return onFoundFunc;
|
||||
}
|
||||
|
||||
public Function<Traversal<T>, Object> getQueueGroupGetter(){
|
||||
return e -> {
|
||||
Traversal<T> curr = e;
|
||||
Traversal<T> target = e;
|
||||
Optional<Traversal<T>> head = e.getHead();
|
||||
while (head.isPresent()) {
|
||||
target = curr;
|
||||
curr = head.get();
|
||||
head = curr.getHead();
|
||||
}
|
||||
return target.getTarget();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<Traversal<T>, Object> getGroupGetter(boolean target) {
|
||||
if (target){
|
||||
return Traversal::getTarget;
|
||||
} else {
|
||||
return getQueueGroupGetter();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
return groupCount;
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,10 @@ import java.util.stream.Collectors;
|
||||
public class VendorsCrawler extends Crawler<Vendor> {
|
||||
private double startFuel;
|
||||
private double startBalance;
|
||||
private final CrawlerSpecification specification;
|
||||
private final VendorsCrawlerSpecification specification;
|
||||
|
||||
public VendorsCrawler(VendorsGraph graph, CrawlerSpecification specification, AnalysisCallBack callback) {
|
||||
super(graph, specification.routeSpecification(), specification.onFoundFunc(), callback);
|
||||
public VendorsCrawler(VendorsGraph graph, VendorsCrawlerSpecification specification, AnalysisCallBack callback) {
|
||||
super(graph, specification, callback);
|
||||
this.specification = specification;
|
||||
startFuel = graph.getProfile().getShip().getTank();
|
||||
startBalance = graph.getProfile().getBalance();
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
import ru.trader.analysis.graph.CrawlerSpecification;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
public interface VendorsCrawlerSpecification extends CrawlerSpecification<Vendor> {
|
||||
|
||||
public double computeWeight(VendorsCrawler.VendorsEdge edge);
|
||||
|
||||
public double computeWeight(VendorsCrawler.VendorsTraversalEntry entry);
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class VendorsGraph extends ConnectibleGraph<Vendor> {
|
||||
return new VendorsCrawler(this, new CrawlerSpecificationByTime(onFoundFunc), callback);
|
||||
}
|
||||
|
||||
public VendorsCrawler crawler(CrawlerSpecification specification, AnalysisCallBack callback){
|
||||
public VendorsCrawler crawler(VendorsCrawlerSpecification specification, AnalysisCallBack callback){
|
||||
return new VendorsCrawler(this, specification, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ public class CCrawler<T extends Connectable<T>> extends Crawler<T> {
|
||||
init();
|
||||
}
|
||||
|
||||
public CCrawler(ConnectibleGraph<T> graph, RouteSpecification<T> specification, Consumer<List<Edge<T>>> onFoundFunc, AnalysisCallBack callback) {
|
||||
super(graph, specification, onFoundFunc, callback);
|
||||
public CCrawler(ConnectibleGraph<T> graph, CrawlerSpecification<T> specification, AnalysisCallBack callback) {
|
||||
super(graph, specification, callback);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@ package ru.trader.analysis.graph;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
import ru.trader.analysis.LimitedQueue;
|
||||
import ru.trader.analysis.RouteSpecification;
|
||||
import ru.trader.analysis.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
@@ -20,22 +18,22 @@ public class Crawler<T> {
|
||||
|
||||
protected final Graph<T> graph;
|
||||
protected final CrawlerCallBack callback;
|
||||
private final Consumer<List<Edge<T>>> onFoundFunc;
|
||||
private final RouteSpecification<T> specification;
|
||||
private final CrawlerSpecification<T> crawlerSpecification;
|
||||
private T target;
|
||||
private int maxSize;
|
||||
|
||||
public Crawler(Graph<T> graph, Consumer<List<Edge<T>>> onFoundFunc, AnalysisCallBack callback) {
|
||||
this(graph, null, onFoundFunc, callback);
|
||||
this(graph, new SimpleCrawlerSpecification<>(onFoundFunc), callback);
|
||||
}
|
||||
|
||||
public Crawler(Graph<T> graph, RouteSpecification<T> specification, Consumer<List<Edge<T>>> onFoundFunc, AnalysisCallBack callback) {
|
||||
public Crawler(Graph<T> graph, CrawlerSpecification<T> specification, AnalysisCallBack callback) {
|
||||
this.graph = graph;
|
||||
this.callback = new CrawlerCallBack(callback);
|
||||
maxSize = graph.getRoot().getLevel();
|
||||
this.onFoundFunc = onFoundFunc;
|
||||
if (specification != null){
|
||||
this.specification = specification;
|
||||
crawlerSpecification = specification;
|
||||
if (crawlerSpecification.routeSpecification() != null){
|
||||
this.specification = crawlerSpecification.routeSpecification();
|
||||
} else {
|
||||
this.specification = (edge, entry) -> isTarget(edge);
|
||||
}
|
||||
@@ -64,17 +62,18 @@ public class Crawler<T> {
|
||||
}
|
||||
|
||||
protected boolean isFound(Edge<T> edge, Traversal<T> head){
|
||||
return isFound(edge, head, false);
|
||||
return specification.specified(edge, head);
|
||||
}
|
||||
|
||||
private boolean isFound(Edge<T> edge, Traversal<T> head, boolean updateStates){
|
||||
return updateStates ? specification.updateSpecified(edge, head) : specification.specified(edge, head);
|
||||
private void updateState(Traversal<T> entry){
|
||||
if (specification.mutable()){
|
||||
specification.update(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void found(List<Edge<T>> res){
|
||||
callback.found();
|
||||
onFoundFunc.accept(res);
|
||||
|
||||
crawlerSpecification.onFoundFunc().accept(res);
|
||||
}
|
||||
|
||||
public int getMaxSize() {
|
||||
@@ -171,7 +170,8 @@ public class Crawler<T> {
|
||||
LOG.trace("DFS from {} to {}, deep {}, count {}, entry {}", source, target, deep, count, entry);
|
||||
if (deep == source.getLevel()){
|
||||
for (Edge<T> next : entry.getEdges()) {
|
||||
if (isFound(next, entry, true)){
|
||||
if (isFound(next, entry)){
|
||||
updateState(entry);
|
||||
List<Edge<T>> res = getCopyList(entry, next);
|
||||
LOG.debug("Last edge found, path {}", res);
|
||||
found++;
|
||||
@@ -222,7 +222,8 @@ public class Crawler<T> {
|
||||
while (iterator.hasNext()){
|
||||
if (callback.isCancel()) break;
|
||||
Edge<T> edge = iterator.next();
|
||||
if (isFound(edge, entry, true)){
|
||||
if (isFound(edge, entry)){
|
||||
updateState(entry);
|
||||
List<Edge<T>> res = getCopyList(entry, edge);
|
||||
LOG.debug("Last edge found, path {}", res);
|
||||
found++;
|
||||
@@ -259,7 +260,8 @@ public class Crawler<T> {
|
||||
LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
|
||||
Edge<T> edge = entry.getEdge();
|
||||
if (edge != null) {
|
||||
if (isFound(edge, entry, true)) {
|
||||
if (isFound(edge, entry)) {
|
||||
updateState(entry);
|
||||
List<Edge<T>> res = entry.toEdges();
|
||||
LOG.debug("Path found {}", res);
|
||||
found++;
|
||||
@@ -296,15 +298,22 @@ public class Crawler<T> {
|
||||
LOG.trace("UCS2 from {} to {}, deep {}, count {}", root.vertex, target, deep, count);
|
||||
int found = 0;
|
||||
double limit = Double.NaN;
|
||||
LimitedQueue<CTEntrySupport> targetsQueue = new LimitedQueue<>(count, Comparator.<CTEntrySupport>naturalOrder());
|
||||
LimitedQueue<CTEntrySupport> queue = new LimitedQueue<>(count, Comparator.<CTEntrySupport>naturalOrder());
|
||||
Queue<CTEntrySupport> targetsQueue;
|
||||
Queue<CTEntrySupport> queue;
|
||||
if (crawlerSpecification.getGroupCount() > 0){
|
||||
int routesByGroup = 1 + count / crawlerSpecification.getGroupCount();
|
||||
targetsQueue = new GroupLimitedQueue<>(routesByGroup, Comparator.<CTEntrySupport>naturalOrder(), e -> crawlerSpecification.getGroupGetter(true).apply(e.entry));
|
||||
queue = new GroupLimitedQueue<>(routesByGroup * maxSize, Comparator.<CTEntrySupport>naturalOrder(), e -> crawlerSpecification.getGroupGetter(false).apply(e.entry));
|
||||
} else {
|
||||
targetsQueue = new LimitedQueue<>(count, Comparator.<CTEntrySupport>naturalOrder());
|
||||
queue = new LimitedQueue<>(count * maxSize, Comparator.<CTEntrySupport>naturalOrder());
|
||||
}
|
||||
root.sort();
|
||||
queue.add(new CTEntrySupport(root));
|
||||
while (!(queue.isEmpty() && targetsQueue.isEmpty()) && count > found){
|
||||
if (callback.isCancel()) break;
|
||||
int alreadyFound = targetsQueue.size();
|
||||
CTEntrySupport curr = targetsQueue.peek();
|
||||
boolean isTarget = curr != null && (queue.isEmpty() || alreadyFound + found >= count || Comparator.<CTEntrySupport>naturalOrder().compare(curr, queue.peek()) <= 0);
|
||||
boolean isTarget = curr != null && (queue.isEmpty() || compareQueue(curr.entry, queue.peek().entry) <= 0);
|
||||
if (isTarget){
|
||||
targetsQueue.poll();
|
||||
} else {
|
||||
@@ -312,12 +321,18 @@ public class Crawler<T> {
|
||||
}
|
||||
CostTraversalEntry entry = curr.entry;
|
||||
LOG.trace("Check path entry {}, weight {}", entry, entry.weight);
|
||||
if (specification.updateMutated() && entry.containsSkipped()){
|
||||
updateState(entry);
|
||||
}
|
||||
if (isTarget) {
|
||||
if (!entry.isSkipped()){
|
||||
updateState(entry);
|
||||
List<Edge<T>> res = entry.toEdges();
|
||||
LOG.trace("Path found {}", res);
|
||||
found++;
|
||||
found(res);
|
||||
if (found >= count) break;
|
||||
}
|
||||
CTEntrySupport next = targetsQueue.peek();
|
||||
limit = next != null ? next.entry.getWeight() : Double.NaN;
|
||||
if (deep > entry.getTarget().getLevel() || entry.size() >= maxSize){
|
||||
@@ -325,12 +340,6 @@ public class Crawler<T> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (alreadyFound + found < count){
|
||||
LOG.trace("Continue search, limit {}", limit);
|
||||
} else {
|
||||
LOG.trace("Already {} found, extracting", alreadyFound);
|
||||
continue;
|
||||
}
|
||||
DFS task = new DFS(curr, deep, count - found, limit);
|
||||
POOL.invoke(task);
|
||||
targetsQueue.addAll(task.getTargets());
|
||||
@@ -339,6 +348,10 @@ public class Crawler<T> {
|
||||
return found;
|
||||
}
|
||||
|
||||
protected int compareQueue(CostTraversalEntry target, CostTraversalEntry queue) {
|
||||
return target.compareTo(queue);
|
||||
}
|
||||
|
||||
private class CTEntrySupport implements Comparable<CTEntrySupport>, Iterator<Edge<T>>{
|
||||
private final CTEntrySupport parent;
|
||||
private Iterator<Edge<T>> iterator;
|
||||
@@ -422,8 +435,14 @@ public class Crawler<T> {
|
||||
this.deep = deep;
|
||||
this.count = count;
|
||||
this.limit = limit;
|
||||
queue = new LimitedQueue<>(count, Comparator.<CTEntrySupport>naturalOrder());
|
||||
if (crawlerSpecification.getGroupCount() > 0){
|
||||
int routesByGroup = 1 + count / crawlerSpecification.getGroupCount();
|
||||
targets = new GroupLimitedQueue<>(routesByGroup, Comparator.<CTEntrySupport>naturalOrder(), e -> crawlerSpecification.getGroupGetter(true).apply(e.entry));
|
||||
queue = new GroupLimitedQueue<>(routesByGroup * maxSize, Comparator.<CTEntrySupport>naturalOrder(), e -> crawlerSpecification.getGroupGetter(false).apply(e.entry));
|
||||
} else {
|
||||
targets = new LimitedQueue<>(count, Comparator.<CTEntrySupport>naturalOrder());
|
||||
queue = new LimitedQueue<>(count * maxSize, Comparator.<CTEntrySupport>naturalOrder());
|
||||
}
|
||||
subTasks = new ArrayList<>(THRESHOLD);
|
||||
isSubTask = subtask;
|
||||
}
|
||||
@@ -491,8 +510,8 @@ public class Crawler<T> {
|
||||
Edge<T> edge = curr.next();
|
||||
CostTraversalEntry entry = curr.entry;
|
||||
if (skip()) continue;
|
||||
LOG.trace("Check edge {}, entry {}, weight {}", edge, entry, entry.weight);
|
||||
boolean isTarget = isFound(edge, entry, true);
|
||||
LOG.trace("Check edge {}, entry {}, weight {}, curr {}", edge, entry, entry.weight, curr);
|
||||
boolean isTarget = isFound(edge, entry);
|
||||
boolean canDeep = !entry.getTarget().isSingle() && deep < entry.getTarget().getLevel() && entry.size() < maxSize-1;
|
||||
if (canDeep || isTarget){
|
||||
CostTraversalEntry nextEntry = travers(entry, edge);
|
||||
@@ -508,23 +527,20 @@ public class Crawler<T> {
|
||||
} else {
|
||||
if (skip()) continue;
|
||||
if (!Double.isNaN(limit) && nextEntry.getWeight() >= limit){
|
||||
if (targets.size() < count){
|
||||
LOG.trace("Not found, limit {}, add entry {} to queue", limit, nextEntry);
|
||||
queue.add(curr);
|
||||
} else {
|
||||
LOG.trace("Not found, limit {}, don't add entry {} to queue", limit, nextEntry);
|
||||
}
|
||||
levelUp();
|
||||
if (!levelUp()){
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isRoot(curr) && maxSize-nextEntry.size() < SPLIT_SIZE){
|
||||
if (addSubTask(curr))
|
||||
if (addSubTask(curr)){
|
||||
levelUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Is limit deep");
|
||||
}
|
||||
@@ -635,6 +651,18 @@ public class Crawler<T> {
|
||||
return skipped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSkipped() {
|
||||
if (skipped) return true;
|
||||
Optional<Traversal<T>> head = getHead();
|
||||
while (head.isPresent()) {
|
||||
Traversal<T> curr = head.get();
|
||||
if (curr.isSkipped()) return true;
|
||||
head = curr.getHead();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List<Edge<T>> collect(Collection<Edge<T>> src){
|
||||
return new ArrayList<>(src);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import ru.trader.analysis.RouteSpecification;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface CrawlerSpecification<T> {
|
||||
public RouteSpecification<T> routeSpecification();
|
||||
|
||||
public Consumer<List<Edge<T>>> onFoundFunc();
|
||||
|
||||
public Function<Traversal<T>, Object> getGroupGetter(boolean target);
|
||||
|
||||
public int getGroupCount();
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public interface Traversal<T> {
|
||||
void sort();
|
||||
void setSkipped(boolean skipped);
|
||||
boolean isSkipped();
|
||||
boolean containsSkipped();
|
||||
|
||||
default boolean isConnect(T target){
|
||||
Edge<T> edge = getEdge();
|
||||
|
||||
@@ -228,7 +228,9 @@ public class VendorsGraphTest extends Assert {
|
||||
vGraph.build(cabreraDock, fWorld.getMarkets(true).collect(Collectors.toList()));
|
||||
LOG.info("Search");
|
||||
SimpleCollector<Vendor> paths = new SimpleCollector<>();
|
||||
Crawler<Vendor> crawler = vGraph.crawler(new CrawlerSpecificationByProfit(new LoopRouteSpecification<>(true), paths::add, true), new AnalysisCallBack());
|
||||
CrawlerSpecificationByProfit specification = new CrawlerSpecificationByProfit(new LoopRouteSpecification<>(true), paths::add, true);
|
||||
specification.setGroupCount(60);
|
||||
Crawler<Vendor> crawler = vGraph.crawler(specification, new AnalysisCallBack());
|
||||
crawler.findMin(cabreraDock, 100);
|
||||
assertEquals(60, paths.get().size());
|
||||
Collection<Vendor> vendors = new ArrayList<>(60);
|
||||
|
||||
Reference in New Issue
Block a user