揹包問題求解Java代碼

package com.pan.test.domain;


import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.assertj.core.util.Lists;

import java.io.Serializable;
import java.util.*;

public class AlgorithmTest {


    public static void main(String[] args) {


        //eg1:揹包問題,限定重量 5kg .
        dpAlgorithm();

        //eg1:多集合交集覆蓋範圍問題.


    }


    /**
     * 動態規劃:揹包問題,限定重量 5kg ,選取價值最大的商品組合.(可以重複選取)
     * <p>
     * 問題分析:該類問題,選取個數無限制,唯一限制的是重量(權重).
     * 對於該類問題,使用動態規劃的原理是,每個因子,都是可以替換的.
     * 故而可以採用遞歸選優的方式,層級遞歸.(這裏可以用循環代替,但思想是層級遞歸)
     * 也就是說,第一層與第二層在限定條件下,選取最優解,選取出來的最優解與第三層比較,選取更優解.
     * 直到所有因子選完,則該解爲最優解.
     * <p>
     * ps:此類算法無法解決球隊選球員問題.及多集合選取最優解,每個集合只能選取一個.不能解決此問題的原因是,一個球隊是一個整體,
     * 這個整體的最優解,來源依賴每一個位置的集合.而每個位置只能有一個人.他的權重比值是分佈在整個球員身上.
     * 也無法採用迪克斯特拉算法,因爲這裏存在限制條件,而這個限制條件是整個球隊的.所以球員的限制條件其實是不確定的.如果採用迪克斯特拉算法,
     * 那麼,選取出來的路徑,可能走不到終點.
     */
    public static void dpAlgorithm() {

        //待選商品
        Product banana = new Product("香蕉", 7, 1);
        Product apple = new Product("蘋果", 15, 2);
        Product orange = new Product("桔子", 24, 3);
        Product durian = new Product("榴蓮", 31, 4);
        List<Product> products = Lists.newArrayList(apple, banana, durian, orange);
        //100個揹包
        for (int i = 0; i < 100; i++) {
            Bag bag = new Bag("小揹包" + i, 3 + i, new ArrayList<>(), 0, 0);
            for (int j = 0; j < products.size(); j++) {
                Product product = products.get(j);
                bag = fillBag(bag, product, products);
            }
            System.out.println("揹包:" + bag.getBagName() + "\t 總價:" + bag.getSumPrice() + "\t 限重:" + bag.getMaxWeight() + "\t 總重量:" + bag.getSumWeight());
            //輸出選取商品名
            String pickName = null;
            List<Product> bagProducts = bag.getProducts();
            for (int j = 0; j < bagProducts.size(); j++) {
                Product product = bagProducts.get(j);
                if (pickName == null) {
                    pickName = product.getName();
                    continue;
                }
                pickName += "-" + product.getName();
            }
            System.out.println("揹包:" + bag.getBagName() + "\t 挑選的商品:" + pickName);
        }
    }


    /**
     * 將商品放入揹包
     *
     * @param bag
     * @param product
     * @param products
     * @return
     */
    public static Bag fillBag(Bag bag, Product product, List<Product> products) {
        Map<Float, Product> productMap = setupProductMap(products);
        float minWeight = products.stream().min(Comparator.comparing(Product::getWeight)).get().getWeight();
        float maxWeight = bag.getMaxWeight();
        List<Product> pickList = bag.getProducts();
        float sumPrice = bag.getSumPrice();
        float sumWeight = bag.getSumWeight();
        //第一次塞入
        if (CollectionUtils.isEmpty(pickList)) {
            while (sumWeight <= maxWeight - product.getWeight()) {
                pickList.add(product);
                sumWeight += product.getWeight();
                sumPrice += product.getPrice();
            }
        } else {
            //替換
            List<Product> needAdd = new ArrayList<>();
            List<Product> needRemove = new ArrayList<>();
            List<Product> subRemove = new ArrayList<>();
            float removeWeight = 0;
            float removePrice = 0;

            for (int i = 0; i < pickList.size(); i++) {
                Product tmp = pickList.get(i);
                //單個替換
                if (tmp.getPrice() / tmp.getWeight() < product.getPrice() / product.getWeight() && tmp.getWeight() + (maxWeight - sumWeight) >= product.getWeight()) {
                    sumWeight += product.getWeight() - tmp.getWeight();
                    sumPrice += product.getPrice() - tmp.getPrice();
                    needAdd.add(product);
                    needRemove.add(tmp);
                }
                //組合替換
                subRemove.add(tmp);
                removeWeight += tmp.getWeight();
                removePrice += tmp.getPrice();
                if (removePrice / removeWeight < product.getPrice() / product.getWeight() && removeWeight + (maxWeight - sumWeight) >= product.getWeight()) {
                    sumWeight += product.getWeight() - removeWeight;
                    sumPrice += product.getPrice() - removePrice;
                    needAdd.add(product);

                    needRemove.addAll(subRemove);
                    subRemove.clear();
                    removeWeight = 0;
                    removePrice = 0;
                }
                //剩餘空間塞值
                float surplus = maxWeight - sumWeight;
                Product surPlus = getSurPlus(products, productMap, surplus);
                while (surplus >= minWeight && surPlus != null) {
                    sumWeight += surPlus.getWeight();
                    sumPrice += surPlus.getPrice();
                    needAdd.add(surPlus);
                    surplus = maxWeight - sumWeight;
                    surPlus = getSurPlus(products, productMap, surplus);
                }
            }
            //刪除替換掉的
            Iterator<Product> removes = needRemove.iterator();
            Iterator<Product> pickIt = pickList.iterator();
            while (removes.hasNext()) {
                Product remove = removes.next();
                while (pickIt.hasNext()) {
                    Product pick = pickIt.next();
                    if (pick.equals(remove)) {
                        pickIt.remove();
                        removes.remove();
                        break;
                    }
                }
            }
            pickList.addAll(needAdd);
        }
        bag.setMaxWeight(maxWeight);
        bag.setSumPrice(sumPrice);
        bag.setSumWeight(sumWeight);
        bag.setProducts(pickList);
        return bag;
    }

