Program starts misbehaving after a certain threshold - java

I have a homework task as follows:
The bin packing problem is to pack the objects of
various weights into containers. Assume each
container can hold a maximum of 10 pounds. The
program uses an algorithm that places an object into
the first bin in which it would fit. Your program should
prompt the user to enter the total number of objects
and the weight of each object. The program displays
the total number of containers needed to pack the
objects and the content of each container. Here is a
simple run of the program:
Enter the number of objects: 6
Enter the weight of the objects: 7 5 2 3 5 8
Container 1 contains objects with weight: 7 2
Container 2 contains objects with weight: 5 3
Container 3 contains objects with weight: 5
Container 4 contains objects with weight: 8
Now I have decided to try making it smarter and optimize the object allocation. It seemed to be working fine, but when I started testing using more numbers than the sample run I noticed that the highest I can go is 27 objects. Anything higher and I start getting a few containers at the end of the execution that could be merged into a single one. Any ideas and suggestions are welcome. Thank you in advance!
package classwork;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.Scanner;
public class BinPacking {
private static final int binLimit = 10;
public static boolean lessThanLimit(int a, int b) {
if (a + b < binLimit) {
return true;
} else {
return false;
}
}
public static boolean perfectFit(int a, int b) {
if (a + b == binLimit) {
return true;
} else {
return false;
}
}
public static boolean weightsLeft(boolean[] a) { // check if there is one more item that has not been binned yet.
for (boolean b : a) {
if (b) {
return true;
}
}
return false;
}
public static ArrayList<int[]> distributeObjects(int[] weights) {
int counter = 0;
boolean[] objectAssigned = new boolean[weights.length]; // array to track which objects have been assigned already
ArrayList<int[]> result = new ArrayList<int[]>(); // list of int[] to be returned
for (int i = 0; i < weights.length; i++) {
ArrayList<Integer> currentBin = new ArrayList<Integer>(); // list to store the values of the weights in currrent bin
int currentBinWeight = 6;
if (!objectAssigned[i]) {
currentBin.add(weights[i]);
currentBinWeight = weights[i];
objectAssigned[i] = true;
} else
counter = 1;
stupidLoopThatWontBreak:
while (currentBinWeight < binLimit && counter < 1) {
counter = 1;
if (!weightsLeft(objectAssigned)) { // break loop if no more weights left
result.add(currentBin.stream().mapToInt(Integer::intValue).toArray());
break stupidLoopThatWontBreak;
}
for (int j = i + 1; j < weights.length; j++) {
if (perfectFit(currentBinWeight, weights[j]) && !objectAssigned[j]) {
currentBin.add(weights[j]);
currentBinWeight += weights[j];
objectAssigned[j] = true;
break stupidLoopThatWontBreak;
}
if (lessThanLimit(currentBinWeight, weights[j]) && !objectAssigned[j]) {
currentBinWeight += weights[j];
currentBin.add(weights[j]);
objectAssigned[j] = true;
}
}
}
// convert arraylist to int[] and add it to result. Java 8+ feature
if (!currentBin.isEmpty()) {
result.add(currentBin.stream().mapToInt(Integer::intValue).toArray());
counter = 0;
}
}
return result;
}
public static void main(String[] args) {
System.err.println("Container weight limit is " + binLimit);
Scanner in = new Scanner(System.in);
//Test numbers 7, 5, 3, 2, 5, 8
// System.out.print("Enter the weights of the objects you want to put into the bins: ");
// String input = in.nextLine();
// in.close();
//========================Random numbers for testing======================
String input = "";
Random ran = new Random();
System.out.print("Enter number of weights to be randomly generated: ");
int num = in.nextInt();
in.close();
for (int i = 0; i < num; i++) {
input += (ran.nextInt(binLimit) + 1) + " "; //+1 to not have zeroes
}
//========================End of random numbers===========================
ArrayList<Integer> list = new ArrayList<Integer>();
String[] str = input.trim().split(" "); // trim surrounding spaces, use space char as separator
for (String a : str) {
list.add(Integer.valueOf(a));
}
// sort the list in a descending order
Collections.sort(list);
Collections.reverse(list); // this could be avoided if I started checking from the last index in distributeObjects()
System.out.println("The generated and sorted descendingly weights are:");
System.out.println("\n" + list.toString() + "\n");
int[] weights = new int[list.size()];
for (int a = 0; a < weights.length; a++) {
weights[a] = list.get(a);
}
ArrayList<int[]> bins = distributeObjects(weights);
for (int i = 0; i < bins.size(); i++) {
System.out.println("Container " + (i + 1) + " contains objects with weight: " + Arrays.toString(bins.get(i)));
}
}
}

