-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathKnapsack.java
More file actions
173 lines (148 loc) · 8.01 KB
/
Knapsack.java
File metadata and controls
173 lines (148 loc) · 8.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import java.util.*;
public class Knapsack {
//Change numberOfItems, values & weights to run with different items
private final int numberOfItems = 5; //used to create arrays of correct size
private int[] values;
private int[] weights;
private int[][] memoizationTable;
private boolean[][] chosenItems; //used for backtracking to see what items were included
public static void main(String[] args) {
Knapsack knapsackSolver = new Knapsack();
knapsackSolver.solveKnapsack(11);
System.out.println("\n\n");
knapsackSolver.solveKnapsack(10);
}
public Knapsack(){ //constructor creates items & populates arrays for values[] & weights[]
//add dummy item 0 in both arrays so arrays are indexed from 1
int[] tempValues = {0, 1, 6, 18, 22, 28}; //values is already declared so make a temporary array with array shorthand
values=tempValues; //set values to the reference of the temp array
int[] tempWeights = {0, 1, 2, 5, 6, 7};
weights=tempWeights;
}
//check if arrays from constructor are the correct length
public boolean verifyCorrectArrayLength(int[] array){
return array.length == (numberOfItems+1); //+1 since arrays are indexed from 1
}
public void solveKnapsack(final int knapsackWeightCapacity){ //final so it can't accidentally be changed
memoizationTable = new int[numberOfItems+1][knapsackWeightCapacity+1]; //arrays are indexed from 1
chosenItems = new boolean[numberOfItems+1][knapsackWeightCapacity+1];
fill2DArray(memoizationTable, -1); //set all values in table to -1
//Initial step in algorithm: fill 0th row with 0's
for(int i=0; i<memoizationTable[0].length ; i++){ //start @ 0, but [0][0] is ignored when printing
memoizationTable[0][i]=0;
}
System.out.println("Solving knapsack weight capacity "+knapsackWeightCapacity+", with "+numberOfItems+" items" +"\n-----------------------------------------------------------------------------------------------------");
printTable(memoizationTable, 0); //Print table for row 0
for(int i=1; i<=numberOfItems; i++){ //start @ row 1
for(int currentCapacity=0; currentCapacity<=knapsackWeightCapacity; currentCapacity++){ // <=
if(weights[i] > currentCapacity){ //item is too heavy to fit
memoizationTable[i][currentCapacity] = memoizationTable[i-1][currentCapacity];
}
//If 2nd argument in max() is greater than 1st: max(M[i-1, w], v[i]+M[i-1][W-w[i] ])
else if( (values[i] + memoizationTable[i-1][currentCapacity-weights[i]]) > (memoizationTable[i-1][currentCapacity]) ){
memoizationTable[i][currentCapacity] = values[i] + memoizationTable[i-1][currentCapacity-weights[i]];
chosenItems[i][currentCapacity]=true; //save item to keep table
}
else{ //if 1st argument of max() is greater
memoizationTable[i][currentCapacity] = memoizationTable[i-1][currentCapacity];
}
}
System.out.println();
printTable(memoizationTable, i);
}
System.out.println("\nKnapsack with weight capacity "+knapsackWeightCapacity+" has optimal value: "+memoizationTable[numberOfItems][knapsackWeightCapacity]);
System.out.println();
findOptimalKnapsackContents(knapsackWeightCapacity, memoizationTable, chosenItems); //see what items were actually added
}
//Fill 2d Array with a value (passed by reference)
public static void fill2DArray(int[][] matrix, int fillValue){
for(int i=0; i<matrix.length ; i++){
for(int j=0; j<matrix[i].length ; j++){
matrix[i][j]= fillValue;
}
}
}
//String representation of an item, easier to print an item by just giving it's index
//No array out of bounds checking
private String getItemAsString(int itemIndex){
return "Item "+itemIndex+" (Value="+values[itemIndex]+", Weight="+weights[itemIndex]+")";
}
private void printTable(int[][] memoTable, int lastRowCompleted){
System.out.println("Memoization table, Row "+lastRowCompleted+" completed \t(view with fixed-width font for columns to line up)");
int rows=memoTable.length;
int columns=memoTable[0].length;
String[] rowSetHeaders = new String[rows]; //create labels for y axis for items: {}, {1}, {1, 2}, {1, 2, 3}, {1, 2, 3, 4} ...
rowSetHeaders[0]="";
rowSetHeaders[1]="1";
for(int i=2; i<rows; i++){
rowSetHeaders[i] = rowSetHeaders[i-1]+ ", "+i; //copy previous value & append new item to be considered
}
int widestRowSetHeader = findWidestElementInList(rowSetHeaders)+5; //We add "{" and "}" on either side when printing, but it needs to be @ least +4 or the columns won't line up
int widestNumber = findWidestNumberInTable(memoTable) + 3; //Find number with most digits in table. All numbers are padded with @ least this many spaces to the left. Then add 3 to give more breathing room
System.out.println(leftPad("Weights -->", widestRowSetHeader*4)); //"Center" this label by giving a bunch of left padding
System.out.print(leftPad("", widestRowSetHeader)); //print empty spaces for formatted table output in upper left corner. Used widestRowSetHeader since must align with 0th column
for(int j=0; j<columns; j++){ //Print weights from 0 up to knapsackWeightCapacity
System.out.print("\t"+ leftPad(j, widestNumber));
}
System.out.println("\n--------------------------------------------------------------------------------------------------------------------------------------------------------");
//the actual memoization table
for(int i=0; i< rows; i++){
System.out.print( leftPad(("{" + rowSetHeaders[i] +"}"), widestRowSetHeader) ); //print row header: how many items were considered @ that row
for(int j=0; j<columns; j++){ //print the value from the memoization table
System.out.print("\t"+ leftPad(memoTable[i][j], widestNumber) ); //leftPad() adds spaces, also put tab separator BETWEEN each number (but not on the lat one in row)
}
System.out.println();
}
System.out.println();
}
//Loop through an entire 2d array & find the widest number (most digits)
public static int findWidestNumberInTable(int[][] table){
int widestNumber=1; //assume every value is @ least 1 digit
for(int[] row: table){
for(int number: row){
int numberWidth = ("" + number ).length(); //convert int to string & get string length
widestNumber = Math.max(widestNumber, numberWidth); //pick the larger
}
}
return widestNumber;
}
public static <Type> int findWidestElementInList(Type[] array){
int widestElement=0; //Don't assume @ least 1 character or digit, could be array of empty strings
for(Type element: array){
int elementWidth = ("" + element ).length(); //convert to string & get string length
widestElement = Math.max(widestElement, elementWidth); //pick the larger
}
return widestElement;
}
//Return a string with padding spaces if the input is not as wide as the desired width. Works for number & strings (including empty string "")
public static <Type> String leftPad(Type input, int desiredWidth){
String paddedInput = input + ""; //convert to string. Works even for empty string
if(paddedInput.length() < desiredWidth){
int paddingRequired = desiredWidth - paddedInput.length(); //loop adds an extra space to the left until it reaches the desired width
for(int i=0; i<paddingRequired; i++){
paddedInput = " " + paddedInput; //add a space to the left
}
}
return paddedInput;
}
//Must be called AFTER solveKnapsack()
private void findOptimalKnapsackContents(final int knapsackWeightCapacity, final int[][] memoizationTable, final boolean[][] chosenItems){
System.out.println("_____Knapsack Contains_____");
int currentCapacity=knapsackWeightCapacity; //start from bottom right corner of the matrix
for(int i=numberOfItems; i>=1; i--){
if(chosenItems[i][currentCapacity]){ //if it was included
System.out.println(getItemAsString(i));
currentCapacity -= weights[i]; //subtract the item's weight for next iteration since we've included an item
}
}
}
//mostly for testing purposes
private void printChosenItems(boolean[][] chosenItemsTable){
System.out.println("Chosen items table");
int rows=chosenItemsTable.length;
for(int i=0; i< rows; i++){
System.out.println(Arrays.toString(chosenItemsTable[i]));
}
System.out.println();
}
}