Add a cache; only try combining while merges lower price
This commit is contained in:
parent
148f59010e
commit
dacb278ef3
3 changed files with 113 additions and 44 deletions
|
@ -1,5 +1,5 @@
|
||||||
use db::LayoutDB;
|
use db::{LayoutDB, SavedLayout};
|
||||||
use city::{City, SIZE};
|
use city::{City, House, SIZE};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use crate::combine::transpose_layout;
|
use crate::combine::transpose_layout;
|
||||||
|
|
||||||
|
@ -23,36 +23,40 @@ fn main() {
|
||||||
let transposed_city = transpose_city(&city);
|
let transposed_city = transpose_city(&city);
|
||||||
eprintln!("Finished building a transposed city");
|
eprintln!("Finished building a transposed city");
|
||||||
|
|
||||||
const TOP_LAYOUT_COUNT: usize = 100;
|
const TOP_LAYOUT_COUNT: usize = 300;
|
||||||
|
|
||||||
|
let mut cache = combine::CompatibilityCache::new();
|
||||||
|
|
||||||
let mut last_improve_step = LastStep::None;
|
let mut last_improve_step = LastStep::None;
|
||||||
loop {
|
loop {
|
||||||
if last_improve_step == LastStep::Vertical { break; }
|
if last_improve_step == LastStep::Vertical { break; }
|
||||||
eprintln!("Starting to combine {} top houses DB; vertical cuts", TOP_LAYOUT_COUNT);
|
eprintln!("Starting to combine {} top houses DB; vertical cuts", TOP_LAYOUT_COUNT);
|
||||||
let sorted: Vec<_> = db.layouts().iter()
|
let sorted: Vec<SavedLayout> = db.layouts().iter()
|
||||||
.sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses())))
|
.sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses())))
|
||||||
.map(|layout| layout.clone())
|
.map(|layout| (*layout).clone())
|
||||||
.collect();
|
.collect();
|
||||||
let chosen_layouts: Vec<_> = sorted.iter().take(TOP_LAYOUT_COUNT).map(|l| l.houses().clone()).collect();
|
let chosen_layouts: Vec<_> = sorted.into_iter().take(TOP_LAYOUT_COUNT).collect();
|
||||||
if combine::try_combine(&city, &chosen_layouts, &chosen_layouts, &mut db, false) {
|
if combine::try_combine(&city, &chosen_layouts, &chosen_layouts, &mut db, &mut cache, false) {
|
||||||
last_improve_step = LastStep::Vertical;
|
last_improve_step = LastStep::Vertical;
|
||||||
}
|
}
|
||||||
eprintln!("Finished vertical cuts, improvement: {}", last_improve_step == LastStep::Vertical);
|
eprintln!("Finished vertical cuts, improvement: {}", last_improve_step == LastStep::Vertical);
|
||||||
|
|
||||||
if last_improve_step == LastStep::Horizontal { break; }
|
if last_improve_step == LastStep::Horizontal { break; }
|
||||||
|
|
||||||
let sorted: Vec<_> = db.layouts().iter()
|
let sorted: Vec<SavedLayout> = db.layouts().iter()
|
||||||
.sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses())))
|
.sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses())))
|
||||||
.map(|layout| layout.clone())
|
.map(|layout| (*layout).clone())
|
||||||
.collect();
|
.collect();
|
||||||
let chosen_layouts: Vec<_> = sorted.iter().take(TOP_LAYOUT_COUNT).map(|l| l.houses().clone()).collect();
|
let chosen_layouts: Vec<_> = sorted.into_iter().take(TOP_LAYOUT_COUNT).collect();
|
||||||
let transposed_chosen_layouts: Vec<_> = chosen_layouts.iter().map(|x| transpose_layout(x)).collect();
|
let transposed_chosen_layouts: Vec<_> = chosen_layouts.iter().map(|x| transpose_saved_layout(x)).collect();
|
||||||
|
|
||||||
eprintln!("Starting to combine {} top houses DB; horizontal cuts", TOP_LAYOUT_COUNT);
|
eprintln!("Starting to combine {} top houses DB; horizontal cuts", TOP_LAYOUT_COUNT);
|
||||||
if combine::try_combine(&transposed_city, &transposed_chosen_layouts, &transposed_chosen_layouts, &mut db, true) {
|
if combine::try_combine(&transposed_city, &transposed_chosen_layouts, &transposed_chosen_layouts, &mut db, &mut cache, true) {
|
||||||
last_improve_step = LastStep::Horizontal;
|
last_improve_step = LastStep::Horizontal;
|
||||||
}
|
}
|
||||||
eprintln!("Finished horizontal cuts, improvement: {}", last_improve_step == LastStep::Horizontal);
|
eprintln!("Finished horizontal cuts, improvement: {}", last_improve_step == LastStep::Horizontal);
|
||||||
|
|
||||||
|
if last_improve_step == LastStep::None { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,3 +72,12 @@ fn transpose_city(city: &City) -> City {
|
||||||
|
|
||||||
City::new(transposed_prices)
|
City::new(transposed_prices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transpose_saved_layout(layout: &SavedLayout) -> SavedLayout {
|
||||||
|
let mut transposed = Vec::new();
|
||||||
|
for house in layout.houses() {
|
||||||
|
transposed.push(House::new(house.y, house.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
SavedLayout::new(layout.id(), transposed)
|
||||||
|
}
|
||||||
|
|
115
src/combine.rs
115
src/combine.rs
|
@ -1,40 +1,75 @@
|
||||||
use crate::city;
|
use crate::city;
|
||||||
use crate::db::LayoutDB;
|
use crate::db::{LayoutDB, SavedLayout};
|
||||||
use crate::city::{City, House, SIZE};
|
use crate::city::{City, House, SIZE};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use itertools::iproduct;
|
use itertools::iproduct;
|
||||||
use std::collections::VecDeque;
|
use std::collections::{VecDeque, HashMap};
|
||||||
use std::collections::vec_deque::Iter;
|
use std::collections::vec_deque::Iter;
|
||||||
|
|
||||||
pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &Vec<Vec<House>>, db: &mut LayoutDB, transposed: bool) -> bool {
|
struct LeftState<'a> {
|
||||||
let mut improved = false;
|
layout: &'a SavedLayout,
|
||||||
|
sorted_houses: Vec<House>,
|
||||||
|
line: LeftLine
|
||||||
|
}
|
||||||
|
|
||||||
// Sorted in reverse so we can remove from the end
|
struct RightState<'a> {
|
||||||
let mut left_houses_sorted: Vec<Vec<House>> = left_layouts.iter()
|
layout: &'a SavedLayout,
|
||||||
.map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect())
|
line: RightLine
|
||||||
.collect();
|
}
|
||||||
let right_houses_sorted: Vec<Vec<House>> = right_layouts.iter()
|
|
||||||
.map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect())
|
pub struct CompatibilityCache {
|
||||||
.collect();
|
map: HashMap<(usize, usize, usize, bool), bool>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompatibilityCache {
|
||||||
|
pub fn new() -> CompatibilityCache {
|
||||||
|
CompatibilityCache {
|
||||||
|
map: HashMap::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_compatible(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, index: usize, y_axis: bool) -> Option<&bool> {
|
||||||
|
self.map.get(&(left_layout.id(), right_layout.id(), index, y_axis))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_compatible(&mut self, left_layout: &SavedLayout, right_layout: &SavedLayout, index: usize, y_axis: bool, compatible: bool) {
|
||||||
|
self.map.insert((left_layout.id(), right_layout.id(), index, y_axis), compatible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts: &Vec<SavedLayout>, db: &mut LayoutDB, cache: &mut CompatibilityCache, transposed: bool) -> bool {
|
||||||
|
let mut improved = false;
|
||||||
|
|
||||||
let mut lefts = Vec::new();
|
let mut lefts = Vec::new();
|
||||||
let mut rights = Vec::new();
|
let mut rights = Vec::new();
|
||||||
|
|
||||||
for _ in left_layouts {
|
for left_layout in left_layouts {
|
||||||
lefts.push(LeftLine::new());
|
// Sorted in reverse so we can remove from the end
|
||||||
|
let sorted_houses: Vec<_> = left_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect();
|
||||||
|
|
||||||
|
lefts.push(LeftState {
|
||||||
|
layout: &left_layout,
|
||||||
|
sorted_houses,
|
||||||
|
line: LeftLine::new()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for mut right_layout in right_houses_sorted {
|
for right_layout in right_layouts {
|
||||||
let mut right = RightLine::new();
|
let mut sorted_houses: Vec<_> = right_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect();
|
||||||
|
|
||||||
|
let mut line = RightLine::new();
|
||||||
// Make sure that we include all houses initially
|
// Make sure that we include all houses initially
|
||||||
while let Some(house) = right_layout.pop() {
|
while let Some(house) = sorted_houses.pop() {
|
||||||
right.add_house(house, &city);
|
line.add_house(house, &city);
|
||||||
}
|
}
|
||||||
rights.push(right);
|
rights.push(RightState {
|
||||||
|
layout: right_layout,
|
||||||
|
line
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut best_price = left_layouts.iter().chain(right_layouts.iter())
|
let mut best_price = left_layouts.iter().chain(right_layouts.iter())
|
||||||
.map(|layout| city::get_price(&city, layout))
|
.map(|layout| city::get_price(&city, layout.houses()))
|
||||||
.min();
|
.min();
|
||||||
|
|
||||||
let axis = if transposed { "y" } else { "x" };
|
let axis = if transposed { "y" } else { "x" };
|
||||||
|
@ -44,11 +79,11 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
|
||||||
eprintln!("Starting {} {}", axis, x);
|
eprintln!("Starting {} {}", axis, x);
|
||||||
|
|
||||||
// Update the lines
|
// Update the lines
|
||||||
for (left_line, left_houses) in lefts.iter_mut().zip(left_houses_sorted.iter_mut()) {
|
for left in lefts.iter_mut() {
|
||||||
while let Some(house) = left_houses.last() {
|
while let Some(house) = left.sorted_houses.last() {
|
||||||
if house.x == x {
|
if house.x == x {
|
||||||
left_line.add_house(*house, &city);
|
left.line.add_house(*house, &city);
|
||||||
left_houses.pop();
|
left.sorted_houses.pop();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +91,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
|
||||||
}
|
}
|
||||||
|
|
||||||
for right_line in rights.iter_mut() {
|
for right_line in rights.iter_mut() {
|
||||||
right_line.remove_houses(x, &city);
|
right_line.line.remove_houses(x, &city);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check compatibility of lines
|
// Check compatibility of lines
|
||||||
|
@ -66,21 +101,37 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pairs: Vec<_> = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate())
|
let pairs = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate())
|
||||||
.filter(|((left_i, _), (right_i, _))| left_i != right_i)
|
.filter(|((left_i, _), (right_i, _))| left_i != right_i)
|
||||||
.map(|((left_i, left), (right_i, right))| (left, right, left.price + right.price, left_i, right_i))
|
.map(|((left_i, left), (right_i, right))| (left, right, left.line.price + right.line.price, left_i, right_i))
|
||||||
.sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2))
|
.sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2));
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut compatibles = 0;
|
let mut compatibles = 0;
|
||||||
let mut incompatibles = 0;
|
let mut incompatibles = 0;
|
||||||
|
let mut cache_hits = 0;
|
||||||
for (left, right, price, left_i, right_i) in pairs {
|
for (left, right, price, left_i, right_i) in pairs {
|
||||||
if is_compatible(city, &left, &right) {
|
if let Some(min_price) = best_price {
|
||||||
|
if price >= min_price {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let compatible = match cache.is_compatible(left.layout, right.layout, x, transposed) {
|
||||||
|
None => {
|
||||||
|
let compatible = is_compatible(city, &left.line, &right.line);
|
||||||
|
cache.set_compatible(left.layout, right.layout, x, transposed, compatible);
|
||||||
|
compatible
|
||||||
|
}
|
||||||
|
Some(compatible) => {
|
||||||
|
cache_hits += 1;
|
||||||
|
*compatible
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if compatible {
|
||||||
if best_price.is_none() || price < best_price.unwrap() {
|
if best_price.is_none() || price < best_price.unwrap() {
|
||||||
best_price = Some(price);
|
best_price = Some(price);
|
||||||
eprintln!("{} - new best score, cut on {} {}, left {} - right {}, printing", price, axis, x, left_i, right_i);
|
eprintln!("{} - new best score, cut on {} {}, left {} - right {}, printing", price, axis, x, left_i, right_i);
|
||||||
println!("{} - new best score, cut on {} {}, left {} - right {}", price, axis, x, left_i, right_i);
|
println!("{} - new best score, cut on {} {}, left {} - right {}", price, axis, x, left_i, right_i);
|
||||||
let mut new_houses: Vec<_> = left.houses().copied().chain(right.houses().copied()).collect();
|
let mut new_houses: Vec<_> = left.line.houses().copied().chain(right.line.houses().copied()).collect();
|
||||||
if transposed {
|
if transposed {
|
||||||
new_houses = transpose_layout(&new_houses);
|
new_houses = transpose_layout(&new_houses);
|
||||||
}
|
}
|
||||||
|
@ -90,7 +141,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only add best results to avoid overfilling the database with similar layouts
|
// We only add best results to avoid overfilling the database with similar layouts
|
||||||
db.add_layout(&new_houses, true);
|
db.add_layout(&new_houses, false);
|
||||||
improved = true;
|
improved = true;
|
||||||
}
|
}
|
||||||
compatibles += 1;
|
compatibles += 1;
|
||||||
|
@ -102,7 +153,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("{} incompatibles checked before {} compatible", incompatibles, compatibles);
|
eprintln!("{} incompatibles checked before {} compatible ({} cache hits)", incompatibles, compatibles, cache_hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
improved
|
improved
|
||||||
|
|
|
@ -7,12 +7,17 @@ pub struct LayoutDB {
|
||||||
layouts: Vec<SavedLayout>
|
layouts: Vec<SavedLayout>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SavedLayout {
|
pub struct SavedLayout {
|
||||||
id: usize,
|
id: usize,
|
||||||
houses: Vec<House>
|
houses: Vec<House>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SavedLayout {
|
impl SavedLayout {
|
||||||
|
pub fn new(id: usize, houses: Vec<House>) -> Self {
|
||||||
|
SavedLayout { id, houses }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn houses(&self) -> &Vec<House> {
|
pub fn houses(&self) -> &Vec<House> {
|
||||||
&self.houses
|
&self.houses
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue