Java Array Sorting based on multiple parameters - java

I have an array that I want to sort in ascending order. However, I want to sort them with reference to a boolean array.I would like to sort the values that are true in ascending order, followed by the values that are false in ascending order.
Little stuck on how to get there.
This is what I have currently:
Object[] arr = new Object[6];
arr[0] = new Object(2);
arr[1] = new Object(5);
arr[2] = new Object(3);
arr[3] = new Object(1);
arr[4] = new Object(6);
arr[5] = new Object(4);
Available[] avalarr = new Available[6];
availarr[0] = new Available (true);
availarr[1] = new Available (false);
availarr[2] = new Available (false);
availarr[3] = new Available (true);
availarr[4] = new Available (true);
availarr[5] = new Available (false);
I need the output to be:
1 2 6 3 4 5

Code:
import java.util.Arrays;
public class SelectiveSort {
public static void main(String[] args) {
Item [] items = new Item [6];
items[0] = new Item(2, true);
items[1] = new Item(5, false);
items[2] = new Item(3, false);
items[3] = new Item(1, true);
items[4] = new Item(6, true);
items[5] = new Item(4, false);
System.out.println("Before Sorting:");
// Removed enhanced for loop
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
// Sorting
Arrays.sort(items);
System.out.println("\n\nAfter Sorting:");
// Removed enhanced for loop
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
System.out.println();
}
}
class Item implements Comparable<Item> {
private int _intValue;
private boolean _boolValue;
public Item(int intValue, boolean boolValue) {
_intValue = intValue;
_boolValue = boolValue;
}
public int getIntValue() { return _intValue; }
public boolean getBoolValue() { return _boolValue; }
#Override
public int compareTo(Item otherItem) {
// Using explicit comparison
int boolComparison = (_boolValue == otherItem._boolValue) ? 0 :
(_boolValue) ? 1 : -1;
return (boolComparison != 0) ? -boolComparison :
( (_intValue == otherItem.getIntValue()) ? 0 :
(_intValue > otherItem.getIntValue()) ? 1 : -1);
}
}
Output:
Before Sorting:
2 5 3 1 6 4
After Sorting:
1 2 6 3 4 5
Explanation:
The idea is to let your "Item" implement Comparable, and override the compareTo(Item otherItem) function based on the desired order.
Once that is done, all you need to do is to call Arrays.sort() on your array of Item.
Version 2 (w/o Comparable/Comparator):
public class SelectiveSort {
public static void main(String[] args) {
Item [] items = new Item [6];
items[0] = new Item(2, true);
items[1] = new Item(5, false);
items[2] = new Item(3, false);
items[3] = new Item(1, true);
items[4] = new Item(6, true);
items[5] = new Item(4, false);
System.out.println("Before Sorting:");
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
// Sorting
bubbleSort(items);
System.out.println("\n\nAfter Sorting:");
for(int i = 0; i < items.length; i++) {
System.out.print(items[i].getIntValue() + " ");
}
System.out.println();
}
public static void bubbleSort(Item [] items) {
int n = items.length;
do {
int newN = 0;
for(int i = 1; i < n; i++) {
if(compareTo(items[i-1], items[i]) == 1) {
Item temp = items[i-1];
items[i-1] = items[i];
items[i] = temp;
newN = i;
}
}
n = newN;
} while (n != 0);
}
public static int compareTo(Item item1, Item item2) {
int boolComparison = (item1.getBoolValue() == item2.getBoolValue())
? 0 : (item1.getBoolValue()) ? 1 : -1;
return (boolComparison != 0) ? -boolComparison :
( (item1.getIntValue() == item2.getIntValue()) ? 0 :
(item1.getIntValue() > item2.getIntValue()) ? 1 : -1);
}
}