    /**
     * 獲取指定可選重量下,最佳選擇的商品
     *
     * @param products
     * @param setupProductMap
     * @param surplus
     * @return
     */
    private static Product getSurPlus(List<Product> products, Map<Float, Product> setupProductMap, float surplus) {
        Product product = setupProductMap.get(surplus);
        if (product != null) {
            return product;
        }
        //重新查找
        for (int i = 0; i < products.size(); i++) {
            Product tmp = products.get(i);
            if (tmp.getWeight() <= surplus) {
                if (product == null) {
                    product = tmp;
                } else if (product.getPrice() < tmp.getPrice()) {
                    product = tmp;
                }
            }
        }
        setupProductMap.put(surplus, product);
        return product;
    }

    /**
     * 相同商品重量的最高價值商品map
     *
     * @param products
     * @return
     */
    public static Map<Float, Product> setupProductMap(List<Product> products) {
        Map<Float, Product> productMap = new HashMap<>();
        for (int i = 0; i < products.size(); i++) {
            Product tmp = products.get(i);
            Product old = productMap.get(tmp.getWeight());
            if (old == null) {
                productMap.put(tmp.getWeight(), tmp);
                continue;
            }
            if (old.getPrice() < tmp.getPrice()) {
                productMap.put(tmp.getWeight(), tmp);
                continue;
            }
        }
        return productMap;
    }


}


/**
 * 揹包實體類
 */
class Bag implements Serializable {

    private static final long serialVersionUID = 3554907573665360761L;

    public Bag(String bagName, float maxWeight, List<Product> products, float sumPrice, float sumWeight) {
        this.bagName = bagName;
        this.maxWeight = maxWeight;
        this.products = products;
        this.sumPrice = sumPrice;
        this.sumWeight = sumWeight;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (!(o instanceof Bag)) return false;

        Bag bag = (Bag) o;

        return new EqualsBuilder()
                .append(getMaxWeight(), bag.getMaxWeight())
                .append(getSumPrice(), bag.getSumPrice())
                .append(getSumWeight(), bag.getSumWeight())
                .append(getBagName(), bag.getBagName())
                .append(getProducts(), bag.getProducts())
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(getBagName())
                .append(getMaxWeight())
                .append(getProducts())
                .append(getSumPrice())
                .append(getSumWeight())
                .toHashCode();
    }

    private String bagName;

    private float maxWeight;

    private List<Product> products;

    private float sumPrice;
    private float sumWeight;


    public String getBagName() {
        return bagName;
    }

    public void setBagName(String bagName) {
        this.bagName = bagName;
    }

    public float getMaxWeight() {
        return maxWeight;
    }

    public void setMaxWeight(float maxWeight) {
        this.maxWeight = maxWeight;
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    public float getSumPrice() {
        return sumPrice;
    }

    public void setSumPrice(float sumPrice) {
        this.sumPrice = sumPrice;
    }

    public float getSumWeight() {
        return sumWeight;
    }

    public void setSumWeight(float sumWeight) {
        this.sumWeight = sumWeight;
    }


}

/**
 * 商品實體類
 *
 */
class Product implements Serializable {
    private static final long serialVersionUID = 5594269264643217406L;


    public Product(String name, float price, float weight) {
        this.name = name;
        this.price = price;
        this.weight = weight;
    }

    private String name;

    private float price;

    private float weight;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public float getWeight() {
        return weight;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章