io.cloudex.framework.partition.builtin.BinPackingPartition.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudex.framework.partition.builtin.BinPackingPartition.java

Source

/**
 * The contents of this file may be used under the terms of the Apache License, Version 2.0
 * in which case, the provisions of the Apache License Version 2.0 are applicable instead of those above.
 *
 * Copyright 2014, Ecarf.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.cloudex.framework.partition.builtin;

import io.cloudex.framework.partition.PartitionFunction;
import io.cloudex.framework.partition.PartitionUtils;
import io.cloudex.framework.partition.entities.Item;
import io.cloudex.framework.partition.entities.Partition;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.Validate;

/**
 * A variation of the Bin Packing algorithm
 * 1- First fit
 * 2- Ordered first fit (First fit decreasing)
 * 3- Full Bin
 * 
 * This class also supports the setting of number of bins before. Used in scenarios where
 * a number of predefined bins should be used and we don't care about capacity.
 * 
 * This function needs the following keys set on the input
 * 
 * <ul>
 * <li>newBinPercentage - Double</li>
 * <li>maxBinItems - Long</li>
 * <li>numberOfBins - Integer</li>
 * </ul>
 * 
 * For more information about bin packing problems see: http://mathworld.wolfram.com/Bin-PackingProblem.html
 * @author Omer Dawelbeit (omerio)
 *
 */
public class BinPackingPartition implements PartitionFunction {

    /*public static final String NEW_BIN_PERCENTAGE = "newBinPercentage";
    public static final String MAX_BIN_ITEMS = "maxBinItems";
    public static final String NUMBER_OF_BINS = "numberOfBins";*/

    private List<? extends Item> items;// = new ArrayList<>();

    private Long maxBinItems;

    private Double newBinPercentage = 0.5;

    private Integer numberOfBins;

    /**
     * 
     */
    public BinPackingPartition() {
        super();
    }

    /**
     * @param items - the items to partition
     */
    public BinPackingPartition(List<Item> items) {
        super();
        this.items = items;
    }

    /* (non-Javadoc)
     * @see io.cloudex.framework.partition.PartitionFunction#setItems(java.util.List)
     */
    @Override
    public void setItems(List<? extends Item> items) {
        this.items = items;
    }

    @Override
    public List<Partition> partition() {

        Validate.notNull(this.items);

        // sort descending
        Collections.sort(items, Collections.reverseOrder());

        // have we got a maximum set?, otherwise use the size of the largest item
        Long max = (this.maxBinItems != null) ? this.maxBinItems : items.get(0).getWeight();

        PartitionUtils.setScale(items, max);

        long sum = PartitionUtils.sum(items, max);

        // check if the number of bins have already been set, in which case we have to fit everything in them
        if (this.numberOfBins == null) {
            // lower bound number of bin
            double numBins = (sum / (float) max);
            double numWholeBins = Math.floor(numBins);
            this.numberOfBins = (int) numWholeBins;
            double excess = numBins - numWholeBins;
            if (excess > newBinPercentage) {
                this.numberOfBins++;
            }

        } else {
            max = (long) Math.ceil(sum / (float) this.numberOfBins);
        }

        List<Partition> bins = new ArrayList<>();

        if (this.numberOfBins == 1) {

            Partition bin = new Partition();
            bins.add(bin);
            bin.addAll(items);
            items.clear();

        } else {

            // Mix of First Fit Decreasing (FFD) strategy and Full Bin strategy
            for (int i = 0; i < this.numberOfBins; i++) {
                Partition bin = new Partition();
                bins.add(bin);

                // check if we have ran out of items
                if (!items.isEmpty()) {

                    Item largestItem = items.get(0);

                    bin.add(largestItem);
                    items.remove(largestItem);

                    Long currentSum = bin.sum();

                    if (currentSum < max) {
                        Long diff = max - currentSum;
                        List<Item> toRemove = new ArrayList<>();

                        for (Item item : items) {
                            //Item item = items.get(j);
                            if (item.getWeight() <= diff) {

                                bin.add(item);
                                toRemove.add(item);

                                if (item.getWeight() == diff) {
                                    break;
                                } else {
                                    // look for an even small number to fill the gap
                                    diff = max - bin.sum();
                                }
                            }
                        }

                        items.removeAll(toRemove);

                    }
                }
                bin.calculateScale();
            }

            /*if(!items.isEmpty()) {
            bins.get(bins.size() - 1).addAll(items);
            items.clear();
            }*/

            // spread out remaining items, this approximate
            if (!items.isEmpty()) {
                //bins.get(bins.size() - 1).addAll(items);
                //items.clear();

                // items are in descending order
                // sort partitions in ascending order
                Collections.sort(bins);

                Partition smallest;
                long largestSum = bins.get(bins.size() - 1).sum();
                int index = 0;
                do {

                    smallest = bins.get(index);

                    // spread the remaining items into the bins, largest item into smallest bin
                    for (int i = 0; i < items.size(); i++) {

                        smallest.add(items.remove(i));

                        if (smallest.sum() > largestSum) {
                            break;
                        }
                    }

                    index++;

                } while (!items.isEmpty());

                items.clear();
            }

        }

        return bins;

    }

    /**
     * @param numberOfBins the numberOfBins to set
     */
    public void setNumberOfBins(Integer numberOfBins) {
        this.numberOfBins = numberOfBins;
    }

    /**
     * @param maxBinItems the maxBinItems to set
     */
    public void setMaxBinItems(Long maxBinItems) {
        this.maxBinItems = maxBinItems;
    }

    /**
     * @param newBinPercentage the newBinPercentage to set
     */
    public void setNewBinPercentage(Double newBinPercentage) {
        this.newBinPercentage = newBinPercentage;
    }

    /**
     * @return the items
     */
    public List<? extends Item> getItems() {
        return items;
    }

    /**
     * @return the maxBinItems
     */
    public Long getMaxBinItems() {
        return maxBinItems;
    }

    /**
     * @return the newBinPercentage
     */
    public Double getNewBinPercentage() {
        return newBinPercentage;
    }

    /**
     * @return the numberOfBins
     */
    public Integer getNumberOfBins() {
        return numberOfBins;
    }

}