Since I cannot comment, I am posing this as an answer -
Run above code with "10 9 8 8 8 7 7 7 6 6 6 5 5 5 4 4 4 4 4 3 3 2 2 2 2 2 2 2 1 1" as input array and you will see the problem being caused by 'counter' variable. If you replace 'counter=1;' from else with 'continue;' it should work for this input. You still need to test this for other inputs. Also, the above code needs refactoring - for example - use either list or weights. Both are nor required.

I tried not to distort your code too much, but this should give you your desired output. The problem was with your second loop, it caused some trouble with the counter variable since counter was never set back to 0 at the end of your loop if the bin should not already be added (when you did not find the first weight that has to be added to the bin).
The easiest way to fix your program is simply to move counter = 0; inside the if statement where you check if there is already a weight inside of the bin or not outside of the if block.
I removed the "stupidLoopThatWouldNotBreak" below and replaced it with another for loop, that looks through all remaining weights if there is one that can fit.
public static ArrayList<int[]> distributeObjects(int[] weights) {
boolean[] objectAssigned = new boolean[weights.length]; // array to track which objects have been assigned already
ArrayList<int[]> result = new ArrayList<int[]>(); // list of int[] to be returned
for (int i = 0; i < weights.length; i++) {
ArrayList<Integer> currentBin = new ArrayList<Integer>(); // list to store the values of the weights in currrent bin
int currentBinWeight = 0;
//This loop searches for the first unused Weight, so you do not need `count` anymore
for (int j = i; j < weights.length; j++) {
if (!objectAssigned[j]) {
currentBin.add(weights[j]);
currentBinWeight = weights[j];
objectAssigned[j] = true;
break;
}
i = j; //You can skip all iterations with used weights
}
for (int j = i; j < weights.length && currentBinWeight < binLimit; j++) {
if (perfectFit(currentBinWeight, weights[j]) && !objectAssigned[j]) {
currentBin.add(weights[j]);
currentBinWeight += weights[j];
objectAssigned[j] = true;
break; //this break gives some performance bonus
}
if (lessThanLimit(currentBinWeight, weights[j]) && !objectAssigned[j]) {
currentBinWeight += weights[j];
currentBin.add(weights[j]);
objectAssigned[j] = true;
}
}
// convert arraylist to int[] and add it to result. Java 8+ feature
if (!currentBin.isEmpty()) {
result.add(currentBin.stream().mapToInt(Integer::intValue).toArray());
}
}
return result;
}
You can further enhance your code if you split this loop up. You can add more functions like the two helperfunctions perfectFit and lessThanlimit. You can for example add another function that will search for the first empty element, maybe even add it to the bin. Also the 2 functions you already have could be merged into one function called addWeight or attemptAdd. Furthermore you could create seperate classes for the bin and for the weights.

Related

Find different index in arrays compare