(To expand on my comment:
You need a basic "thing":
class Thing {
boolean newAvailable;
int order;
public Thing(boolean newAvailable, int order) {
...
}
}
...and a Comparable...
class CompareThings implements Comparator<Thing> {
...
int compare(Thing t1, Thing t2) {
if (t1.newAvailable!=t2.newAvailable)
return t1.newAvailable==true ? 1 : -1;
return t1.order-t2.order;
}
}
(Note that t1.newAvailable==true is redundant, but I think it clarifies what's going on here.)
Now build an array of Thing and call Arrays.sort(Thing[] things, CompareThings);

Related

ArrayOutOfBoundException while inserting elements

Getting ArrayIndexOutOfBoundException while inserting elements to a two Dimensional array..I dont know where the problem is..
I tried a lot but couldn't understand why this is giving the exception.Here is my code..Thanks in advance..
public class TruthTableAutoMateClass extends JPanel implements ActionListener
{
static int size;
private int i = 0;
static JButton btnOk = new JButton();
String[][] array= new String[size][];
private double[][] dim = { {0.50, 0.50}, {0.25, 0.20, 0.20, 0.20, TableLayout.FILL}};
public static void main(String args[])
{
TruthTableAutoMateClass auto = new TruthTableAutoMateClass();
checkAdding(auto);
btnOk.addActionListener(auto);
}
private static String[] prepareArray()
{
String[] arr = new String[2];
if (size == 16)
{
String text1 = field1.getText();
String text2 = field2.getText();
String text3 = field3.getText();
String text4 = field4.getText();
String firstArray = new StringBuilder().append(text1).append("|").append(text3).append("|").append(text3).append("|").append(text4).toString();
arr[0] = firstArray;
arr[1] = ledValue1.getText();
}
else if (size == 9)
{
}
else if (size == 4)
{
}
return arr;
}
private static void checkAdding(TruthTableAutoMateClass auto1)
{
TruthTableAutoMateClass auto = auto1;
String answer = JOptionPane.showInputDialog("How many values are there 4 or 3 " + "if 4 yes else press no");
if (answer.equalsIgnoreCase("yes"))
{
size = 4 * 4;
auto.add(lbl1, "0,0");
auto.add(lbl2, "0,1");
auto.add(lbl3, "0,2");
auto.add(lbl4, "0,3");
auto.add(field1, "1,0");
auto.add(field2, "1,1");
auto.add(field3, "1,2");
auto.add(field4, "1,3");
auto.add(ledValue1, "1,4");
/*auto.add(ledValue2);
auto.add(ledValue3);
auto.add(ledValue4);*/
auto.add(btnOk, "0,4");
}
else
{
size = 3 * 3;
String answer2 = JOptionPane.showInputDialog("Again enter yes or no" + "3 for yes , 2 for no");
if (answer2.equalsIgnoreCase("yes"))
{
auto.add(lbl1, "0,0");
auto.add(lbl2, "0,1");
auto.add(lbl3, "0,2");
auto.add(field1, "1,0");
auto.add(field2, "1,1");
auto.add(field3, "1,2");
auto.add(ledValue1, "1,4");
/* auto.add(ledValue2);
auto.add(ledValue3);*/
auto.add(btnOk, "0,4");
}
else
{
size = 2 * 2;
auto.add(lbl1, "0,0");
auto.add(lbl2, "0,1");
auto.add(field1, "1,0");
auto.add(field2, "1,1");
auto.add(ledValue1, "1,4");
/* auto.add(ledValue2);*/
auto.add(btnOk, "0,4");
}
}
}
#Override
public void actionPerformed(ActionEvent e)
{
array[i] = prepareArray();
i++;
System.out.println(Arrays.toString(array));
}}
At the main() method you are creating an instance of TruthTableAutoMateClass. When this one is creating, size = 0 (by default) and String[][] array= new String[size][]; creates an array with size 0.
So, your array had size 0 and you got an exception when you had tried to assign some value like this array[i] = prepareArray();.

Codename one calendar UpdateButtonDayDate() issue

I am working on my project where I want to show when application starts then calendar display, which date contain events, for instance if the date contain events, then the day button contains * symbol and day, And if the date doesn't contain any event then it only displays a day.
I wrote following code, but it only displays * symbol when I am clicking on that button, So how can I manage this code that display * symbol on the date which only contain events when the application starts or that page gonna be loaded.
Following is my code:-
public class Customised extends Calendar{
ArrayList<String[]> data = new ArrayList<>();
int i,j,columns;
#Override
protected void updateButtonDayDate(Button dayButton,int currentMonth, int day) {
dayButton.setText(""+day);
dayButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//Check which date having how many number of events===============================================================
try{
ShowEvent.removeAll();
cur = db.executeQuery("SELECT Event, Description from CalendarData WHERE Date = ? ", dateLabel.getText());
columns = cur.getColumnCount();
if(columns > 0) {
boolean next = cur.next();
if(next) {
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = cur.getColumnName(iter);
}
while(next) {
Row currentRow = cur.getRow();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = currentRow.getString(iter);
}
data.add(currentRowArray);
next = cur.next();
}
Object[][] arr = new Object[data.size()][];
data.toArray(arr);
}
}
}catch(IOException e){
e.printStackTrace();
}
for(i = 0 ; i< data.size(); i++){
Log.p(data.get(i)[0]);
}
Label a = new Label(dateLabel.getText());
Label b = new Label(" "+i);
Container container1 = TableLayout.encloseIn(2, a,b);
container1.setUIID("container1");
ShowEvent.add(container1);
for( i = 0 ; i< data.size(); i++){
for(j = 0; j<columns; j++){
Log.p(data.get(i)[j]);
SpanLabel spanData = new SpanLabel(data.get(i)[j]);
spanData.setUIID("SpanLabel");
ShowEvent.add(spanData);
}
Label space = new Label("=======================");
ShowEvent.add(space);
Log.p("###################");
}
data.clear();
if(i>0){
if(Dialog.show("Choose action", "What you want to do?", "Add Events","View Events")){
calendar.show();
}
else{
ShowEvent.show();
}
}else{
Dialog.show("Add event","There is no event to display, Please add events first","OK","");
}
//============================================================================================================
}
});
}
#Override
protected void initComponent(){
ArrayList<String[]> data1 = new ArrayList<>();
int k;
Log.p("initComponent");
try{
cur = db.executeQuery("select Date from CalendarData");
columns = cur.getColumnCount();
if(columns > 0) {
boolean next = cur.next();
if(next) {
String[] columnNames = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
columnNames[iter] = cur.getColumnName(iter);
}
while(next) {
Row currentRow = cur.getRow();
String[] currentRowArray = new String[columns];
for(int iter = 0 ; iter < columns ; iter++) {
currentRowArray[iter] = currentRow.getString(iter);
}
data1.add(currentRowArray);
next = cur.next();
}
Object[][] arr = new Object[data1.size()][];
data1.toArray(arr);
}
}
}catch(IOException e){
e.printStackTrace();
}
for(k = 0 ; k< data1.size(); k++){
Log.p(data1.get(k)[0]);
}
if(k>0){
//cal.setUIID("CalendarSelectedDay");
}
}
/*
#Override
protected boolean isInitialized(){
boolean result = false;
Log.p("isInitialised");
return result;
}*/
public Customised(){
}
#Override
protected Button createDay() {
Button day = new Button();
day.setAlignment(CENTER);
day.setUIID("CalendarDay1");
day.setEndsWith3Points(false);
day.setTickerEnabled(false);
return day;
}
}
And the expected result will be:-
That's because you placed the code inside the actionPerformed method which is only triggered upon Button pressed/released.
Move your code to the updateButtonDayDate scope

