Add caching for intersection checks

This commit is contained in:
arnekeller 2019-04-02 17:51:26 +02:00
parent 551a0ba14c
commit c13029c5e5
6 changed files with 67 additions and 23 deletions

31
Cargo.lock generated
View File

@ -5,7 +5,8 @@ name = "aufgabe1"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geo 0.12.0", "geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)",
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
@ -45,6 +46,11 @@ name = "bitflags"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.32" version = "1.0.32"
@ -106,9 +112,10 @@ dependencies = [
[[package]] [[package]]
name = "geo" name = "geo"
version = "0.12.0" version = "0.12.0"
source = "git+https://github.com/FliegendeWurst/geo#2b702bce790d918e09dbc6cb4f244312636a87b5"
dependencies = [ dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"geo-types 0.4.1", "geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -116,11 +123,21 @@ dependencies = [
[[package]] [[package]]
name = "geo-types" name = "geo-types"
version = "0.4.1" version = "0.4.1"
source = "git+https://github.com/FliegendeWurst/geo#2b702bce790d918e09dbc6cb4f244312636a87b5"
dependencies = [ dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "hashbrown"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.1" version = "0.3.1"
@ -233,6 +250,11 @@ name = "rustc-demangle"
version = "0.1.13" version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scopeguard"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "simplecss" name = "simplecss"
version = "0.1.0" version = "0.1.0"
@ -385,6 +407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" "checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0daef304fa0b4238f5f7ed7178774b43b06f6a9b6509f6642bef4ff1f7b9b2" "checksum cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0daef304fa0b4238f5f7ed7178774b43b06f6a9b6509f6642bef4ff1f7b9b2"
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
@ -392,6 +415,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" "checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600"
"checksum geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
"checksum geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
@ -408,6 +434,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53b0200cbfa8b3f6cfd6076592717d697a1ddc57cb2a8fbfd3d133c06011b579" "checksum roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53b0200cbfa8b3f6cfd6076592717d697a1ddc57cb2a8fbfd3d133c06011b579"
"checksum rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "120bfe4837befb82c5a637a5a8c490a27d25524ac19fffec5b4e555ca6e36ee8" "checksum rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "120bfe4837befb82c5a637a5a8c490a27d25524ac19fffec5b4e555ca6e36ee8"
"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e" "checksum simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e"
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"

View File

@ -8,12 +8,14 @@ edition = "2018"
codegen-units = 1 codegen-units = 1
opt-level = 3 opt-level = 3
panic = "abort" panic = "abort"
debug = true
[dependencies] [dependencies]
geo = { path = "/home/arne/Documents/Code/Github/geo/geo" } geo = { git = "https://github.com/FliegendeWurst/geo" }
svg = "0.5.11" svg = "0.5.11"
chrono = "0.4.6" chrono = "0.4.6"
structopt = { version = "0.2", default-features = false } structopt = { version = "0.2", default-features = false }
svgdom = "0.16.1" svgdom = "0.16.1"
rstar = "0.2.0" rstar = "0.2.0"
lazy_static = "1.3.0" lazy_static = "1.3.0"
hashbrown = "0.1.8"

View File

@ -239,7 +239,7 @@ Der Weg von Lisa und dem Bus ist animiert. Zusätzlich zu den unbedingt benötig
\subsection{Optimierungen} \subsection{Optimierungen}
Wie bereits in der Laufzeitanalyse kurz angesprochen, benutzt das Programm bestimmte Heuristiken, um die Suche zu beschleunigen. Eine davon ist die maximal mögliche Wartezeit für eine bestimmte Route. Dafür berechnet das Programm die höchstmögliche Position des Busses, was proportional zur gesuchten Größe ist. Wie bereits in der Laufzeitanalyse kurz angesprochen, benutzt das Programm bestimmte Heuristiken, um die Suche zu beschleunigen. Eine davon ist die maximal mögliche Wartezeit für eine bestimmte Route. Dafür berechnet das Programm die höchstmögliche Position des Busses, was proportional zur gesuchten Größe ist.
Um zu überprüfen, ob ein Routenabschnitt die Hindernisse schneidet, wird unter anderem ein R*-Baum benutzt. Mit ca. 40 Polygonen ist diese Methode ca. 5-6x schneller als alle Polygone zu überprüfen. Die Geschwindigkeitsgewinne werden größer, je mehr Polygone vorhanden sind. Um zu überprüfen, ob ein Routenabschnitt die Hindernisse schneidet, wird unter anderem ein R*-Baum benutzt. Fûr ca. 40 Polygonen ist diese Methode ca. 5-6x schneller als alle Polygone zu überprüfen. Die Geschwindigkeitsgewinne werden größer, je mehr Polygone vorhanden sind. Zudem wird das Ergebnis in einer Hashtabelle gespeichert, was die insgesamte Performanz ca. 6-10x steigert ($\frac{n*(n-1)}{2}$ Berechnungen statt $n!$).
\section{Beispiele} \section{Beispiele}
Alle Beispiele sind im Maßstab 1:70m. Alle Beispiele sind im Maßstab 1:70m.

View File

@ -9,7 +9,7 @@ use std::fs;
use super::*; use super::*;
pub(crate) fn dump_latex_code(route: &[RunState], polys: &[Polygon]) { pub(crate) fn dump_latex_code(route: &[RunState], polys: &[Polygon]) {
const SCALE: f64 = 70.0; const SCALE: f32 = 70.0;
let mut xmax = 0.0; let mut xmax = 0.0;
let mut ymax = 0.0; let mut ymax = 0.0;
for poly in polys { for poly in polys {
@ -220,7 +220,7 @@ pub(crate) fn generate_svg(
document.append(path); document.append(path);
} }
let mut first: (f64, f64) = (route1[0].x(), route1[0].y()); let mut first: (f32, f32) = (route1[0].x(), route1[0].y());
first.1 *= -1.0; first.1 *= -1.0;
let mut data = Data::new().move_to(first); let mut data = Data::new().move_to(first);
for point in &route1[1..] { for point in &route1[1..] {

View File

@ -85,16 +85,16 @@ fn read_svg(input: &str) -> Result<InputData, Box<Error>> {
for p in &path.0 { for p in &path.0 {
match p { match p {
PathSegment::MoveTo { x, y, .. } => { PathSegment::MoveTo { x, y, .. } => {
points.push(Point::new(*x, -*y)); points.push(Point::new(*x as f32, -*y as f32));
} }
PathSegment::LineTo { x, y, .. } => { PathSegment::LineTo { x, y, .. } => {
points.push(Point::new(*x, -*y)); points.push(Point::new(*x as f32, -*y as f32));
} }
PathSegment::VerticalLineTo { y, .. } => { PathSegment::VerticalLineTo { y, .. } => {
points.push(Point::new(points.last().unwrap().x(), -*y)); points.push(Point::new(points.last().unwrap().x(), -*y as f32));
} }
PathSegment::HorizontalLineTo { x, .. } => { PathSegment::HorizontalLineTo { x, .. } => {
points.push(Point::new(*x, points.last().unwrap().y())); points.push(Point::new(*x as f32, points.last().unwrap().y()));
} }
PathSegment::ClosePath { .. } => break, PathSegment::ClosePath { .. } => break,
_ => unimplemented!("nicht unterstütztes SVG-Element: {:?}", p), _ => unimplemented!("nicht unterstütztes SVG-Element: {:?}", p),

View File

@ -1,12 +1,13 @@
use chrono::{Duration, NaiveTime}; use chrono::{Duration, NaiveTime};
use geo::prelude::*; use geo::prelude::*;
use hashbrown::HashMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use rstar::{RTree, RTreeObject}; use rstar::{RTree, RTreeObject};
use structopt::StructOpt; use structopt::StructOpt;
use std::cmp; use std::cmp;
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
use std::f64; use std::f32;
use std::process::exit; use std::process::exit;
use std::sync::RwLock; use std::sync::RwLock;
use std::time::{self, Instant}; use std::time::{self, Instant};
@ -14,10 +15,10 @@ use std::time::{self, Instant};
mod display; mod display;
mod input; mod input;
type Point = geo::Point<f64>; type Point = geo::Point<f32>;
type Line = geo::Line<f64>; type Line = geo::Line<f32>;
type LineString = geo::LineString<f64>; type LineString = geo::LineString<f32>;
type Polygon = geo::Polygon<f64>; type Polygon = geo::Polygon<f32>;
/// Datenstruktur für Kommandozeilen-Argumente /// Datenstruktur für Kommandozeilen-Argumente
#[derive(StructOpt, Clone, Copy)] #[derive(StructOpt, Clone, Copy)]
@ -34,21 +35,21 @@ struct Opt {
tkz: bool, tkz: bool,
/// Geschwindigkeit von Lisa (in km/h) /// Geschwindigkeit von Lisa (in km/h)
#[structopt(short = "l", long = "lisa", default_value = "15")] #[structopt(short = "l", long = "lisa", default_value = "15")]
lisa: f64, lisa: f32,
/// Geschwindigkeit vom Bus (in km/h) /// Geschwindigkeit vom Bus (in km/h)
#[structopt(short = "b", long = "bus", default_value = "30")] #[structopt(short = "b", long = "bus", default_value = "30")]
bus: f64, bus: f32,
} }
/// Möglicher Zustand von Lisas Rennen /// Möglicher Zustand von Lisas Rennen
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
struct RunState { struct RunState {
/// Theoretisch mögliche Startposition des Busses /// Theoretisch mögliche Startposition des Busses
bus: f64, bus: f32,
/// Lisas Position /// Lisas Position
pos: Point, pos: Point,
// Bisher zurückgelegte Strecke // Bisher zurückgelegte Strecke
total_distance: f64, total_distance: f32,
} }
/// Trait-Implementierung, damit die Struktur in BinaryHeaps passt /// Trait-Implementierung, damit die Struktur in BinaryHeaps passt
impl cmp::Eq for RunState {} impl cmp::Eq for RunState {}
@ -134,7 +135,7 @@ fn main() {
states.push(vec![start]); states.push(vec![start]);
// beste Lösung speichern // beste Lösung speichern
let mut best_bus = f64::NEG_INFINITY; let mut best_bus = f32::NEG_INFINITY;
let mut best = vec![]; let mut best = vec![];
// Zwischenlösungen speichern // Zwischenlösungen speichern
let save_prefix = "route_"; let save_prefix = "route_";
@ -274,11 +275,22 @@ fn main() {
lazy_static! { lazy_static! {
static ref RTREE: RwLock<RTree<Polygon>> = RwLock::new(RTree::new()); static ref RTREE: RwLock<RTree<Polygon>> = RwLock::new(RTree::new());
static ref COLLISIONS: RwLock<HashMap<[i16; 4], bool>> = RwLock::new(HashMap::new());
} }
/// Schneidet die Linie keines der Polygone? /// Schneidet die Linie keines der Polygone?
/// (Berührungen erlaubt) /// (Berührungen erlaubt)
fn none_intersect(line: Line) -> bool { fn none_intersect(mut line: Line) -> bool {
if line.start.y > line.end.y || (line.start.y == line.end.y && line.start.x > line.end.x) {
let end = line.start;
line.start = line.end;
line.end = end;
}
let mut collisions = COLLISIONS.write().unwrap();
let id = [line.start.x as i16, line.start.y as i16, line.end.x as i16, line.end.y as i16];
if let Some(&r) = collisions.get(&id) {
return r;
}
let rtree = RTREE.read().unwrap(); let rtree = RTREE.read().unwrap();
let polys = rtree.locate_in_envelope_intersecting(&line.envelope()); let polys = rtree.locate_in_envelope_intersecting(&line.envelope());
let mut middle = line.start; let mut middle = line.start;
@ -305,29 +317,32 @@ fn none_intersect(line: Line) -> bool {
continue; // Linie schneidet sich immer selbst continue; // Linie schneidet sich immer selbst
} }
if l.intersects(&line) { if l.intersects(&line) {
collisions.insert(id, false);
return false; return false;
} }
} }
// falls Mittelpunkt in Polygon: // falls Mittelpunkt in Polygon:
if p.euclidean_distance(&middle) == 0.0 { if p.euclidean_distance(&middle) == 0.0 {
// schneidet Polygon // schneidet Polygon
collisions.insert(id, false);
return false; return false;
} }
// (dieser Fall tritt auf, wenn die Linie von einer Polygon-Ecke zu einer anderen Ecke verläuft) // (dieser Fall tritt auf, wenn die Linie von einer Polygon-Ecke zu einer anderen Ecke verläuft)
} }
// keine Überschneidung gefunden: der Weg ist frei! // keine Überschneidung gefunden: der Weg ist frei!
collisions.insert(id, true);
true true
} }
/// Euklidische Distanz zweier Punkte /// Euklidische Distanz zweier Punkte
fn distance(a: Point, b: Point) -> f64 { fn distance(a: Point, b: Point) -> f32 {
((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt() ((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt()
} }
/// Mit dieser Funktion kann man bestimmen, wo der Bus zu Lisas Startzeit war. /// Mit dieser Funktion kann man bestimmen, wo der Bus zu Lisas Startzeit war.
/// Die Funktion nimmt an, dass Lisa, von ihrer aktuellen Position aus, den Bus ohne Umwege erreichen kann. /// Die Funktion nimmt an, dass Lisa, von ihrer aktuellen Position aus, den Bus ohne Umwege erreichen kann.
/// Sehr hilfreich als Heuristik, da die theoretisch beste Lösung nie schlechter als das eigentliche Ergebnis sein kann. /// Sehr hilfreich als Heuristik, da die theoretisch beste Lösung nie schlechter als das eigentliche Ergebnis sein kann.
fn best_bus_pos(opt: &Opt, total_distance: f64, lisa: Point) -> f64 { fn best_bus_pos(opt: &Opt, total_distance: f32, lisa: Point) -> f32 {
let x_l = lisa.x(); // a let x_l = lisa.x(); // a
let y_l = lisa.y(); // b let y_l = lisa.y(); // b