I'm trying to learn a bit Java with tutorials and currently I'm struggling with piece of code where I should find on which index is difference between arrays (if there is difference at all)
My code
Scanner scanner = new Scanner(System.in);
int[] arrOne = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] arrTwo = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int sumArrOne = 0;
int index = 0;
boolean diff = false;
for (int k : arrOne) {
if (Arrays.equals(arrOne, arrTwo)) {
sumArrOne += k;
} else {
for (int i : arrTwo) {
if (k != i) {
index = i;
diff = true;
break;
}
}
}
}
if (diff) {
System.out.println("Found difference at " + index + " index.");
} else {
System.out.println("Sum: " + sumArrOne);
}
So, if arrays are identical I'm sum array elements in arrOne. If they are not identical -> must show at which index they are not.
With this code when I input
1 2 3 4 5
1 2 4 3 5
I should get that difference is at index 2 instead I've got index 1.
I'm not quite sure why and would be glad if someone point me out where is my mistake.
I updated your code. Looks like you're misunderstanding the concept of indexes yet.
Use one common index to check with in both arrays, in my example it's simply called i:
import java.util.Arrays;
import java.util.Scanner;
public class BadArray {
static private final int INVALID_INDEX = Integer.MIN_VALUE;
public static void main(final String[] args) {
try (final Scanner scanner = new Scanner(System.in);) {
final int[] arrOne = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
final int[] arrTwo = Arrays.stream(scanner.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int sumArrOne = 0;
int diffIndex = INVALID_INDEX;
final int minLen = Math.min(arrOne.length, arrTwo.length);
for (int i = 0; i < minLen; i++) {
sumArrOne += arrOne[i];
if (arrOne[i] != arrTwo[i]) {
diffIndex = i;
break;
}
}
if (diffIndex != INVALID_INDEX) {
System.out.println("Found difference at " + diffIndex + " index.");
} else if (arrOne.length != arrTwo.length) {
System.out.println("Arrays are equal but have different length!");
} else {
System.out.println("Sum: " + sumArrOne);
}
}
}
}
I also put the scanner into a try-resource-catch to handle resource releasing properly.
Note you could also do the array lengths comparison right at the start if different array lengths play a more crucial role.
You are trying to find out which index has the first difference so you should iterate via the index rather than using a for-each loop (aka enhanced for loop). The following method should work for this.
/**
* Returns the index of the first element of the two arrays that are not the same.
* Returns -1 if both arrays have the same values in the same order.
* #param left an int[]
* #param right an int[]
* #return index of difference or -1 if none
*/
public int findIndexOfDifference(int[] left, int[] right) {
// short-circuit if we're comparing an array against itself
if (left == right) return -1;
for (int index = 0 ; index < left.length && index < right.length ; ++index) {
if (left[index] != right[index]) {
return index;
}
}
return -1;
}
In your code you compare, where the indexes are different, not the values at the indexes. Also your code has several other issues. I'll try to go through them step by step:
// compare the whole array only once, not inside a loop:
diff = !Arrays.equals(arrOne, arrTwo));
if (!diff) {
// do the summing without a loop
sumArrOne = Arrays.stream(arrOne).sum();
} else {
// find the difference
// it could be the length
index = Math.min(arrOne.length, arrTwo.length);
// or in some different values
for (int i = 0; i < index; i++) { // do a loop with counter
if (arrOne[i] != arrTwo[i]) {
index = i;
break;
}
}
}
It doesn't matter that I set index here above the loop as it's value will be overwritten anyways inside the loop, if relevant.

*First* Longest Increasing Subsequence

