- •Санкт-петербургский государственный политехнический университет
- •Введение Основные понятия теории принятия решений
- •Формализация задач принятия решений
- •Методы оценки многокритериальных альтернатив
- •Прямые методы
- •Аксиоматические методы
- •Методы компенсации
- •Точки и кривые безразличия
- •Методы сравнения разностей оценок альтернатив
- •Методы порогов несравнимости
- •Человеко-машинные процедуры принятия решений
- •Постановка задачи об упаковке.
- •Функция полезности.
- •Слои Парето.
- •Программа.
- •Структура.
- •Пример решения задачи.
- •Заключение
- •Список использованных источников
- •Приложение 1
- •Классы предметной области.
- •Полный исходный код программы.
Приложение 1
Классы предметной области.
При чтении кода рекомендуется обращать внимание на комментарии (текст после последовательностей «//» и между последовательностями «/**» и «*/»). Код, к которому относится конкретный комментарий, расположен ниже него.
Класс «Упаковываемый объект». Параметры этого класса – ID, объём, вес, оценки по пяти критериям и ссылка на парный объект.
/**
* Упаковываемый объект.
* @author AtemB
*
*/
public class Item {
/** ID. */
String id;
/** Объём. */
int volume;
/** Вес. */
int weight;
/** Критерий 1. */
int rate1;
/** Критерий 2. */
int rate2;
/** Критерий 3. */
int rate3;
/** Критерий 4. */
int rate4;
/** Критерий 5. */
int rate5;
/** Ссылка на парный объект. */
private Item pair;
public Item(String id,
int volume,
int weight,
int rate1,
int rate2,
int rate3,
int rate4,
int rate5) {
super();
this.id = id;
this.volume = volume;
this.weight = weight;
this.rate1 = rate1;
this.rate2 = rate2;
this.rate3 = rate3;
this.rate4 = rate4;
this.rate5 = rate5;
}
/***/
public Item() {
this.id = 0 + "";
this.volume = 0;
this.weight = 0;
this.rate1 = 0;
this.rate2 = 0;
this.rate3 = 0;
this.rate4 = 0;
this.rate5 = 0;
}
public String getId() {
return id;
}
public Item getPair() {
return pair;
}
public void setPair(Item pair) {
this.pair = pair;
}
public boolean hasPair() {
return pair != null;
}
/**
* @return the volume
*/
public int getVolume() {
return volume;
}
/**
* @return the weight
*/
public int getWeight() {
return weight;
}
/**
* @return the rate1
*/
public int getRate1() {
return rate1;
}
/**
* @return the rate2
*/
public int getRate2() {
return rate2;
}
/**
* @return the rate3
*/
public int getRate3() {
return rate3;
}
/**
* @return the rate4
*/
public int getRate4() {
return rate4;
}
/**
* @return the rate5
*/
public int getRate5() {
return rate5;
}
/**
* @param volume the volume to set
*/
public void setVolume(int volume) {
this.volume = volume;
}
/**
* @param weight the weight to set
*/
public void setWeight(int weight) {
this.weight = weight;
}
/**
* @param rate1 the rate1 to set
*/
public void setRate1(int rate1) {
this.rate1 = rate1;
}
/**
* @param rate2 the rate2 to set
*/
public void setRate2(int rate2) {
this.rate2 = rate2;
}
/**
* @param rate3 the rate3 to set
*/
public void setRate3(int rate3) {
this.rate3 = rate3;
}
/**
* @param rate4 the rate4 to set
*/
public void setRate4(int rate4) {
this.rate4 = rate4;
}
/**
* @param rate5 the rate5 to set
*/
public void setRate5(int rate5) {
this.rate5 = rate5;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Item)) {
return false;
}
Item other = (Item) obj;
if (rate1 != other.rate1) {
return false;
}
if (rate2 != other.rate2) {
return false;
}
if (rate3 != other.rate3) {
return false;
}
if (rate4 != other.rate4) {
return false;
}
if (rate5 != other.rate5) {
return false;
}
if (volume != other.volume) {
return false;
}
if (Double.doubleToLongBits(weight) != Double
.doubleToLongBits(other.weight)) {
return false;
}
return true;
}
@Override
public Item clone() {
Item clone = new Item(id, volume, weight, rate1, rate2, rate3, rate4, rate5);
return clone;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Объект" +
"\nID: " + id + ",\nОбъём: " + volume + ",\nВес: " + weight
+ ",\nКр. 1: " + rate1 + ",\nКр. 2: " + rate2 + ",\nКр. 3: " + rate3
+ ",\nКр. 4: " + rate4 + ",\nКр. 5" + rate5 + ",\nID парного объекта: " + pair.id;
}
Класс «Контейнер». Его параметры – это ID, объём и грузоподъёмность.
/**
* Контейнер.
* @author AtemB
*
*/
public class Container {
/** ID. */
private int id;
/** Объём. */
private int volume;
/** Грузоподъёмность. */
private int cargo;
public Container(int id, int volume, int cargo) {
this.id = id;
this.volume = volume;
this.cargo = cargo;
}
public int getVolume() {
return volume;
}
public int getCargo() {
return cargo;
}
public int getId() {
return id;
}
/**
* @param volume the volume to set
*/
public void setVolume(int volume) {
this.volume = volume;
}
/**
* @param cargo the cargo to set
*/
public void setCargo(int cargo) {
this.cargo = cargo;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
@Override
public Container clone() {
Container clone = new Container(id, volume, cargo);
return clone;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Контейнер" +
",\nID: " + id +
"\nОбъём: " + volume +
",\nГрузоподъёмность: " + cargo;
}
}
Класс ЛПР. У этого класса всего одно поле – список весов критериев, используемая им при расчёте полезности объекта. ЛПР умеет сравнивать объекты, используя вложенный класс «Бинарное отношение», умеет отображать пару объектов в один объект и получать значение полезности любого объекта. Для ЛПР в данном случае полезность объекта прямо пропорциональна значениям оценок по критериям объекта и обратно пропорциональна его массе и объёму.
/**
* ЛПР (лицо, принимающее решения).
* @author AtemB
*
*/
public class Boss {
class BossBinaryRelation implements BinaryRelation<Item> {
@Override
public Item compare(Item i1, Item i2) {
// Конечно, не самая пряморукая реализация, зато относительно понятна для восприятия.
// В этот список заносятся разности оценок по критериям.
List<Double> rates = new LinkedList<Double>();
rates.add((double) (i1.rate1 - i2.rate1));
rates.add((double) (i1.rate2 - i2.rate2));
// Флаги в этом списке означают, положителен или отрицателен
// результат сравнения критериев.
// Одинаковые значения в список не заносятся.
List<Boolean> flags = new LinkedList<Boolean>();
for (Double d: rates) {
if (d.doubleValue() > 0) {
flags.add(true);
} else if (d.doubleValue() < 0) {
flags.add(false);
}
}
// Если спиок флагов пуст, значит, объекты по проверяемым критериям равны.
if (flags.isEmpty()) {
return null;
} else {
boolean resultFlag = false;
for (int i = 1; i < flags.size(); i++) {
resultFlag = flags.get(i);
// Если в списке встречаются разные значения флагов, значит,
// объекты несравнимы по значимым критериям
// (один лучше по одним критериям, другой по другим).
if (flags.get(i - 1) != flags.get(i)) {
return null;
}
}
// Если предыдущий цикл выполнился до конца, значит,
// один из объектов оказался лучше другого.
// Проверяем значение результирующего флага
// и возвращаем доминирующий объект.
if (resultFlag) {
return i1;
} else {
return i2;
}
}
}
}
private List<Double> weights;
public Boss() {
weights = new ArrayList<Double>();
{
weights.add(0.45);
weights.add(0.45);
weights.add(0.04);
weights.add(0.04);
weights.add(0.02);
}
}
/**
* Получить полезность объекта.
* @param i Объект.
* @return Оценка объекта.
*/
public double getUtility(Item i) {
double ratesSum = i.rate1 * weights.get(0) +
i.rate2 * weights.get(1) +
i.rate3 * weights.get(2) +
i.rate4 * weights.get(3) +
i.rate5 * weights.get(4);
return ratesSum / i.weight / i.volume;//((i.weight + i.volume) / 2);
}
/**
* Отобразить пару объектов в один объект.
* @param i Любой объект из пары.
* @return Результирующий объект.
*/
public static Item mapPair(Item i) {
Item first = i;
Item second = i.getPair();
Item resultItem = new Item(first.id + ":" + second.id,
first.volume + second.volume,
first.weight + second.weight,
first.rate1 + second.rate1,
first.rate2 + second.rate2,
first.rate3 + second.rate3,
first.rate4 + second.rate4,
first.rate5 + second.rate5);
return resultItem;
}
/**
* Получить бинарное отношение, определённое ЛПР.
* @return Бинарное отношение.
*/
public BinaryRelation<Item> getBinaryRelation() {
return new BossBinaryRelation();
}
}
Класс «Склад». Его поля – список упаковываемых объектов, список контейнеров, сформированные слои Парето (при создании объекта это поле равно null), карта упакованных объектов и остаток, представляющий из себя список объектов. Также есть поле типа java.math.Random, использующееся при инициализации упаковываемых объектов и контейнеров.
/**
* Склад.
* @author AtemB
*
*/
public class Store {
class MapKeysComparator implements Comparator<Container> {
@Override
public int compare(Container c1, Container c2) {
return c1.getId() - c2.getId();
}
}
private List<Item> items;
private List<Container> containers;
private Random r = new Random();
private List<List<Item>> paretoSet;
private SortedMap<Container, List<Item>> map;
private List<Item> rest;
public Store() {
containers = new ArrayList<Container>();
items = new ArrayList<Item>();
rest = new ArrayList<Item>();
map = new TreeMap<Container, List<Item>>(new MapKeysComparator());
}
public void createContainers(int containersNum, ContainerTemplate ct, int maxVolume, int maxCargo) {
Random r = new Random();
int volumeDiapasonLength = maxVolume - ct.getVolume().getBegin();
int cargoDiapasonLength = maxCargo - ct.getCargo().getBegin();
for (int i = 0; i < containersNum; i++) {
containers.add(new Container(i,
r.nextInt(volumeDiapasonLength + 1) + ct.getVolume().getBegin(),
r.nextInt(cargoDiapasonLength + 1) + ct.getCargo().getBegin()));
}
for (Container c: containers) {
map.put(c, new LinkedList<Item>());
}
}
public void createItems(int itemsNum, ItemTemplate it, int maxVolume, int maxWeight) {
int volumeDiapasonLength = maxVolume - it.getVolume().getBegin();
int weightDiapasonLength = maxWeight - it.getWeight().getBegin();
int r1DiapasonLength = it.getRate1().getEnd() - it.getRate1().getBegin();
int r2DiapasonLength = it.getRate2().getEnd() - it.getRate2().getBegin();
int r3DiapasonLength = it.getRate3().getEnd() - it.getRate3().getBegin();
int r4DiapasonLength = it.getRate4().getEnd() - it.getRate4().getBegin();
int r5DiapasonLength = it.getRate5().getEnd() - it.getRate5().getBegin();
for (int i = 0; i < itemsNum; i++) {
items.add(new Item(i + "", r.nextInt(volumeDiapasonLength + 1) + it.getVolume().getBegin(),
r.nextInt(weightDiapasonLength + 1) + it.getWeight().getBegin(),
r.nextInt(r1DiapasonLength + 1) + it.getRate1().getBegin(),
r.nextInt(r2DiapasonLength + 1) + it.getRate2().getBegin(),
r.nextInt(r3DiapasonLength + 1) + it.getRate3().getBegin(),
r.nextInt(r4DiapasonLength + 1) + it.getRate4().getBegin(),
r.nextInt(r5DiapasonLength + 1) + it.getRate5().getBegin()));
}
}
/**
* @return the containers
*/
public List<Container> getContainers() {
return containers;
}
/**
* @return the items
*/
public List<Item> getItemsClones() {
List<SimpleEntry<String, String>> itemPairs = new ArrayList<SimpleEntry<String, String>>();
List<Item> newItems = new ArrayList<Item>();
for (Item i: items) {
newItems.add(i.clone());
if (i.hasPair()) {
itemPairs.add(new SimpleEntry<String, String>(i.getId(), i.getPair().getId()));
}
}
for (Entry<String, String> e: itemPairs) {
getItemFromListByID(e.getKey(), newItems).setPair(getItemFromListByID(e.getValue(), newItems));
}
return newItems;
}
public List<Item> getItemsInstance() {
return items;
}
public void setEmpty() {
items.clear();
containers.clear();
}
/**
* @return the paretoSet
*/
public List<List<Item>> getParetoSetInstance() {
return paretoSet;
}
/**
* @return the paretoSet
*/
public List<List<Item>> getParetoSetClone() {
List<List<Item>> clone = new ArrayList<List<Item>>();
List<SimpleEntry<String, String>> itemPairs = new ArrayList<SimpleEntry<String, String>>();
for (List<Item> paretoLayer: paretoSet) {
List<Item> newParetoLayer = new ArrayList<Item>();
for (Item i: paretoLayer) {
newParetoLayer.add(i.clone());
if (i.hasPair()) {
itemPairs.add(new SimpleEntry<String, String>(i.getId(), i.getPair().getId()));
}
}
clone.add(newParetoLayer);
}
for (Entry<String, String> e: itemPairs) {
getItemFromParetoSetByID(e.getKey(), clone).setPair(getItemFromParetoSetByID(e.getValue(), clone));
}
return clone;
}
/**
* @param paretoSet the paretoSet to set
*/
public void setParetoSet(List<List<Item>> paretoSet) {
this.paretoSet = paretoSet;
}
private Item getItemFromListByID(String id, List<Item> items) {
for (Item i: items) {
if (i.getId().equals(id)) {
return i;
}
}
return null;
}
private Item getItemFromParetoSetByID(String id, List<List<Item>> paretoSet) {
for (List<Item> items: paretoSet) {
for (Item i: items) {
if (i.getId().equals(id)) {
return i;
}
}
}
return null;
}
/**
* @return the map
*/
public SortedMap<Container, List<Item>> getMapInstance() {
return map;
}
/**
* @return the map
*/
public SortedMap<Container, List<Item>> getMapClone() {
SortedMap<Container, List<Item>> clone = new TreeMap<Container, List<Item>>(new MapKeysComparator());
Set<Entry<Container, List<Item>>> set = map.entrySet();
for (Entry<Container, List<Item>> e: set) {
List<Item> items = new ArrayList<Item>();
for (Item i: e.getValue()) {
items.add(i.clone());
}
clone.put(e.getKey().clone(), items);
}
return clone;
}
/**
* @param map the map to set
*/
public void setMap(SortedMap<Container, List<Item>> map) {
this.map = map;
}
public boolean paretoSetIsEmpty() {
boolean result = false;
return result;
}
/**
* @return the rest
*/
public List<Item> getRest() {
return rest;
}
/**
* @param rest the rest to set
*/
public void setRest(List<Item> rest) {
this.rest = rest;
}
}
Класс Упаковщик. Поля этого класса – ЛПР и Склад. Также в нём объявлены два перечисления, в которых указаны тип сортировки объектов и алгоритм упаковки. Этот класс оперирует с упаковываемыми объектами на основе информации ЛПР (обрабатывает парные объекты, создаёт слои Парето) , сортирует указанным образом, и упаковывает объекты.
/**
* Упаковщик.
* @author AtemB
*
*/
public class Packer {
/**
* Алгоритм упаковки.
* @author AtemB
*
*/
public enum ALGORYTHM {
/** В первый подходящий. */
FIRST,
/** В первый подходящий с убыванием. */
FIRST_DECREASE,
/** В лучший из подходящих. */
BEST,
/** В лучший из подходящих с убыванием. */
BEST_DECREASE
}
/**
* Тип сортировки.
* @author AtemB
*
*/
public enum SORT_TYPE {
/** По весу. */
WEIGHT,
/** По объёму. */
VOLUME,
/** По полезности. */
UTILITY,
/** По величине. */
SIZE
}
private Store store;
private Boss boss;
public Packer(Store store, Boss boss) {
this.store = store;
this.boss = boss;
}
public void breakPair(Item i1, Item i2) {
i1.setPair(null);
i2.setPair(null);
}
public void createPair(Item i1, Item i2) {
i1.setPair(i2);
i2.setPair(i1);
}
/**
* Обработать парные объекты.
* @param items Исходное множество объектов.
* @return Результирующее множество объектов.
*/
public List<Item> processPairs(List<Item> items) {
List<Item> listForRemove = new ArrayList<Item>();
// Отображаем все пары в монолитные объекты.
for (int i = 0; i < items.size(); i++) {
Item current = items.get(i);
if (current.hasPair()) {
// Помещаем парный текущему объект в список на удаление,
// т.к. теперь его параметры будут отражены в результирующем объекте.
listForRemove.add(current.getPair());
Item resultItem = Boss.mapPair(current);
// Замещаем исходный объект по текущему индексу результирующим.
items.set(i, resultItem);
current.getPair().setPair(null);
}
}
// Удаляем парные объекты из списка.
for (Item i: listForRemove) {
items.remove(i);
}
return items;
}
/**
* Создать слой Парето.
* @param boss ЛПР.
* @param items Исходное множество.
* @return Слой парето.
*/
public List<Item> extractParetoLayer(List<Item> items) {
BinaryRelation<Item> relation = boss.getBinaryRelation();
if (items.isEmpty()) {
return null;
} else {
// Сначала извлекаем первый попавшийся недоминируемый объект на исходном множестве.
List<Item> bestItems = new ArrayList<Item>();
Item best = items.get(0);
for (Item i: items) {
// Сравниваем текущий элемент с каждым из элементов последовательности.
// Если новый элемент лучше текущего - инициализируем им ссылку на лучший объект.
if (relation.compare(best, i) == i) {
best = i;
}
}
// Удаляем недоминируемый объект из исходного множества.
items.remove(best);
// Добавляем его в слой Парето.
bestItems.add(best);
// Теперь нужно найти все несравнимые с ним элементы исходного множества.
List<Item> equalItems = new ArrayList<Item>();
for (Item i: items) {
// Если очередной элемент несравним с ранее полученным лучшим,
// добавляем его в результирующее множество..
if (relation.compare(best, i) == null) {
equalItems.add(i);
}
}
// Удаляем из исходного множества все ссылки на недоминируемые объекты.
for (Item i: equalItems) {
items.remove(i);
}
// Добавляем недоминируемые объекты в слой Парето.
for (Item i: equalItems) {
bestItems.add(i);
}
return bestItems;
}
}
/**
* Получить множества (слои) Парето.
* @param boss ЛПР.
* @param items Исходное множество.
* @return Слои Парето.
*/
public List<List<Item>> createParetoSet(List<Item> items) {
List<List<Item>> layers = new ArrayList<List<Item>>();
while (!(items.isEmpty())) {
List<Item> paretoLayer = extractParetoLayer(items);
layers.add(paretoLayer);
}
return layers;
}
/**
* Упаковать очередной слой Парето. Инкапсулирует логику алгоритма упаковки.
* @param map Карта, содержащая информацию о контейнерах и об упакованных объектах.
* @param paretoLayer Слой Парето.
* @return Ту же карту с очередным добавленным слоем.
*/
public SortedMap<Container, List<Item>> pack(SortedMap<Container, List<Item>> map, List<Item> paretoLayer, ALGORYTHM algorythm) {
// Получаем массу и объём слоя Парето.
double layerWeight = 0.0;
double layerVolume = 0.0;
for (Item i: paretoLayer) {
layerWeight += i.weight;
layerVolume += i.volume;
}
if (algorythm == ALGORYTHM.FIRST_DECREASE || algorythm == ALGORYTHM.BEST_DECREASE) {
sort(paretoLayer, SORT_TYPE.SIZE);
}
// Если он в полном составе не упаковывается, его нужно сортировать по эффективности.
if (layerWeight > getFreeCargo(map) ||
layerVolume > getFreeSpace(map)) {
sort(paretoLayer, Packer.SORT_TYPE.UTILITY);
}
List<Item> forRemove = new ArrayList<Item>();
for (Item item: paretoLayer) {
if (!tryToPack(map, item, algorythm)) {
store.getRest().add(item);
forRemove.add(item);
}
}
for (Item i: forRemove) {
paretoLayer.remove(i);
}
return map;
}
/**
* Попробовать паковать объект.
* @param map
* @param item Объект.
* @return Флаг удачного завершения операции.
*/
private boolean tryToPack(SortedMap<Container, List<Item>> map, Item item, ALGORYTHM algorythm) {
Set<Container> containers = map.keySet();
if (algorythm == ALGORYTHM.BEST || algorythm == ALGORYTHM.BEST_DECREASE) {
Container bestContainer = getBest(containers, item, map);
if (bestContainer == null) {
return false;
} else {
map.get(bestContainer).add(item);
return true;
}
}
for (Container container: containers) {
if (canPack(container, item, map)) {
map.get(container).add(item);
return true;
}
}
return false;
}
/**
* Получить свободное место в контейнерах.
* @param map Карта, содержащая информацию о контейнерах и об упакованных объектах.
* @return Свободное место.
*/
private double getFreeSpace(SortedMap<Container, List<Item>> map) {
double freeSpace = 0.0;
for (Container c: map.keySet()) {
double itemsVolume = 0.0;
for (Item i: map.get(c)) {
itemsVolume += i.volume;
}
freeSpace += (c.getVolume() - itemsVolume);
}
return freeSpace;
}
/**
* Получить оставшуюся грузоподъёмность контейнеров.
* @param map Карта, содержащая информацию о контейнерах и об упакованных объектах.
* @return Оставшуюся грузоподъёмность контейнеров.
*/
private double getFreeCargo(SortedMap<Container, List<Item>> map) {
double freeCargo = 0.0;
for (Container c: map.keySet()) {
double itemsWeight = 0.0;
for (Item i: map.get(c)) {
itemsWeight += i.weight;
}
freeCargo += (c.getCargo() - itemsWeight);
}
return freeCargo;
}
/**
* Проверить возможность упаковки объекта в контейнер.
* @param c Контейнер.
* @param i Объект.
* @param map Карта, содержащая информацию об упакованных объектах.
* @return Флаг проверки.
*/
private boolean canPack(Container c, Item i, SortedMap<Container, List<Item>> map) {
// Получаем список объектов, упакованных в этот контейнер.
List<Item> items = map.get(c);
int totalVolume = 0;
double totalWeight = 0.0;
// Получаем суммарный вес и объём объектов, упакованных в этот контейнер.
for (Item item: items) {
totalVolume += item.volume;
totalWeight += item.weight;
}
// Если масса или объём проверяемого объекта
// больше оставшейся грузоподъёмности
// или свободного места в контейнере,
// объект в него упаковать нельзя.
if (c.getVolume() - totalVolume < i.volume ||
c.getCargo() - totalWeight < i.weight) {
return false;
}
return true;
}
/**
* Создать парные объекты на исходном множестве.
* @param pairsNum Необходимое количество пар.
* @param items Исходное множество.
*/
public void createPairs(int pairsNum, List<Item> items) {
Random r = new Random();
int pairCounter = 0;
while (pairCounter < pairsNum) {
findPair(items.get(r.nextInt(items.size())), items);
pairCounter = countPairs(items);
}
}
/**
* Найти парный объект.
* @param i Объект, для которого ищется парный объект.
* @param items Исходное множество.
*/
private void findPair(Item i, List<Item> items) {
if (!(i.hasPair())) {
Random r = new Random();
Item pair;
do {
pair = items.get(r.nextInt(items.size()));
} while (pair.hasPair() || pair == i);
i.setPair(pair);
pair.setPair(i);
}
}
/**
* Сосчитать парные объекты.
* @param items Исходное множество.
* @return Количество пар.
*/
public int countPairs(List<Item> items) {
int pairsCounter = 0;
for (Item i: items) {
if (i.hasPair()) {
pairsCounter++;
}
}
return pairsCounter / 2;
}
/**
* Извлечь наиболее тяжёлый объект.<p>
* Полученный объект удаляется из списка.
* @param items Список объектов.
* @return Самый тяжёлый объект.
*/
public Item extractHardest(List<Item> items) {
Item hardest = items.get(0);
for (Item i: items) {
if (i.weight > hardest.weight) {
hardest = i;
}
}
items.remove(hardest);
return hardest;
}
/**
* Извлечь наиболее объёмный объект.<p>
* Полученный объект удаляется из списка.
* @param items Список объектов.
* @return Самый объёмный объект.
*/
public Item extractLargest(List<Item> items) {
Item largest = items.get(0);
for (Item i: items) {
if (i.volume > largest.volume) {
largest = i;
}
}
items.remove(largest);
return largest;
}
public void sort(List<Item> items, SORT_TYPE... template) {
int itemsSize = items.size();
int templateLength = template.length;
for (int i = 0; i < itemsSize; i++) {
SORT_TYPE type = template[i % templateLength];
for (int j = i; j < itemsSize; j++) {
for (int k = i; k < itemsSize; k++) {
switch (type) {
case VOLUME:
if (items.get(j).volume < items.get(k).volume) {
Item buffer = items.get(j);
items.set(j, items.get(k));
items.set(k, buffer);
}
break;
case WEIGHT:
if (items.get(j).weight < items.get(k).weight) {
Item buffer = items.get(j);
items.set(j, items.get(k));
items.set(k, buffer);
}
break;
case UTILITY:
if (boss.getUtility(items.get(j)) < boss.getUtility(items.get(k))) {
Item buffer = items.get(j);
items.set(j, items.get(k));
items.set(k, buffer);
}
break;
case SIZE:
if (countSize(items.get(j)) > countSize(items.get(k))) {
Item buffer = items.get(j);
items.set(j, items.get(k));
items.set(k, buffer);
}
break;
default:
break;
}
}
}
}
}
/**
* Рассчитать размер. Расчёт производится по массе и объёму.
* @param i Объект .
*/
private double countSize(Item i) {
return i.volume * i.weight;
}
/**
* Получить лучший контейнер из подходящих.<p>
* Лучшим контейнер с минимальным свободным местом (грузоподъёмность или объём) под упаковку объекта.
* @param containers Контейнеры.
* @param i Объект.
* @param map Карта упаковки.
* @return Лучший контейнер.
*/
private Container getBest(Collection<Container> containers, Item i, SortedMap<Container, List<Item>> map) {
return getValid(getValidContainers(containers, i, map), i, map);
}
/**
* Получить контейнеры, пригодные для упаковки объекта.
* @param containers Контейнеры.
* @param i Объект.
* @param map Карта упаковки.
* @return Список контейнеров, в которые можно упаковать объект.
*/
private List<Container> getValidContainers(Collection<Container> containers, Item i, SortedMap<Container, List<Item>> map) {
List<Container> validContainers = new ArrayList<Container>();
for (Container c: containers) {
if (canPack(c, i, map)) {
validContainers.add(c);
}
}
return validContainers;
}
/**
* Получить контейнер, в котором осталось наименьшее количество места для упаковки объекта.
* @param containers Контейнеры.
* @param i Объект.
* @param map Карта упаковки.
* @return Контейнер.
*/
private Container getValid(List<Container> containers, Item i, SortedMap<Container, List<Item>> map) {
double minimalRests = 0;
for (Container c: containers) {
double rests = getRests(c, i, map);
if (rests > minimalRests) {
minimalRests += rests;
}
}
for (Container c: containers) {
double rests = getRests(c, i, map);
if (rests == 0) {
return c;
}
if (rests < minimalRests) {
minimalRests = rests;
}
}
for (Container c: containers) {
if (getRests(c, i, map) == minimalRests) {
return c;
}
}
return null;
}
/**
* Получить остаток.<p>
* Остатком считается наименьшее значение из остатка по грузоподъёмности или остатка по объёму.
* @param c Контейнер.
* @param i Объект.
* @param map Карта упаковки.
* @return Остаток.
*/
private double getRests(Container c, Item i, SortedMap<Container, List<Item>> map) {
double totalVolume = 0;
double totalWeight = 0;
for (Item item: map.get(c)) {
totalVolume += item.volume;
totalWeight += item.weight;
}
double volumeRests = c.getVolume() - totalVolume;
double cargoRests = c.getCargo() - totalWeight;
if (volumeRests < cargoRests) {
return volumeRests;
} else if (cargoRests < volumeRests) {
return cargoRests;
}
return 0;
}
}
Также в программе присутствуют классы графического интерфейса и отдельный класс, содержащий метод main().
