algorithms-for-computing-li.../LinearRegressionTool/src/main/java/de/wwwu/awolf/presenter/util/IntersectionComputer.java

156 lines
5.5 KiB
Java

package de.wwwu.awolf.presenter.util;
import com.google.common.collect.Lists;
import de.wwwu.awolf.model.Line;
import de.wwwu.awolf.model.Point;
import java.util.*;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
*
* @Author: Armin Wolf
* @Email: a_wolf28@uni-muenster.de
* @Date: 18.09.2017.
*/
public class IntersectionComputer {
private static IntersectionComputer instance;
/**
* Konstruktor
*/
private IntersectionComputer() {
}
public static IntersectionComputer getInstance() {
if (instance == null) {
instance = new IntersectionComputer();
Logging.logInfo("Created instance of IntersectionComputer");
}
return instance;
}
/**
* Berechnet zu einer gegebenen Menge von dualen Geraden die Schnittpunkte. Dafür wird ein modifizierter Merge-Sort
* Algorithmus verwendett. Um die Performance zu steigern wird die Berechnung ab einer passenden Größe auf vier
* Threads ausgelagert.
*
* @param lower untere Schranke
* @param higher obere Schranke
* @return Liste der Schnittpunkte
*/
public Set<Point> compute(final Collection<Line> lines, final double lower, final double higher) {
final Set<Line> fullInput = new HashSet<>(lines);
final Set<Line> copyInput = new HashSet<>(lines);
if (lower == higher) {
return Collections.emptySet();
} else {
Logging.logDebug("Open ForkJoinPool: lines: " + lines.size() + " I(" + lower + ", " + higher + "]");
ForkJoinPool pool = ForkJoinPool.commonPool();
RecursiveComputationTask recursiveComputationTask = new RecursiveComputationTask(fullInput, copyInput, lower, higher);
pool.execute(recursiveComputationTask);
Set<Point> join = recursiveComputationTask.join();
return join;
}
}
/**
* Berechnet die Schnittpunkte zwischen einer gegebenen Gerade und einer Menge an Geraden.
*
* @param set Menge an Geraden
* @param sampledLine eine spezielle Gerade
* @return Liste mit x Koordinaten der Schnittpunkte
*/
public List<Double> calculateIntersectionAbscissas(Collection<Line> set, Line sampledLine, double lower, double upper) {
List<Line> lines = new LinkedList<>(set);
Set<Double> intersections = new HashSet<>();
for (Line line : lines) {
if (line != sampledLine) {
if (sampledLine.doIntersect(line, lower, upper)) {
intersections.add(sampledLine.intersect(line).getX());
}
}
}
return new ArrayList<>(intersections);
}
private class RecursiveComputationTask extends RecursiveTask<Set<Point>> {
private static final int THRESHOLD = 200;
private final Set<Line> lines;
private final Set<Line> fullList;
private final double lower;
private final double upper;
public RecursiveComputationTask(final Set<Line> fullList, final Set<Line> lines, final double lower, final double upper) {
this.lines = lines;
this.fullList = fullList;
this.lower = lower;
this.upper = upper;
}
@Override
protected Set<Point> compute() {
if (this.lines.isEmpty()) {
return Collections.emptySet();
} else if (this.lines.size() > THRESHOLD) {
return ForkJoinTask.invokeAll(createSubTask()).stream().map(ForkJoinTask::join).flatMap(Collection::stream).collect(Collectors.toSet());
} else {
return work(this.fullList, this.lines, this.lower, this.upper);
}
}
private Collection<RecursiveComputationTask> createSubTask() {
List<RecursiveComputationTask> dividedTasks = new ArrayList<>();
long midpoint = Math.round(this.lines.size() * 0.5);
Set<Line> firstSubSet = new HashSet<>();
Set<Line> secondSubSet = new HashSet<>();
AtomicInteger count = new AtomicInteger();
this.lines.forEach(next -> {
int index = count.getAndIncrement();
if (index < midpoint) {
firstSubSet.add(next);
} else {
secondSubSet.add(next);
}
});
dividedTasks.add(new RecursiveComputationTask(this.fullList, firstSubSet, this.lower, this.upper));
dividedTasks.add(new RecursiveComputationTask(this.fullList, secondSubSet, this.lower, this.upper));
return dividedTasks;
}
private Set<Point> work(Set<Line> fullList, Set<Line> lines, double lower, double higher) {
Set<Point> points = new HashSet<>();
List<List<Line>> lists = Lists.cartesianProduct(new ArrayList<>(fullList), new ArrayList<>(lines));
lists.forEach(entry -> {
if (entry.get(0).doIntersect(entry.get(1), lower, upper)) {
Point intersect = entry.get(0).intersect(entry.get(1));
if (intersect.getX() > lower && intersect.getX() <= higher) {
points.add(intersect);
}
}
});
return new HashSet<>(points);
}
}
}