The longest increasing subsequence is the well known problem and I have a solution with the patience algorithm.
Problem is, my solution gives me the "Best longest increasing sequence" instead of the First longest increasing sequence that appears.
The difference is that some of the members of the sequence are larger numbers in the first(but the sequence length is exactly the same).
Getting the first sequence is turning out to be quite harder than expected, because having the best sequence doesn't easily translate into having the first sequence.
I've thought of doing my algorithm then finding the first sequence of length N, but not sure how to.
So, how would you find the First longest increasing subsequence from a sequence of random integers?
My code snippet:
public static void main (String[] args) throws java.lang.Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int inputInt;
int[] intArr;
try {
String input = br.readLine().trim();
inputInt = Integer.parseInt(input);
String inputArr = br.readLine().trim();
intArr = Arrays.stream(inputArr.split(" ")).mapToInt(Integer::parseInt).toArray();
} catch (NumberFormatException e) {
System.out.println("Could not parse integers.");
return;
}
if(inputInt != intArr.length) {
System.out.println("Invalid number of arguments.");
return;
}
ArrayList<ArrayList<Integer>> sequences = new ArrayList<ArrayList<Integer>>();
int sequenceCount = 1;
sequences.add(new ArrayList<Integer>());
sequences.get(0).add(0);
for(int i = 1; i < intArr.length; i++) {
for(int j = 0; j < sequenceCount; j++) {
if(intArr[i] <= intArr[sequences.get(j).get(sequences.get(j).size() - 1)]) {
sequences.get(j).remove(sequences.get(j).size() - 1);
sequences.get(j).add(i);
break;
} else if (j + 1 == sequenceCount) {
sequences.add(new ArrayList<Integer>(sequences.get(j)));
sequences.get(j + 1).add(i);
sequenceCount++;
break; //increasing sequenceCount causes infinite loop
} else if(intArr[i] < intArr[sequences.get(j + 1).get(sequences.get(j + 1).size() - 1)]) {
sequences.set(j+ 1, new ArrayList<Integer>(sequences.get(j)));
sequences.get(j+ 1).add(i);
break;
}
}
}
int bestSequenceLength = sequenceCount;
ArrayList<Integer> bestIndexes = new ArrayList<Integer>(sequences.get(bestSequenceLength - 1));
//build bestSequence, then after it I'm supposed to find the first one instead
int[] bestSequence = Arrays.stream(bestIndexes.toArray()).mapToInt(x -> intArr[(int) x]).toArray();
StringBuilder output = new StringBuilder("");
for(Integer x : bestSequence) {
output.append(x + " ");
}
System.out.println(output.toString().trim());
}
I'm storing indexes instead in preparation for having to access the original array again. Since it's easier to go from indexes to values than vice versa.
Example:
3 6 1 2 8
My code returns: 1 2 8
First sequence is: 3 6 8
Another Example:
1 5 2 3
My code correctly returns: 1 2 3
Basically, my code works as long as the first longest sequence is the same as the best longest sequence. But when you have a bunch of longest sequences of the same length, it grabs the best one not the first one.
Code is self-explanatory. (Have added comments, let me know if you need something extra).
public class Solution {
public static void main(String[] args) {
int[] arr = {3,6,1,2,8};
System.out.println(solve(arr).toString());
}
private static List<Integer> solve(int[] arr){
int[][] data = new int[arr.length][2];
int max_length = 0;
// first location for previous element index (for backtracing to print list) and second for longest series length for the element
for(int i=0;i<arr.length;++i){
data[i][0] = -1; //none should point to anything at first
data[i][1] = 1;
for(int j=i-1;j>=0;--j){
if(arr[i] > arr[j]){
if(data[i][1] <= data[j][1] + 1){ // <= instead of < because we are aiming for the first longest sequence
data[i][1] = data[j][1] + 1;
data[i][0] = j;
}
}
}
max_length = Math.max(max_length,data[i][1]);
}
List<Integer> ans = new ArrayList<>();
for(int i=0;i<arr.length;++i){
if(data[i][1] == max_length){
int curr = i;
while(curr != -1){
ans.add(arr[curr]);
curr = data[curr][0];
}
break;
}
}
Collections.reverse(ans);// since there were added in reverse order in the above while loop
return ans;
}
}
Output:
[3, 6, 8]

Creating multiple nested loops to generate two numbers that move through the length of a Array

