Řešení KSP úlohy 33-3-4 Obsazování území https://ksp.mff.cuni.cz/h/ulohy/33/zadani3.html#task-33-3-4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

#### 177 lines 9.0 KiB Raw Normal View History

 3 years ago ```use db::{MemoryLayoutDB, SqliteLayoutDB, LayoutDB, SavedLayout}; ``` ```use city::{City, House, HouseLayout, get_price}; ``` 3 years ago ```use itertools::Itertools; ``` ```use rand::{thread_rng, Rng, SeedableRng}; ``` ```use rand::rngs::StdRng; ``` ```use rayon::prelude::*; ``` ``` ``` ```mod city; ``` ```mod db; ``` ```mod combine; ``` ```mod subcity; ``` ```mod optimization; ``` ```mod population; ``` ``` ``` ```fn main() { ``` ``` let mut sqlite_db = SqliteLayoutDB::from_file("layouts.sqlite").expect("Failed to load the DB"); ``` ``` eprintln!("Loaded the DB, {} stored layouts", sqlite_db.layouts().len()); ``` ``` ``` ``` let city = City::read_from_file("01.in", city::INPUT_CITY_WIDTH, city::INPUT_CITY_HEIGHT); ``` ``` eprintln!("Loaded the city file, {} houses", city.get_house_count()); ``` ``` ``` ``` let best_layout: SavedLayout = sqlite_db.layouts().iter() ``` ``` .sorted_by(|x, y| get_price(&city, x.houses()).cmp(&get_price(&city, y.houses()))) ``` 3 years ago ``` .map(|layout| (*layout).clone()) ``` ``` .next().expect("No best layout found"); ``` ``` eprintln!("Found best layout, ID {}, price {}", best_layout.id(), get_price(&city, best_layout.houses())); ``` 3 years ago ``` ``` ``` let x_range = 5533..=12000; ``` ``` let y_range = 4750..=12500; ``` ``` //let x_range = 5533..=8000; ``` ``` //let y_range = 4750..=8000; ``` ``` eprintln!("X {}-{}, Y {}-{}", x_range.start(), x_range.end(), y_range.start(), y_range.end()); ``` ``` ``` ``` let static_houses: Vec = best_layout.houses().iter() ``` ``` .filter(|house| !(x_range.contains(&house.x) && y_range.contains(&house.y))) ``` ``` .map(|&house| house) ``` ``` .collect(); ``` ``` ``` ``` let removed_houses: Vec = best_layout.houses().iter() ``` ``` .filter(|house| x_range.contains(&house.x) && y_range.contains(&house.y)) ``` ``` .map(|&house| house) ``` ``` .collect(); ``` ``` ``` ``` let removed_price: u32 = removed_houses.iter().map(|x| city.get_price(*x) as u32).sum(); ``` ``` eprintln!("Price of all removed houses: {}", removed_price); ``` ``` ``` ``` let subcity = subcity::build_subcity(&city, &static_houses); ``` ``` ``` ``` eprintln!("Built subcity, width {}, height {}, {} houses, offset ({},{})", ``` ``` subcity.city().width(), ``` ``` subcity.city().height(), ``` ``` subcity.city().get_house_count(), ``` ``` subcity.x_offset(), ``` ``` subcity.y_offset() ``` ``` ); ``` ``` ``` ``` //let mut subcity_db = MemoryLayoutDB::new(); ``` ``` let filename = format!("X{}_{}Y{}_{}ID{}.sqlite", x_range.start(), x_range.end(), y_range.start(), y_range.end(), best_layout.id()); ``` ``` let mut subcity_db = SqliteLayoutDB::from_file(&filename).unwrap(); ``` ``` ``` ``` //for layout in subcity_db.layouts().iter() ``` ``` // .filter(|x| get_price(subcity.city(), x.houses()) < removed_price) ``` ``` // .sorted_by(|x, y| get_price(subcity.city(), x.houses()).cmp(&get_price(subcity.city(), y.houses()))) { ``` ``` // let price = get_price(subcity.city(), layout.houses()); ``` ``` // let mut full_houses = subcity.to_full_houses(layout.houses()); ``` ``` // assert!(city::is_valid(&city, &full_houses).is_some()); ``` ``` ``` ``` // let mut house_layout = HouseLayout::new(&city); ``` ``` // for house in &full_houses { ``` ``` // house_layout.add_house(*house); ``` ``` // } ``` ``` // let seed: u64 = thread_rng().gen(); ``` ``` // let mut rng = StdRng::seed_from_u64(seed); ``` ``` // optimization::iterate_improvements(&mut house_layout, &mut rng, true); ``` ``` // eprintln!("Improvements finished"); ``` ``` // assert!(house_layout.is_valid()); ``` ``` // let improved_price = city::get_price(&city, house_layout.houses()); ``` ``` // if improved_price < city::get_price(&city, &full_houses) { ``` ``` // eprintln!("Found improvement, new price {}, updating houses", improved_price); ``` ``` // full_houses = house_layout.houses().clone(); ``` ``` // } ``` ``` ``` ``` // assert!(city::is_valid(&city, &full_houses).is_some()); ``` ``` // println!("Layout {}, price {}, full price {}", layout.id(), price, city::get_price(&city, &full_houses)); ``` ``` // // Be careful with duplicates here ``` ``` // //sqlite_db.add_layout(&full_houses, true); ``` ``` // //println!("Inserted into the global DB"); ``` ``` //} ``` ``` //return; ``` ``` ``` ``` ``` 3 years ago ``` // Architecture: ``` ``` // Build `FULL_RANDOM_LAYOUTS` random layouts ``` ``` // loop { ``` ``` // Try combining `CUT_COMBINE_TOP_LAYOUTS` top layouts using vertical/horizontal cuts ``` ``` // Generate `WEIGHTED_RANDOM_LAYOUTS` layouts weighted by scores from existing layouts ``` ``` // } ``` ``` ``` ``` const FULL_RANDOM_LAYOUTS: usize = 100; ``` ``` const DB_CHOICE_PROBABILITY: f64 = 0.90; ``` ``` const WEIGHTED_RANDOM_LAYOUTS: usize = 200; ``` ``` const CUT_COMBINE_TOP_LAYOUTS: usize = 500; ``` ``` const IGNORED_WEIGHT_RATIO: f64 = 0.5; ``` ``` ``` ``` if subcity_db.layouts().len() == 0 { ``` ``` let mut full_random_layouts = Vec::new(); ``` ``` ``` ``` full_random_layouts.par_extend((0..FULL_RANDOM_LAYOUTS).into_par_iter().map(|i| { ``` ``` let seed: u64 = thread_rng().gen(); ``` ``` let mut rng = StdRng::seed_from_u64(seed); ``` ``` let mut layout = HouseLayout::new(subcity.city()); ``` ``` ``` ``` //eprintln!("Starting random population {}", i); ``` ``` population::populate_random(&mut layout, &mut rng); ``` ``` //eprintln!("Finished random init {}, price: {}, houses: {}", i, layout.price(), layout.houses().len()); ``` ``` optimization::iterate_improvements(&mut layout, &mut rng, false); ``` ``` //eprintln!("Finished iterated improvements {}, price: {}, houses: {}", i, layout.price(), layout.houses().len()); ``` ``` layout.houses().clone() ``` ``` })); ``` ``` ``` ``` for houses in &full_random_layouts { ``` ``` subcity_db.add_layout(houses, true); ``` ``` } ``` ``` ``` ``` eprintln!("Finished initial full random population"); ``` ``` } else { ``` ``` eprintln!("Skipping initial full random population because DB is non-empty [{} layouts]", subcity_db.layouts().len()); ``` ``` } ``` ``` ``` ``` let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); ``` ``` let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); ``` ``` eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len()); ``` ``` ``` ``` let mut cache = combine::CompatibilityCache::new(); ``` ``` ``` ``` // TODO: Deduplication of the DB ``` ``` loop { ``` ``` // This is a bottleneck when it comes to multithreading, it only runs on a single thread ``` ``` combine::iterate_combines(&mut subcity_db, CUT_COMBINE_TOP_LAYOUTS, subcity.city(), &mut cache, false); ``` ``` ``` ``` let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); ``` ``` let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); ``` ``` eprintln!("Finished cut combines"); ``` ``` eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len()); ``` ``` ``` ``` let mut weighted_random_layouts = Vec::new(); ``` ``` // Only using the underlying memory-based DB is required here as the SqliteLayoutDB ``` ``` // is not thread-safe. We are only reading in the parallel iterator, so that's fine. ``` ``` let memory_db = subcity_db.memory_db(); ``` ``` weighted_random_layouts.par_extend((0..WEIGHTED_RANDOM_LAYOUTS).into_par_iter().map(|i| { ``` ``` let seed: u64 = thread_rng().gen(); ``` ``` let mut rng = StdRng::seed_from_u64(seed); ``` ``` let mut layout = HouseLayout::new(subcity.city()); ``` ``` ``` ``` let price_range = worst_price - best_price; ``` ``` let max_price = best_price as f64 + (price_range as f64 * (1. - IGNORED_WEIGHT_RATIO)); ``` ``` //eprintln!("Starting random weighted population {}, using DB, score range {}-{}, DB use probability {}...", i, best_price, worst_price, DB_CHOICE_PROBABILITY); ``` ``` population::populate_using_db(&mut layout, &mut rng, memory_db, best_price as f64, max_price as f64, DB_CHOICE_PROBABILITY); ``` ``` //eprintln!("Finished random init {}, price: {}, houses: {}", i, layout.price(), layout.houses().len()); ``` ``` optimization::iterate_improvements(&mut layout, &mut rng, false); ``` ``` //eprintln!("Finished iterated improvements {}, price: {}, houses: {}", i, layout.price(), layout.houses().len()); ``` ``` layout.houses().clone() ``` ``` })); ``` ``` ``` ``` for houses in &weighted_random_layouts { ``` ``` subcity_db.add_layout(houses, true); ``` ``` } ``` ``` ``` ``` let w_best: u32 = weighted_random_layouts.iter().map(|houses| houses.iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); ``` ``` let w_worst: u32 = weighted_random_layouts.iter().map(|houses| houses.iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); ``` ``` let best_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).min().unwrap(); ``` ``` let worst_price: u32 = subcity_db.layouts().iter().map(|layout| layout.houses().iter().map(|h| subcity.city().get_price(*h) as u32).sum()).max().unwrap(); ``` ``` eprintln!("Finished weighted random population, price range {}-{}", w_best, w_worst); ``` ``` eprintln!("Best {}, worst {} [{} layouts]", best_price, worst_price, subcity_db.layouts().len()); ``` ``` } ``` ```} ```