Get ranks to iterating ArrayList value

I have five values in an ArrayList like {50,25,50,30,10} . I want to set ranks in every value, So please tell me how I could do this. The output is like
50->1st rank
50->1st rank
30->2nd rank
20->3rd rank
10->4th rank
you should check this and this.
It is easy to do with lambda expression which is available from java 1.8.
Here is code i have got from above reference link
List<Player> players = new ArrayList<Player>() {{
add(new Player(1L, "a", 5));
add(new Player(2L, "b", 7));
add(new Player(3L, "c", 8));
add(new Player(4L, "d", 9));
add(new Player(5L, "e", 3));
add(new Player(6L, "f", 8));
}};
int[] score = {Integer.MIN_VALUE};
int[] no = {0};
int[] rank = {0};
List<Ranking> ranking = players.stream()
.sorted((a, b) -> b.getScores() - a.getScores())
.map(p -> {
++no[0];
if (score[0] != p.getScores()) rank[0] = no[0];
return new Ranking(rank[0], score[0] = p.getScores());
})
// .distinct() // if you want to remove duplicate rankings.
.collect(Collectors.toList());
System.out.println(ranking);
// result:
// rank=1, score=9
// rank=2, score=8
// rank=2, score=8
// rank=4, score=7
// rank=5, score=5
// rank=6, score=3
Please try this code.
package snakePack;
import java.util.ArrayList;
public class MainSnake {
public static void main(String[] args) {
Snake blackMambo = new Snake("blackMambo",2,0);
Snake rattle = new Snake("rattle",9,0);
Snake green = new Snake("green",6,0);
Snake cobra = new Snake("cobra",78,0);
Snake kingCobra = new Snake("kingCobra",5,0);
Snake whiteCobra = new Snake("whiteCobra",5,0);
Snake python = new Snake("python",5,0);
Snake yellow = new Snake("yellow",5,0);
Snake blackCobra = new Snake("blackCobra",1000,0);
Snake desertCobra = new Snake("desertCobra",5,0);
ArrayList<Snake> list = new ArrayList<Snake>();
list.add(blackMambo);list.add(rattle);list.add(green);list.add(cobra);list.add(kingCobra);
list.add(whiteCobra);list.add(python);list.add(yellow);list.add(blackCobra);list.add(desertCobra);
ArrayList<Integer> mongoosebigSnakes = new ArrayList<Integer>();
Integer tempHelperFrog = 0;
Integer rankMe = 0;
for (int grossHopper = 0; grossHopper < list.size(); grossHopper++) {
Integer strongSnake = 0;
for (int spider = 0; spider < list.size(); spider++) {
if (list.get(spider).getSnakePoisionRate() > strongSnake) {
boolean bool = false;
if (mongoosebigSnakes.size() != 0) {
for (int dragonFly = 0; dragonFly < mongoosebigSnakes.size(); dragonFly++) {
if (mongoosebigSnakes.get(dragonFly).intValue() == list.get(spider).getSnakePoisionRate().intValue()) {
bool = true;
}
}
if (bool != true) {
bool = false;
strongSnake = list.get(spider).getSnakePoisionRate();
}
} else {
if (tempHelperFrog != list.get(spider).getSnakePoisionRate()) {
strongSnake = list.get(spider).getSnakePoisionRate();
}
}
}
}
tempHelperFrog = strongSnake;
mongoosebigSnakes.add(strongSnake);
++rankMe;
for (int x = 0; x < list.size(); x++) {
if (strongSnake == list.get(x).getSnakePoisionRate()) {
list.get(x).setSnakeRank(rankMe);
}
}
}
System.out.println(" Hey guys get ready to see who has strong poison >>>");
Integer ratHelperCounter = 0;
for (Snake snake : list) {
System.out.println(++ratHelperCounter + " :" + snake.toString());
}
System.out.println("it's awesome" + " huge me !!!! ");
}
}
Bean class:
package snakePack;
public class Snake {
String snakeName;
Integer snakePoisionRate;
Integer snakeRank;
public Snake(String string, int i, int j) {
this.snakeName = "";
this.snakePoisionRate = i;
this.snakeRank = j;
}
public String getSnakeName() {
return snakeName;
}
public void setSnakeName(String snakeName) {
this.snakeName = snakeName;
}
public Integer getSnakePoisionRate() {
return snakePoisionRate;
}
public void setSnakePoisionRate(Integer snakePoisionRate) {
this.snakePoisionRate = snakePoisionRate;
}
public Integer getSnakeRank() {
return snakeRank;
}
public void setSnakeRank(Integer snakeRank) {
this.snakeRank = snakeRank;
}
#Override
public String toString() {
return "Snake [snakeName=" + snakeName + ", snakePoisionRate="
+ snakePoisionRate + ", snakeRank=" + snakeRank + "]";
}
}

