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

Java tutorial

Introduction

Here is the source code for io.cloudex.framework.partition.builtin.BinPackingPartition1.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 java.util.Random;

import org.apache.commons.lang3.Validate;

/**
 * A variation of the Bin Packing algorithm
 * 1- First fit
 * 2- Ordered first fit (First fit decreasing)
 * 3- Balanced
 * No full bin in this function
 * 
 * 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 BinPackingPartition1 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 BinPackingPartition1() {
        super();
    }

    /**
     * @param items - the items to partition
     */
    public BinPackingPartition1(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());

        /*long seed = System.nanoTime();
        Collections.shuffle(items, new Random(seed));*/

        // 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 {

            // create all the bins
            for (int i = 0; i < this.numberOfBins; i++) {
                bins.add(new Partition(max));
            }

            List<Item> toRemove = new ArrayList<>();

            int binIndex = 0;

            for (Item item : items) {

                // iterate over bins and try to put the item into the first one it fits into

                Partition startBin = bins.get(binIndex);
                ;
                Partition currentBin = null;
                int count = 0;

                while (!toRemove.contains(item)) { // did we put the item in a bin?

                    currentBin = bins.get(binIndex);

                    if ((count != 0) && (currentBin == startBin)) {
                        // back where we started item did not fit in last bin. move on
                        break;

                    }

                    if (currentBin.addIfPossible(item)) {
                        // item fit in bin
                        toRemove.add(item);
                    }

                    // try next bin
                    binIndex++;

                    if (binIndex == bins.size()) {
                        // go back to the beginning
                        binIndex = 0;
                    }

                    count++;

                }
            }

            items.removeAll(toRemove);

            // 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);
                //Collections.sort(items, Collections.reverseOrder());

                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++;
                    // there is a large item we can't break
                    if (!items.isEmpty() && (index >= bins.size())) {
                        bins.get(index - 1).addAll(items);
                        items.clear();
                    }

                } while (!items.isEmpty());

                items.clear();
            }

            for (Partition bin : bins) {
                bin.calculateScale();
            }

        }

        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;
    }

}