As the title reads, I have been thinking about creating multiple nested loops that aim to achieve one purpose. Move two generated random numbers between 0-9 through each possible possition of an array.
For example, App generates first number (fNum) 1 and second number (sNum) 6. It then moves these numbers in the array which containts ABC. However firstNum and secondNum will need to also try all the possible combinations, so each one will need to be different with each loop.
-1ABC6
-A1BC6
-AB1C6
-ABC16
-ABC61
-AB6C1
-A6BC1
-6ABC1
-A6B1C
-A61BC
-A16BC
-A1B6C
-A1BC6
and so on...
I beleive the best way will be to create a method for generating a counter, which increments the numbers which I can call.
private int getNextNumber(int num) {
if (num == 0) {
return num;
} else {
num++;
}
if (num < 10) {
return num;
} else {
return -1;
}
}
Then I will need multiple nested loops... I have decided to go for several loops which will go infinitly.
while (j < maxlen) {
//J = 0 and maxlen = length of text so in this case 3 as it is ABC
//Add two numbers and check against answer
while (fNum != -1 || sNum != -1) {
//incrememnt numbers
fNum = getNextNumber(fNum);
System.out.println(fNum);
sNum = getNextNumber(sNum);
System.out.println(fNum);
}
String textIni = "ABC";
int lenOfText = textIni.length();
char[] split = textIni.toCharArray();
for (int i = 0; i < lenOfText; i++) {
//here it will look at the length of the Text and
//try the possible positions it could be at....
//maybe wiser to do a longer loop but I am not too sure
}
}
Since you don't need to store all possible combinations, we will save some memory using only O(n) storage with an iterative solution. I propose you a basic implementation but don't expect to use it on large arrays since it has a O(n³) complexity.
public static void generateCombinationsIterative(List<Integer> original, int fnum, int snum) {
int size = original.size();
for (int i=0 ; i<=size ; i++) {
List<Integer> tmp = new ArrayList<>(original);
tmp.add(i,fnum);
for (int j=0 ; j<=size + 1 ; j++) {
tmp.add(j,snum);
System.out.print(tmp + (i == size && j == size + 1 ? "" : ", "));
tmp.remove(j);
}
}
}
For your culture, here is an example of a recursive solution, which takes a lot of memory so don't use it if you don't need to generate the lists of results. Nevertheless, this is a more general solution that can deal with any number of elements to insert.
public static List<List<Integer>> generateCombinations(List<Integer> original, Deque<Integer> toAdd) {
if (toAdd.isEmpty()) {
List<List<Integer>> res = new ArrayList<>();
res.add(original);
return res;
}
int element = toAdd.pop();
List<List<Integer>> res = new LinkedList<>();
for (int i=0 ; i<=original.size() ; i++)
// you must make a copy of toAdd, otherwise each recursive call will perform
// a pop() on it and the result will be wrong
res.addAll(generateCombinations(insertAt(original,element,i),new LinkedList<>(toAdd)));
return res;
}
// a helper function for a clear code
public static List<Integer> insertAt(List<Integer> input, int element, int index) {
List<Integer> result = new ArrayList<>(input);
result.add(index,element);
return result;
}
Note that I did not use any array in order to benefit from dynamic data structures, however you can call the methods like this :
int[] arr = { 1,2,3 };
int fnum = 4, snum = 5;
generateCombinationsIterative(Arrays.asList(arr),fnum,snum);
generateCombinations(Arrays.asList(arr),new LinkedList<>(Arrays.asList(fnum,snum));
Note that both methods generate the combinations in the same order.

Recursive Knapsack Java

I'm struggling with a homework assignment and I believe I am vastly over-complicating the solution and need some help from anyone willing to offer it. Let me explain some ground rules for the assignment.
Below is a link to another post that has the exact problem informatation.
How do I solve the 'classic' knapsack algorithm recursively?
A set of numbers will be given such as for example: 15, 11, 8, 7, 6, 5. The first number always corresponds to the target or capacity of the knapsack. What I must do is recursively check all the numbers and see if any of the numbers add up to the capacity of the knapsack. If they do, I am to print the numbers that add up to the target sum and then continue checking for other possible solutions. When researching this problem, most posts solve for one solution. Let me explain the ground rules for the assignment.
This assignment must be done recursively, no exceptions.
All solutions must be found
The numbers are sorted from highest to lowest.
In the 15, 11, 8, 7, 6, 5 There was only one solution of 8 + 7 + 5 = 15. However, given a data set such as 15, 10, 9, 8, 7, 6, 5, 4, 3, 2 there exist multiple solutions such as.
10 + 5 = 15
9 + 6 = 15
8 + 7 = 15
Essentially there are two problems to solve.
From the previous post linked up above:
The idea, given the problem you stated (which specifies we must use recursion) is simple: for each item that you can take, see if it's better to take it or not. So there are only two possible path
you take the item
you don't take it
When you take the item, you remove it from your list and you decrease the capacity by the weight of the item.
When you don't take the item, you remove if from you list but you do not decrease the capacity.
I'm having some trouble getting my mind around what the author in this solution was saying.
For example: Assuming a number set of 20, 11, 8, 7, 6,5
1. Target is 20
2. Read in number from set: 11
4. 11 < 20, Add 11 to solution
5. New target is 9 (20 - 11)
6. Read in the next number: 8
7. 8 is less than 9, Add 8 to solution
8. New target is 1 (20 - 19)
9 Read in 7, 7 is larger than 1, do not add 7
What I'm failing to understand is what do I do if I don't add a number?
You take an item: You remove the item from your list and decrease the capacity
You dont take an item: You remove the item from your list but you don't decrease the capacity.
In my code, in either case of "take item" or "dont take item", I do not remove an item from my weight list and I think this is my problem to begin with.
I'll post some code I've worked on below for this assignment. As you can see, there is is an overly bloated solution that does not work as elegantly as the real solution should. If anyone could provide advice or insight on how to really solve this problem with the assignment parameters mentioned above, I would greatly appreciate it. Thank you.
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.swing.JOptionPane;
public class Knapsack
{
public static void main(String[] args)
{
//Read in user input first
int[] tempArray;
String userInput = JOptionPane.showInputDialog("Enter a list of numbers delimited by a single space.");
String[] splitElements = userInput.split("\\s+");
//User array will contain the exact amount of
//numbers as long as extra spaces are not entered.
tempArray = new int[splitElements.length];
for(int i = 0; i < tempArray.length; i++)
{
tempArray[i] = Integer.parseInt(splitElements[i]);
}
Recursion recObj = new Recursion(tempArray);
}
}
class Recursion
{
private int[] weightArray;
private int [] solutionArray;
private int counter;
private int mainGoal;
private int [] backupOfOriginal;
private int solutionArrayCounter;
private ArrayList numberList;
private ArrayList previousSolutionsFound;
private int passThrough;
private int baseIterator;
private ArrayList distinctSolutions;
public Recursion(int[] paramArray)
{
weightArray = paramArray;
backupOfOriginal = weightArray;
solutionArray = new int[paramArray.length];
//Start at index 1 where the first number technically starts.
counter = 0;
//Keep track of main goal
mainGoal = weightArray[0];
solutionArrayCounter = 0;
passThrough = 0;
baseIterator = 0;
distinctSolutions = new ArrayList();
numberList = new ArrayList();
previousSolutionsFound = new ArrayList();
for(int i = 1; i < weightArray.length; i++)
{
numberList.add(weightArray[i]);
}
//Begin the recursive problem.
CheckForSums(mainGoal, numberList);
}
public void CheckForSums(int targetValue, ArrayList weightArray)
{
int numberRead = (Integer) weightArray.get(counter);
targetValue = ComputeTarget();
counter++;
//Base case if any number to read
//is greater than the main target value
//remove it
if(numberRead > mainGoal)
{
weightArray.remove(counter);
counter--;
}
if(numberRead <= targetValue)
{
AddToSolution(numberRead);
CheckForPossibleSolution();
//Add the item to the solution
}
//counter++;
if(counter == weightArray.size())
{
passThrough++;
counter = passThrough + 1;
RemoveOneFromSolution();
}
//Advance forward one position
if(passThrough == weightArray.size() - 1)
{
counter = 0;
passThrough = 0;
weightArray = RebuildArrayList(weightArray);
for(int i = 0; i < baseIterator; i++)
{
weightArray.remove(0);
}
baseIterator++;
ResetSolutionArray();
}
if(baseIterator == this.weightArray.length - 2)
{
//Should be completely done
return;
}
CheckForSums(targetValue, weightArray);
}
public void ResetSolutionArray()
{
solutionArrayCounter = 0;
for(int i = 0; i < solutionArray.length; i++)
{
solutionArray[i] = 0;
}
}
public void CheckForPossibleSolution()
{
if(SumOfSolutionsFound() == mainGoal)
{
PrintFoundSolution();
RemoveDownToBaseNumber();
}
else
{
System.out.println("No solution found yet.");
}
}
public void RemoveOneFromSolution()
{
if(solutionArrayCounter > 1)
{
solutionArrayCounter--;
}
if(solutionArrayCounter > 1)
{
solutionArray[solutionArrayCounter] = 0;
}
}
public void RemoveDownToBaseNumber()
{
while(solutionArrayCounter > 1)
{
solutionArrayCounter--;
solutionArray[solutionArrayCounter] =0;
}
}
public int SumOfSolutionsFound()
{
int sumOfSolutions = 0;
for(int i = 0; i < solutionArray.length; i++)
{
sumOfSolutions += solutionArray[i];
}
return sumOfSolutions;
}
public ArrayList<Integer> RebuildArrayList(ArrayList<Integer> paramList)
{
paramList = new ArrayList();
for(int i = 1; i < weightArray.length; i++)
{
paramList.add(weightArray[i]);
}
return paramList;
}
public void PrintFoundSolution()
{
StringBuilder toMessageBox = new StringBuilder();
System.out.print("Found a solution! ");
toMessageBox.append("Found a Solution! ");
for(int i = 0; i < solutionArray.length; i++)
{
System.out.print(solutionArray[i] + " ");
toMessageBox.append(solutionArray[i] + " ");
}
String finishedMessage = toMessageBox.toString();
boolean displayCurrentSolution = true;
for(int i = 0; i < previousSolutionsFound.size(); i++)
{
String previousSolution = previousSolutionsFound.get(i).toString();
if(finishedMessage.equals(previousSolution))
{
displayCurrentSolution = false;
}
}
previousSolutionsFound.add(finishedMessage);
if(displayCurrentSolution == true)
{
distinctSolutions.add(finishedMessage);
JOptionPane.showMessageDialog(null, finishedMessage,
"Solution for target: " + mainGoal, JOptionPane.INFORMATION_MESSAGE);
}
}
public void AddToSolution(int value)
{
solutionArray[solutionArrayCounter] = value;
solutionArrayCounter++;
}
public int ComputeTarget()
{
int sumOfSolutions = 0;
for(int i = 0; i < solutionArray.length; i++)
{
sumOfSolutions += solutionArray[i];
}
int numbersNeededToReachMainGoal = mainGoal - sumOfSolutions;
return numbersNeededToReachMainGoal;
}
}
The problem you described is actually a special case where you have only items weights, but no profits - or alternatively the weights and the profits are equal. This problem isusually not termed as Knapsack but the maximization version of Subset Sum.
Furthermore, for a recursive solution no array besides the input is needed.
Suppose the item sizes are given in the array weightArray (indices zero-based here) of length n and capacity denoted the total capacity availabel.
Define (first conceptually, not in code) the function
F( remainingCapacity, i ) :=
maximum total weight attainable for items
with indices in {0,..,i} of infinity if no such solution exists
note that
F( capacity, n - 1 )
yields the solution to the problem. Additionally, F has the property
F( remainingCapacity, -1 ) = 0 if remainingCapacity >= 0
and
F( remainingCapacity, i ) =
Infinity (can be simulated by a sufficiently
large integer) if remainingCapacity < 0
and
F( remainingCapacity, i ) =
max( F( remainingCapacity - weightArray[ i ], i - 1 ),
F( remainingCapacity, i - 1 ) )
where the first term in the maximum expression corresponds to the "take item i" case and the second expression corresponds to the "don't take item i" case. The cases above can more or less easily transformed to an actual implementation.
However note that this will yield only the maximum value attainable by a choice of items, but not the actual choice of items itself.

Count how many times an element occurs in an array - Java

I recently made a very simple practice program in Python, that takes user input and rolls dice. The code is:
import random
import sys
import math
def roll(rolls, sides, results):
for rolls in range(1, rolls + 1):
result = random.randrange(1, sides + 1)
print result
results.append(result)
def countf(rolls, sides, results):
i = 1
print "There were", rolls, "rolls."
for sides in range(1, sides + 1):
if results.count(i) != 1:
print "There were", results.count(i), i,"s."
else:
print "There was", results.count(i), i
i = i + 1
if i == sides:
break
rolls = input("How many rolls? ")
sides = input("How many sides of the die? ")
results = []
roll(rolls, sides, results)
countf(rolls, sides, results)
(actually this is part of a larger program, so I had to cut'n'paste bits, and I might have missed something out).
And so I decided to translate that to Java. Notice the algorithm here: get random number, print it, append it to an array, then count the amount of each number in the array at the end, and print out that value. Problem is, I don't know how to do the equivalent of someArray.count(someIndex) in Java syntax. So my Java program looks like this so far:
import java.util.*;
public class Dice {
static Scanner input = new Scanner(System.in);
public static void main(String[] args) {
final static int TIMES_TO_ROLL = getInt("Times to roll?");
Random flip = new Random();
int[] results = new int[TIMES_TO_ROLL];
for (int i = 0; i < TIMES_TO_ROLL; i++) {
int result = flip.nextInt(6);
System.out.println(result);
results[i] = result;
}
}
public static int getInt(String prompt) {
System.out.print(prompt + " ");
int integer = input.nextInt();
input.nextLine();
return integer;
}
}
So can someone help me with the array counting code? I understand that this might not be a defined method, since Python is higher level after all, so I could make my own array counting method, but I was wondering if Java, like Python, has a predefined one.
EDIT: I managed something like this:
public static int arrayCount(int[] array, int item) {
int amt = 0;
for (int i = 0; i < array.length; i++) {
if (array[i] == item) {
amt++;
}
else {
amt = amt;
}
}
return amt;
}
EDIT: Just out of interest, assuming I use Command prompt to run my Java program and Python.exe (command prompt console for Python), which one will be faster (in other words, for the same code, which language has better performance?)?
You could use a HashMap to store the result.
If the new number is not in your map you add it with "1" as initial value.
If it exists your put "+1" to the current map value.
To display the values you just have to iterate on you entries in a for each loop.
The solution is to transform your array to a List and then use the Collections.frequency method:
List<Integer> resultList = Arrays.asList(results);
int freq = Collections.frequency(resultList, 4);
Also you could use ArrayList from the very beginning saving you the transformation:
List<Integer> result = new ArrayList<Integer>();
// add results
int freq = Collections.frequency(result, 4);
See the Collections documentation here
EDIT: If performance is an issue (as suggested in the comments) then maybe you want to use each index of the array as a counter, as follows:
Random flip = new Random(SIDES);
int[] counters = new int[SIDES];
for (int i = 0; i < TIMES_TO_ROLL; i++) {
int result = flip.nextInt;
counters[result] = counters[result]+1;
}
Notice that you no longer need to count at the end since you've already got all the counters in the array and there is no overhead of calculating the hash.
There are a couple libraries that will do this for you:
Google Guava's MultiSet
Apache Common's Bag
But for something so simple, you may consider an extra library a bit excessive.
You can also do this yourself with an int[]. Assuming your dice is using whole numbers, have the number rolled refer to the index into the array, and then increment the value at that index. When you need to retrieve the value for a given number, look up its value by the index.
private static final int NUMBER_DICE_SIDES = 6;
public static void main(String[] args) {
final static int TIMES_TO_ROLL = getInt("Times to roll?");
Random flip = new Random(NUMBER_DICE_SIDES);
int[] results = new int[NUMBER_DICE_SIDES];
for (int i = 0; i < TIMES_TO_ROLL; i++) {
int result = flip.nextInt;
System.out.println(result);
results[result]++;
}
for(int i = 0; i < NUMBER_DICE_SIDES; ++i) {
System.out.println((i+1)+"'s: " + arraysCount(results, i));
}
}
public static int arrayCount(int[] array, int item) {
return array[item];
}
There's a frequency method in collections
int occurrences = Collections.frequency(listObject, searchItem);
Java doc for collections
As far as I am aware, there is no defined method to return the frequency of a particular element in an array. If you were to write a custom method, it would simply be a matter of iterating through the array, checking each value, and if the value matches the element you're after, incrementing a counter.
So something like:
// in this example, we assume myArray is an array of ints
private int count( int[] myArray, int targetValue) {
int counter = 0;
for (int i = 0 ; i < myArray.length; i++ ) {
if (myArray[i] == targetValue) {
counter++;
}
}
return counter;
}
Of course, if you want to find the frequency of all the unique values in your array, this has the potential of being extremely inefficient.
Also, why are you using a 7-sided die? The Random nextInt() will return a number from 0 up to but not including the max. So your die will return values from 0 through 6. For a six-sided die, you'd want a new Random(6); and then increment your roll by one to get a value from one through six: flip.nextInt() +1;.
class FindOccurrence {
public static void main (String[]args) {
int myArray[] = {5, 8, 5, 12, 19, 5, 6, 7, 100, 5, 45, 6, 5, 5, 5};
int numToFind = 5;
int numberOfOccurrence = 0;
for (int i=0; i < myArray.length; i++) {
if (numToFind == myArray[i]) {
numberOfOccurrence++;
}
}
System.out.println("Our number: " + numToFind);
System.out.println("Number of times it appears: " + numberOfOccurrence);
}
}

Categories