Read range of values in an array with unkown dimension

I 'm looking for a way to read a range of elements in an array of unknown dimension ( not length).
The client can send a read request for an object and specify the range to read. The input String could be like this : "1:2:3:2,2:3:1:4" for example. This would mean he wants to read the elements in the range from [1][2][3][2] to [2][3][1][4] of an array.
To read a concrete element I created this function:
public Object readValue(Object obj,int[] positions ) {
Object value = null; //Result
int objDimension = getDimension(obj); //Dimesion of the array
System.out.println("Dimension: " + objDimension );
try {
Object[] aux = (Object[]) obj;
for (int i = 0; i < objDimension - 1; i++) {
int pos = positions[i];
aux = (Object[]) aux[pos];
}
value = aux[positions[objDimension - 1]];
System.out.println("Result: " + value);
} catch (ArrayIndexOutOfBoundsException e) {
// TODO: Send a fault to the client.
System.out.println("Error: "+e.getMessage());
}
return value;
}
public static int getDimension(Object value) {
Class<?> clazz = value.getClass();
String className = clazz.getName();
int dimension = 0;
for (int i = 0; i < className.length(); i++) {
if (className.charAt(i) != '[') {
dimension = i;
break;
}
}
return dimension;
}
//Example.
public static void main(String[] args) {
// TODO code application logic here
TestMultiDimensioNRead test = new TestMultiDimensioNRead();
Integer[][][][] testSubject = new Integer[5][2][4][];
testSubject[0][0][2] = new Integer[8];
testSubject[0][0][0] = new Integer[15];
testSubject[0][0][1] = new Integer[20];
testSubject[0][0][3] = new Integer[2];
testSubject[1][1][2] = new Integer[7];
testSubject[1][1][2][0] = 80;
test.readValue(testSubject,new int[]{1, 1, 2, 0});
}
I was thinking a good way may be to calculate the differens between each dimension length.
If anyone can come with a good idea, I would really appreciatee.
Thanks in advance.
EDIT 1: The code posted in this question does read the value of a given position in an array of unknown dimension. My problem is to read all the elements that are between to given points. This might not have been clear in the initial question.
You could use a recursive solution:
public class Test {
private class TestMultiDimensioNRead {
public Integer readValue(Object testSubject, int[] coordinates) {
return readValue(testSubject, coordinates, 0);
}
private Integer readValue(Object testSubject, int[] coordinates, int which) {
if (testSubject instanceof Object[]) {
Object[] subject = (Object[]) testSubject;
if (coordinates.length > which + 1) {
return readValue(subject[coordinates[which]], coordinates, which + 1);
} else {
return (Integer) subject[coordinates[which]];
}
} else {
// Throw some sort of exception?
return -1;
}
}
public Iterator<Integer> readValues(Object testSubject, int[] coordinates, int count) {
return readValues(testSubject, coordinates, count, 0);
}
private Iterator<Integer> readValues(Object testSubject, int[] coordinates, int count, int level) {
if (testSubject instanceof Object[]) {
Object[] subject = (Object[]) testSubject;
if (coordinates.length > level + 1) {
return readValues(subject[coordinates[level]], coordinates, count, level + 1);
} else {
return new Iterator<Integer>() {
int i = 0;
Integer[] intSubject = (Integer[]) subject;
#Override
public boolean hasNext() {
return i <= count;
}
#Override
public Integer next() {
return intSubject[coordinates[level] + (i++)];
}
};
}
} else {
// Throw some sort of exception?
return null;
}
}
}
public void test() {
TestMultiDimensioNRead test = new TestMultiDimensioNRead();
Integer[][][][] testSubject = new Integer[5][2][4][];
testSubject[0][0][2] = new Integer[8];
testSubject[0][0][0] = new Integer[15];
testSubject[0][0][1] = new Integer[20];
testSubject[0][0][3] = new Integer[2];
testSubject[1][1][2] = new Integer[7];
testSubject[1][1][2][0] = 80;
testSubject[1][1][2][1] = 79;
testSubject[1][1][2][2] = 78;
Iterator<Integer> them = test.readValues(testSubject, new int[]{1, 1, 2, 0}, 3);
for (Integer x = them.next(); them.hasNext(); x = them.next()) {
System.out.println(x);
}
System.out.println();
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
Prints 80 as expected.
There's probably more to do in terms of sanity checks but this seems to work.
Found the way to do it, maybe it's helpfull at somepoint for someone.
I didn't include any checks, this is more a test case to see that is works.
public class TestReadMultiDimensionArray {
private int[] startPosition; //Start position.
private int[] endPosition; //End position.
private boolean inRange = false; //If the current position is in range.
private List<Object> result; //List to store the values we find.
public TestReadMultiDimensionArray() {
result = new ArrayList<>();
}
public static void main(String[] args) {
TestReadMultiDimensionArray test = new TestReadMultiDimensionArray();
Integer[][][][] testSubject = new Integer[2][2][4][];
//(0,0,y,z)
testSubject[0][0][0] = new Integer[]{1}; //(0,0,0,0)
testSubject[0][0][1] = new Integer[]{2}; //(0,0,1,0)
testSubject[0][0][2] = new Integer[]{3}; //(0,0,2,0)
testSubject[0][0][3] = new Integer[]{4}; //(0,0,3,0)
//(0,1,y,z)
testSubject[0][1][0] = new Integer[]{5}; //(0,1,0,0)
testSubject[0][1][1] = new Integer[]{6}; //(0,1,1,0)
testSubject[0][1][2] = new Integer[]{7, 8, 9}; //(0,1,2,0) (0,1,2,1) (0,1,2,2)
testSubject[0][1][3] = new Integer[]{10}; //(0,1,3,0)
//(1,0,y,z)
testSubject[1][0][0] = new Integer[]{11, 12}; //(1,0,0,0)..
testSubject[1][0][1] = new Integer[]{13, 14, 15};
testSubject[1][0][2] = new Integer[]{16, 17, 18};
testSubject[1][0][3] = new Integer[]{19, 20, 21}; //..(1,0,3,2)
//(1,1,y,z)
testSubject[1][1][0] = new Integer[]{22, 23}; //(1,1,0,0)..
testSubject[1][1][1] = new Integer[]{24, 25, 26};
testSubject[1][1][2] = new Integer[]{27, 28, 29, 30, 31, 32, 33, 34};
testSubject[1][1][3] = new Integer[]{35, 36}; //..(1,1,3,1)
//Launch the test.
test.readValue(testSubject);
}
/**
*
* #param obj The Array from where we want to get the data.
*/
public void readValue(Object obj) {
//Where should it start.
startPosition = new int[]{0, 1, 0, 0};
//Where should it stop.
endPosition = new int[]{1, 1, 1, 2};
System.out.println("Start Position:" + Arrays.toString(startPosition) + " End Position:" + Arrays.toString(endPosition));
int[] currentPosition = new int[]{-1, -1, -1, -1};
//Call to the method.
testRead((Object[]) obj, 0, currentPosition);
//Result to array.
Object[] arrayToReturn = result.toArray(new Object[0]);
System.out.println("Result: " + Arrays.toString(arrayToReturn));
}
/**
* Recursive method that looks for the values in a multi-dimensional array, in a given range. /!\ No checks are implemented here, wrong input can end in a
* StackOverFlow.
*
* #param obj The array in Object[] form.
* #param currentDimension The dimension we are currently in.
* #param result The reference to the list that will store all the values we found.
* #param currentPosition The current position we are in.
*/
private void testRead(Object[] obj, int currentDimension, int[] currentPosition) {
for (int i = 0; i < obj.length; i++) {
currentPosition[currentDimension] = i;
if (Arrays.equals(startPosition, currentPosition) && currentDimension == (currentPosition.length - 1)) {
//Found the start position.
System.out.println("############ START ############");
inRange = true;
}
if ((i >= startPosition[currentDimension] && i <= endPosition[currentDimension]) || inRange == true) {
//We are in the write track to get to the values we are looking for.
if (obj[i] instanceof Object[]) {
//The data contained in the cell is an array.
testRead((Object[]) obj[i], currentDimension + 1, currentPosition);
} else {
//The data contained in the cell is a scalar. This is what we where looking for.
System.out.println(Arrays.toString(currentPosition) + " Data: " + obj[i]);
result.add(obj[i]);
}
}
if (Arrays.equals(endPosition, currentPosition) && currentDimension == (currentPosition.length - 1)) {
//Found the end position.
System.out.println("############ END ############");
inRange = false;
}
}
}
}
Any question or idea to better the code is welcome.

Bin Packing Algorithm In Need of Speeding-Up

I'm looking for an intelligent way to approach a version of the common bin-packing problem. Given a number of bags (as I'm calling them) with a certain capacity, and list of items that take up a certain amount of space, the task is to determine if all of the items can fit in the bags; and if so, how. I've got an exhaustive DFS working right now, but it takes... forever. My DFS is iterative and requires copying entire states at every step, which is very expensive. Here's my code for a specific problem with 4 bags with 10 capacity (the truly relevant portions of this code are just the pack() method and the State class if you don't want to look at it all):
import java.util.ArrayList;
import java.util.Stack;
public class BagProblem {
int numBags;
int bagCapacity;
ArrayList<Item> items = new ArrayList<Item>();
public static void main(String[] args) {
BagProblem bp = new BagProblem(4, 10);
bp.pack();
}
public BagProblem(int numBags, int bagCapacity) {
this.numBags = numBags;
this.bagCapacity = bagCapacity;
items = new ArrayList<Item>();
items.add(new Item("item0", 6));
items.add(new Item("item1", 6));
items.add(new Item("item2", 6));
items.add(new Item("item5", 3));
items.add(new Item("item6", 3));
items.add(new Item("item7", 3));
items.add(new Item("item8", 2));
items.add(new Item("item9", 2));
items.add(new Item("item10", 2));
items.add(new Item("item11", 2));
items.add(new Item("item12", 2));
items.add(new Item("item13", 2));
items.add(new Item("item14", 1));
}
// find a valid way to pack and print the items in each Bag, or
// print failure
public void pack() {
Stack <State> s = new Stack<State>();
Bag[] currBags = new Bag[numBags];
for (int i = 0; i < numBags; i++) {
currBags[i] = new Bag(bagCapacity);
}
s.push(new State(currBags));
while(!s.isEmpty()) {
State currState = s.pop();
for (Item i : items) {
if (!currState.containsItem(i)) {
State newState = new State(currState.bags);
newState.numItems = currState.numItems;
if (newState.addItem(i)) {
s.push(newState);
if (newState.numItems == items.size()) {
System.out.println("success");
System.out.println(newState);
return;
}
}
}
}
}
System.out.println("failure");
}
private class State {
Bag[] bags;
int numItems;
public State(Bag[] currBags) {
bags = new Bag[numBags];
for (int i = 0; i < numBags; i++) {
bags[i] = new Bag(bagCapacity);
}
// figure out how to actually copy this
for (int j = 0; j < numBags; j++) {
Bag bagToCopy = currBags[j];
for (Item item : bagToCopy.contents) {
Item newItem = new Item(item.name, item.size);
bags[j].size = bagToCopy.size;
bags[j].contents.add(newItem);
}
}
}
public boolean addItem(Item i) {
for (Bag b : bags) {
if (b.addItem(i)) {
numItems++;
return true;
}
}
return false;
}
public boolean containsItem(Item i) {
for (Bag b : bags) {
for (Item item : b.contents) {
if (item.name.equals(i.name))
return true;
}
}
return false;
}
public String toString() {
String output = "";
for (Bag b : bags) {
for (Item j : b.contents) {
output += j.name + " ";
}
output += "\n";
}
return output;
}
}
private class Bag {
int capacity;
int size;
ArrayList<Item> contents;
public Bag(int capacity) {
this.capacity = capacity;
this.size = 0;
contents = new ArrayList<Item>();
}
public boolean addItem(Item i) {
if(size + i.size > capacity)
return false;
contents.add(i);
size += i.size;
return true;
}
public String toString() {
String output = "";
for (Item i : contents) {
output += i.name + " ";
}
return output + "\n";
}
}
private class Item {
String name;
int size;
public Item(String name, int size) {
this.name = name;
this.size = size;
}
public String toString() {
return name;
}
}
}
After approximately one million years, this does spit out a correct answer (you probably won't want to actually wait that long if you try to run this):
success
item14 item7 item6 item5
item13 item12 item2
item11 item10 item1
item9 item8 item0
Each line indicates a separate bag. How can I speed this up? I know there are heuristics about trying to place the largest item first, etc., but what I'm really interested in is getting the basic DFS (or maybe I should try backtracking?) to have less overhead; I'll try to get fancier later.
Any help would be greatly appreciated.
I don't use Java but your implementation seems quite inefficient (as you've mentioned yourself) due to overcomplicating it. The algorithm itself is also very strange, I did not attempt to replicate it and just used the obvious O(bags^items) brute force algorithm that tries to put the first item into each bag, for each of those cases tries to put the second item into each bag, etc...
Instead of replicating the entire state repeatedly on the stack, you can put an item in a bag, explore the branch of the tree with this change, then take the item out of the bag.
Here is an example that completes instantly for your test case in C#.
static int[] itemSize;
static int[] bagFreeSpace;
static bool[,] doesBagContainItem; // in case this looks weird, [,] is a matrix, in java it would be [][]
static bool pack(int item)
{
// output the solution if we're done
if (item == itemSize.Length)
{
for (int i = 0; i < bagFreeSpace.Length; i++)
{
Console.WriteLine("bag" + i);
for (int j = 0; j < itemSize.Length; j++)
if (doesBagContainItem[i, j])
Console.Write("item" + j + "(" + itemSize[j] + ") ");
Console.WriteLine();
}
return true;
}
// otherwise, keep traversing the state tree
for (int i = 0; i < bagFreeSpace.Length; i++)
{
if (bagFreeSpace[i] >= itemSize[item])
{
doesBagContainItem[i,item] = true; // put item into bag
bagFreeSpace[i] -= itemSize[item];
if (pack(item + 1)) // explore subtree
return true;
bagFreeSpace[i] += itemSize[item]; // take item out of the bag
doesBagContainItem[i,item] = false;
}
}
return false;
}
static void Main(string[] args)
{
itemSize = new int[] { 6, 6, 6, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1 };
bagFreeSpace = new int[] { 10, 10, 10, 10 };
doesBagContainItem = new bool[bagFreeSpace.Length, itemSize.Length];
if (!pack(0))
Console.WriteLine("No solution");
}
Note: if you want to parallelize execution, you need to give each worker its own copy of the state (or 1 copy per job), but only at the point of branching, they can still then proceed as above, without replicating the state.

Categories