Commit 45e52942 authored by tleydxdy's avatar tleydxdy

move cga implementation out

parent 00549e11
/*
* A Multi-Way Number Partitioning library
* Copyright 2019 Yunxiang Li
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use super::Cost;
/// Solve multi-way partitioning problem with k partitions using
/// complete greedy algorithm.
///
/// # Examples
///
/// ```
/// assert_eq!(
/// cga::solve(3, vec![8, 4, 7, 5, 6]),
/// vec![vec![8], vec![7, 4], vec![6, 5]]
/// );
/// ```
pub fn solve<T>(k: usize, mut input: Vec<T>) -> Vec<Vec<T>>
where
T: Cost + Copy,
{
// Deal with dumb inputs.
if k < 2 || input.len() == 0 {
return vec![input];
} else if input.len() <= k {
return input.drain(..).map(|x| vec![x]).collect();
}
let mut total = input.iter().map(|x| x.cost()).sum();
input.sort_unstable_by_key(|x| x.cost());
// First number always goes in the "first" bin, so don't need to search a tree.
let mut part = Solution::new();
let first = &input.pop().unwrap();
part.add(0, first);
_solve(k, input.iter().rev(), total, &mut total, &mut part)
.unwrap()
.drain()
.map(|(_, mut v)| v.drain(..).map(|&x| x).collect())
.collect()
}
fn _solve<T>(
k: usize,
mut input: impl Iterator<Item = T> + Clone + std::iter::ExactSizeIterator,
total: usize,
best_cost: &mut usize,
part: &mut Solution<T>,
) -> Option<Solution<T>>
where
T: Cost + Copy,
{
match input.next() {
Some(item) => {
let mut cur_best = None;
// If there's still empty bins, try the empty bin first.
if part.len() < k {
let key = part.add(part.len(), item);
match _solve(k, input.clone(), total, best_cost, part) {
Some(s) => {
if s.is_perfect(k, total) {
return Some(s);
}
*best_cost = s.cost();
cur_best = Some(s);
}
None => (),
}
part.del(key);
}
// Proceed if lowering the best is possible.
// Use ((a - 1) / b) + 1 for always round up division.
let cur_max = part.cost();
if ((total - cur_max - 1) / k) + 1 < *best_cost {
let keys: Vec<Key> = part.keys().map(|&k| k).collect();
// Try all the bins one by one, for the last i item they
// only need to be put in the i smallest bins. If all
// the bins are the same, just use the first one.
let until;
if keys[0].cost() == cur_max {
until = 1;
} else {
until = input.len() + 1;
}
for (_, &key) in (0..until).zip(keys.iter()) {
if key.cost() + item.cost() < *best_cost {
let key = part.push(key, item);
match _solve(k, input.clone(), total, best_cost, part) {
Some(s) => {
if s.is_perfect(k, total) {
return Some(s);
}
*best_cost = s.cost();
cur_best = Some(s);
}
None => (),
}
part.pop(key);
}
}
}
// return best found in all leaf
cur_best
}
None => {
// Leaf node, update best.
return Some(part.clone());
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Key(usize, usize);
impl Cost for Key {
fn cost(&self) -> usize {
self.0
}
}
use std::collections::btree_map::IntoIter;
use std::collections::btree_map::Keys;
use std::collections::BTreeMap;
struct Solution<T: Cost + Copy>(BTreeMap<Key, Vec<T>>);
impl<T> Solution<T>
where
T: Cost + Copy,
{
fn new() -> Self {
Self(BTreeMap::new())
}
fn add(&mut self, id: usize, item: T) -> Key {
let key = Key(item.cost(), id);
self.0.insert(key, vec![item]);
key
}
fn del(&mut self, key: Key) {
self.0.remove(&key);
}
fn push(&mut self, mut key: Key, item: T) -> Key {
let mut vec = self.0.remove(&key).unwrap();
key.0 += item.cost();
vec.push(item);
self.0.insert(key, vec);
key
}
fn pop(&mut self, mut key: Key) {
let mut vec = self.0.remove(&key).unwrap();
let item = vec.pop().unwrap();
key.0 -= item.cost();
self.0.insert(key, vec);
}
fn len(&self) -> usize {
self.0.len()
}
fn keys(&self) -> Keys<Key, Vec<T>> {
self.0.keys()
}
// BTreeMap doesn't have drain yet, see https://github.com/rust-lang/rust/issues/42849
fn drain(&mut self) -> IntoIter<Key, Vec<T>> {
std::mem::take(&mut self.0).into_iter()
}
// perfect partitions are either all the same, or the difference are 1
fn is_perfect(&self, k: usize, total: usize) -> bool {
self.cost() * k <= total + k
}
}
impl<T> Cost for Solution<T>
where
T: Cost + Copy,
{
// The cost of the biggest bin is the cost of the solution
fn cost(&self) -> usize {
self.keys().next_back().unwrap().cost()
}
}
impl<T> Clone for Solution<T>
where
T: Cost + Copy,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
......@@ -36,193 +36,7 @@ impl<T: Cost> Cost for &T {
}
}
pub mod cga {
use super::Cost;
use std::collections::btree_map::Keys;
use std::collections::btree_map::ValuesMut;
use std::collections::BTreeMap;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Key(usize, usize);
impl Cost for Key {
fn cost(&self) -> usize {
self.0
}
}
struct Solution<T: Cost + Copy>(BTreeMap<Key, Vec<T>>);
impl<T> Solution<T>
where
T: Cost + Copy,
{
fn new() -> Self {
Self(BTreeMap::new())
}
fn add_bin(&mut self, id: usize, item: T) -> Key {
let key = Key(item.cost(), id);
self.0.insert(key, vec![item]);
key
}
fn remove_bin(&mut self, key: Key) {
self.0.remove(&key);
}
fn push(&mut self, mut key: Key, item: T) -> Key {
let mut vec = self.0.remove(&key).unwrap();
key.0 += item.cost();
vec.push(item);
self.0.insert(key, vec);
key
}
fn pop(&mut self, mut key: Key) {
let mut vec = self.0.remove(&key).unwrap();
let item = vec.pop().unwrap();
key.0 -= item.cost();
self.0.insert(key, vec);
}
fn len(&self) -> usize {
self.0.len()
}
fn values_mut(&mut self) -> ValuesMut<Key, Vec<T>> {
self.0.values_mut()
}
fn keys(&self) -> Keys<Key, Vec<T>> {
self.0.keys()
}
// perfect partitions are either all the same, or the difference are 1
fn is_perfect(&self, k: usize, total: usize) -> bool {
self.cost() * k <= total + k
}
}
impl<T> Cost for Solution<T>
where
T: Cost + Copy,
{
// The cost of the biggest bin is the cost of the solution
fn cost(&self) -> usize {
self.keys().next_back().unwrap().cost()
}
}
impl<T> Clone for Solution<T>
where
T: Cost + Copy,
{
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
fn _solve<T>(
k: usize,
mut input: impl Iterator<Item = T> + Clone + std::iter::ExactSizeIterator,
total: usize,
best_cost: &mut usize,
part: &mut Solution<T>,
) -> Option<Solution<T>>
where
T: Cost + Copy,
{
match input.next() {
Some(item) => {
let mut cur_best = None;
// If there's still empty bins, try the empty bin first.
if part.len() < k {
let key = part.add_bin(part.len(), item);
match _solve(k, input.clone(), total, best_cost, part) {
Some(s) => {
if s.is_perfect(k, total) {
return Some(s);
}
*best_cost = s.cost();
cur_best = Some(s);
}
None => (),
}
part.remove_bin(key);
}
// Proceed if lowering the best is possible.
// Use ((a - 1) / b) + 1 for always round up division.
let cur_max = part.cost();
if ((total - cur_max - 1) / k) + 1 < *best_cost {
let keys: Vec<Key> = part.keys().map(|&k| k).collect();
// Try all the bins one by one, for the last i item they
// only need to be put in the i smallest bins. If all
// the bins are the same, just use the first one.
let cur_min = keys[0].cost();
let until;
if cur_min == cur_max {
until = 1;
} else {
until = input.len() + 1;
}
for (_, &key) in (0..until).zip(keys.iter()) {
if key.cost() + item.cost() < *best_cost {
let key = part.push(key, item);
match _solve(k, input.clone(), total, best_cost, part) {
Some(s) => {
if s.is_perfect(k, total) {
return Some(s);
}
*best_cost = s.cost();
cur_best = Some(s);
}
None => (),
}
part.pop(key);
}
}
}
// return best found in all leaf
cur_best
}
None => {
// Leaf node, update best.
return Some(part.clone());
}
}
}
pub fn solve<T>(k: usize, mut input: Vec<T>) -> Vec<Vec<T>>
where
T: Cost + Copy,
{
// Deal with dumb inputs.
if k == 1 || input.len() == 0 {
return vec![input];
} else if input.len() <= k {
return input.iter().map(|x| vec![*x]).collect();
}
let mut total = input.iter().map(|x| x.cost()).sum();
input.sort_unstable_by_key(|x| x.cost());
// First number always goes in the "first" bin, so don't need to search a tree.
let mut part = Solution::new();
let first = &input.pop().unwrap();
part.add_bin(0, first);
_solve(k, input.iter().rev(), total, &mut total, &mut part)
.unwrap()
.values_mut()
.map(|v| v.drain(..).map(|&x| x).collect())
.collect()
}
}
pub mod cga;
#[cfg(test)]
mod tests {
......@@ -308,6 +122,4 @@ mod tests {
)
});
}
/*
*/
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment