I got to know that memoization is better approach for playing with Fibonacci series. Today i wrote the program in this way--
private void printFibonacci(int lengthOfSeries) {
int lastNumb = 1;
int secondLastNumb = 1;
List<Integer> fabArray = new ArrayList<Integer>();
if (lengthOfSeries == 0) {
System.out.println("Please enter number to print series.");
} else {
fabArray.add(secondLastNumb);
fabArray.add(lastNumb);
while (fabArray.size() < lengthOfSeries) {
lastNumb = fabArray.get(fabArray.size() - 1);
secondLastNumb = fabArray.get(fabArray.size() - 2);
fabArray.add(lastNumb + secondLastNumb);
}
}
System.out.println(fabArray);
}
This is working fine. But when the input number is high (say 100,000,000), its throwing outOfMemoryerror. And its throwing error at line where printing List.
Can anybody please give some suggestion to make it powerful.
It's the growth of the ArrayList that is causing the OutOfMemoryError, and you don't need to either print the list or terminate your loop. The following code should have no problems with memory:
private void printFibonacci(int lengthOfSeries) {
int lastNumb = 1;
int secondLastNumb = 1;
int count = 2;
if (lengthOfSeries == 0) {
System.out.println("Please enter number to print series.");
} else {
System.out.print("1, 1");
while (count < lengthOfSeries) {
int next = lastNumb + secondLastNumb;
System.out.print(", " + next);
secondLastNumb = lastNumb;
lastNumb = next;
//Updated
count = count+1;
}
System.out.println();
}
}
That's the nature of this kind of algorithms, they're very memory-hungry. Also using int/Integer for it is a very bad idea as you'll quickly run out of Integer capacity (2*10^32 -1). Probably you want doubles, but still there is a limit of how far you can go.
100 million Integers takes about 400 million bytes.
This may well be larger than your JVMs default maximum heap size. Try adding command line switches to allocate more memory.
Some indications here: http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.rt.doc.20%2Fdiag%2Fappendixes%2Fdefaults.html
Following my comment, a trivial implementation would be:
int[] fabArray = new int[] {1, 1, 0};
for (int i = 0; i < lengthOfSeries; i++) {
fabArray[2] = fabArray[1] + fabArray[0];
System.out.print(fabArray[0] + ", ");
fabArray[0] = fabArray[1];
fabArray[1] = fabArray[2];
}
// End of line
System.out.println();
Related
I've been playing around with the Project Euler challenges to help improve my knowledge of Java. In particular, I wrote the following code for problem 14, which asks you to find the longest Collatz chain which starts at a number below 1,000,000. It works on the assumption that subchains are incredibly likely to arise more than once, and by storing them in a cache, no redundant calculations are done.
Collatz.java:
import java.util.HashMap;
public class Collatz {
private HashMap<Long, Integer> chainCache = new HashMap<Long, Integer>();
public void initialiseCache() {
chainCache.put((long) 1, 1);
}
private long collatzOp(long n) {
if(n % 2 == 0) {
return n/2;
}
else {
return 3*n +1;
}
}
public int collatzChain(long n) {
if(chainCache.containsKey(n)) {
return chainCache.get(n);
}
else {
int count = 1 + collatzChain(collatzOp(n));
chainCache.put(n, count);
return count;
}
}
}
ProjectEuler14.java:
public class ProjectEuler14 {
public static void main(String[] args) {
Collatz col = new Collatz();
col.initialiseCache();
long limit = 1000000;
long temp = 0;
long longestLength = 0;
long index = 1;
for(long i = 1; i < limit; i++) {
temp = col.collatzChain(i);
if(temp > longestLength) {
longestLength = temp;
index = i;
}
}
System.out.println(index + " has the longest chain, with length " + longestLength);
}
}
This works. And according to the "measure-command" command from Windows Powershell, it takes roughly 1708 milliseconds (1.708 seconds) to execute.
However, after reading through the forums, I noticed that some people, who had written seemingly naive code, which calculate each chain from scratch, seemed to be getting much better execution times than me. I (conceptually) took one of the answers, and translated it into Java:
NaiveProjectEuler14.java:
public class NaiveProjectEuler14 {
public static void main(String[] args) {
int longest = 0;
int numTerms = 0;
int i;
long j;
for (i = 1; i <= 10000000; i++) {
j = i;
int currentTerms = 1;
while (j != 1) {
currentTerms++;
if (currentTerms > numTerms){
numTerms = currentTerms;
longest = i;
}
if (j % 2 == 0){
j = j / 2;
}
else{
j = 3 * j + 1;
}
}
}
System.out.println("Longest: " + longest + " (" + numTerms + ").");
}
}
On my machine, this also gives the correct answer, but it gives it in 0.502 milliseconds - a third of the speed of my original program. At first I thought that maybe there was a small overhead in creating a HashMap, and that the times taken were too small to draw any conclusions. However, if I increase the upper limit from 1,000,000 to 10,000,000 in both programs, NaiveProjectEuler14 takes 4709 milliseconds (4.709 seconds), whilst ProjectEuler14 takes a whopping 25324 milliseconds (25.324 seconds)!
Why does ProjectEuler14 take so long? The only explanation I can fathom is that storing huge amounts of pairs in the HashMap data structure is adding a huge overhead, but I can't see why that should be the case. I've also tried recording the number of (key, value) pairs stored during the course of the program (2,168,611 pairs for the 1,000,000 case, and 21,730,849 pairs for the 10,000,000 case) and supplying a little over that number to the HashMap constructor so that it only has to resize itself at most once, but this does not seem to affect the execution times.
Does anyone have any rationale for why the memoized version is a lot slower?
There are some reasons for that unfortunate reality:
Instead of containsKey, do an immediate get and check for null
The code uses an extra method to be called
The map stores wrapped objects (Integer, Long) for primitive types
The JIT compiler translating byte code to machine code can do more with calculations
The caching does not concern a large percentage, like fibonacci
Comparable would be
public static void main(String[] args) {
int longest = 0;
int numTerms = 0;
int i;
long j;
Map<Long, Integer> map = new HashMap<>();
for (i = 1; i <= 10000000; i++) {
j = i;
Integer terms = map.get(i);
if (terms != null) {
continue;
}
int currentTerms = 1;
while (j != 1) {
currentTerms++;
if (currentTerms > numTerms){
numTerms = currentTerms;
longest = i;
}
if (j % 2 == 0){
j = j / 2;
// Maybe check the map only here
Integer m = map.get(j);
if (m != null) {
currentTerms += m;
break;
}
}
else{
j = 3 * j + 1;
}
}
map.put(j, currentTerms);
}
System.out.println("Longest: " + longest + " (" + numTerms + ").");
}
This does not really do an adequate memoization. For increasing parameters not checking the 3*j+1 somewhat decreases the misses (but might also skip meoized values).
Memoization lives from heavy calculation per call. If the function takes long because of deep recursion rather than calculation, the memoization overhead per function call counts negatively.
I've been trying to implement Euclid's algorithm in Java for 2 numbers or more.The problem with my code is that
a) It works fine for 2 numbers,but returns the correct value multiple times when more than 2 numbers are entered.My guess is that this is probably because of the return statements in my code.
b) I don't quite understand how it works.Though I coded it myself,I don't quite understand how the return statements are working.
import java.util.*;
public class GCDCalc {
static int big, small, remainder, gcd;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// Remove duplicates from the arraylist containing the user input.
ArrayList<Integer> listofnum = new ArrayList();
System.out.println("GCD Calculator");
System.out.println("Enter the number of values you want to calculate the GCD of: ");
int counter = sc.nextInt();
for (int i = 0; i < counter; i++) {
System.out.println("Enter #" + (i + 1) + ": ");
int val = sc.nextInt();
listofnum.add(val);
}
// Sorting algorithm.
// This removed the need of conditional statements(we don't have to
// check if the 1st number is greater than the 2nd element
// before applying Euclid's algorithm.
// The outer loop ensures that the maximum number of swaps are occurred.
// It ensures the implementation of the swapping process as many times
// as there are numbers in the array.
for (int i = 0; i < listofnum.size(); i++) {
// The inner loop performs the swapping.
for (int j = 1; j < listofnum.size(); j++) {
if (listofnum.get(j - 1) > listofnum.get(j)) {
int dummyvar = listofnum.get(j);
int dummyvar2 = listofnum.get(j - 1);
listofnum.set(j - 1, dummyvar);
listofnum.set(j, dummyvar2);
}
}
}
// nodup contains the array containing the userinput,without any
// duplicates.
ArrayList<Integer> nodup = new ArrayList();
// Remove duplicates.
for (int i = 0; i < listofnum.size(); i++) {
if (!nodup.contains(listofnum.get(i))) {
nodup.add(listofnum.get(i));
}
}
// Since the array is sorted in ascending order,we can easily determine
// which of the indexes has the bigger and smaller values.
small = nodup.get(0);
big = nodup.get(1);
remainder = big % small;
if (nodup.size() == 2) {
recursion(big, small, remainder);
} else if (nodup.size() > 2) {
largerlist(nodup, big, small, 2);
} else // In the case,the array only consists of one value.
{
System.out.println("GCD: " + nodup.get(0));
}
}
// recursive method.
public static int recursion(int big, int small, int remainder) {
remainder = big % small;
if (remainder == 0) {
System.out.println(small);
} else {
int dummyvar = remainder;
big = small;
small = dummyvar;
recursion(big, small, remainder);
}
return small;
}
// Method to deal with more than 2 numbers.
public static void largerlist(ArrayList<Integer> list, int big, int small, int counter) {
remainder = big % small;
gcd = recursion(big, small, remainder);
if (counter == list.size()) {
} else if (counter != list.size()) {
big = gcd;
small = list.get(counter);
counter++;
largerlist(list, gcd, small, counter);
}
}
}
I apologize in advance for any formatting errors etc.
Any suggestions would be appreciated.Thanks!
I think these two assignments are the wrong way around
big = gcd;
small = list.get(counter);
and then big not used
largerlist(list, gcd, small, counter);
Also you've used static variables, which is usually a problem.
I suggest removing static/global variables and generally don't reuse variables.
Edit: Oh yes, return. You've ignored the return value of the recursion method when called from the recursion method. That shouldn't matter as you are printing out instead of returning the value, but such solutions break when, say, you want to use the function more than once.
I have developed a code for expressing the number in terms of the power of the 2 and I am attaching the same code below.
But the problem is that the expressed output should of minimum length.
I am getting output as 3^2+1^2+1^2+1^2 which is not minimum length.
I need to output in this format:
package com.algo;
import java.util.Scanner;
public class GetInputFromUser {
public static void main(String[] args) {
// TODO Auto-generated method stub
int n;
Scanner in = new Scanner(System.in);
System.out.println("Enter an integer");
n = in.nextInt();
System.out.println("The result is:");
algofunction(n);
}
public static int algofunction(int n1)
{
int r1 = 0;
int r2 = 0;
int r3 = 0;
//System.out.println("n1: "+n1);
r1 = (int) Math.sqrt(n1);
r2 = (int) Math.pow(r1, 2);
// System.out.println("r1: "+r1);
//System.out.println("r2: "+r2);
System.out.print(r1+"^2");
r3 = n1-r2;
//System.out.println("r3: "+r3);
if (r3 == 0)
return 1;
if(r3 == 1)
{
System.out.print("+1^2");
return 1;
}
else {
System.out.print("+");
algofunction(r3);
return 1;
}
}
}
Dynamic programming is all about defining the problem in such a way that if you knew the answer to a smaller version of the original, you could use that to answer the main problem more quickly/directly. It's like applied mathematical induction.
In your particular problem, we can define MinLen(n) as the minimum length representation of n. Next, say, since we want to solve MinLen(12), suppose we already knew the answer to MinLen(1), MinLen(2), MinLen(3), ..., MinLen(11). How could we use the answer to those smaller problems to figure out MinLen(12)? This is the other half of dynamic programming - figuring out how to use the smaller problems to solve the bigger one. It doesn't help you if you come up with some smaller problem, but have no way of combining them back together.
For this problem, we can make the simple statement, "For 12, it's minimum length representation DEFINITELY has either 1^2, 2^2, or 3^2 in it." And in general, the minimum length representation of n will have some square less than or equal to n as a part of it. There is probably a better statement you can make, which would improve the runtime, but I'll say that it is good enough for now.
This statement means that MinLen(12) = 1^2 + MinLen(11), OR 2^2 + MinLen(8), OR 3^2 + MinLen(3). You check all of them and select the best one, and now you save that as MinLen(12). Now, if you want to solve MinLen(13), you can do that too.
Advice when solo:
The way I would test this kind of program myself is to plug in 1, 2, 3, 4, 5, etc, and see the first time it goes wrong. Additionally, any assumptions I happen to have thought were a good idea, I question: "Is it really true that the largest square number less than n will be in the representation of MinLen(n)?"
Your code:
r1 = (int) Math.sqrt(n1);
r2 = (int) Math.pow(r1, 2);
embodies that assumption (a greedy assumption), but it is wrong, as you've clearly seen with the answer for MinLen(12).
Instead you want something more like this:
public ArrayList<Integer> minLen(int n)
{
// base case of recursion
if (n == 0)
return new ArrayList<Integer>();
ArrayList<Integer> best = null;
int bestInt = -1;
for (int i = 1; i*i <= n; ++i)
{
// Check what happens if we use i^2 as part of our representation
ArrayList<Integer> guess = minLen(n - i*i);
// If we haven't selected a 'best' yet (best == null)
// or if our new guess is better than the current choice (guess.size() < best.size())
// update our choice of best
if (best == null || guess.size() < best.size())
{
best = guess;
bestInt = i;
}
}
best.add(bestInt);
return best;
}
Then, once you have your list, you can sort it (no guarantees that it came in sorted order), and print it out the way you want.
Lastly, you may notice that for larger values of n (1000 may be too large) that you plug in to the above recursion, it will start going very slow. This is because we are constantly recalculating all the small subproblems - for example, we figure out MinLen(3) when we call MinLen(4), because 4 - 1^2 = 3. But we figure it out twice for MinLen(7) -> 3 = 7 - 2^2, but 3 also is 7 - 1^2 - 1^2 - 1^2 - 1^2. And it gets much worse the larger you go.
The solution to this, which lets you solve up to n = 1,000,000 or more, very quickly, is to use a technique called Memoization. This means that once we figure out MinLen(3), we save it somewhere, let's say a global location to make it easy. Then, whenever we would try to recalculate it, we check the global cache first to see if we already did it. If so, then we just use that, instead of redoing all the work.
import java.util.*;
class SquareRepresentation
{
private static HashMap<Integer, ArrayList<Integer>> cachedSolutions;
public static void main(String[] args)
{
cachedSolutions = new HashMap<Integer, ArrayList<Integer>>();
for (int j = 100000; j < 100001; ++j)
{
ArrayList<Integer> answer = minLen(j);
Collections.sort(answer);
Collections.reverse(answer);
for (int i = 0; i < answer.size(); ++i)
{
if (i != 0)
System.out.printf("+");
System.out.printf("%d^2", answer.get(i));
}
System.out.println();
}
}
public static ArrayList<Integer> minLen(int n)
{
// base case of recursion
if (n == 0)
return new ArrayList<Integer>();
// new base case: problem already solved once before
if (cachedSolutions.containsKey(n))
{
// It is a bit tricky though, because we need to be careful!
// See how below that we are modifying the 'guess' array we get in?
// That means we would modify our previous solutions! No good!
// So here we need to return a copy
ArrayList<Integer> ans = cachedSolutions.get(n);
ArrayList<Integer> copy = new ArrayList<Integer>();
for (int i: ans) copy.add(i);
return copy;
}
ArrayList<Integer> best = null;
int bestInt = -1;
// THIS IS WRONG, can you figure out why it doesn't work?:
// for (int i = 1; i*i <= n; ++i)
for (int i = (int)Math.sqrt(n); i >= 1; --i)
{
// Check what happens if we use i^2 as part of our representation
ArrayList<Integer> guess = minLen(n - i*i);
// If we haven't selected a 'best' yet (best == null)
// or if our new guess is better than the current choice (guess.size() < best.size())
// update our choice of best
if (best == null || guess.size() < best.size())
{
best = guess;
bestInt = i;
}
}
best.add(bestInt);
// check... not needed unless you coded wrong
int sum = 0;
for (int i = 0; i < best.size(); ++i)
{
sum += best.get(i) * best.get(i);
}
if (sum != n)
{
throw new RuntimeException(String.format("n = %d, sum=%d, arr=%s\n", n, sum, best));
}
// New step: Save the solution to the global cache
cachedSolutions.put(n, best);
// Same deal as before... if you don't return a copy, you end up modifying your previous solutions
//
ArrayList<Integer> copy = new ArrayList<Integer>();
for (int i: best) copy.add(i);
return copy;
}
}
It took my program around ~5s to run for n = 100,000. Clearly there is more to be done if we want it to be faster, and to solve for larger n. The main issue now is that in storing the entire list of results of previous answers, we use up a lot of memory. And all of that copying! There is more you can do, like storing only an integer and a pointer to the subproblem, but I'll let you do that.
And by the by, 1000 = 30^2 + 10^2.
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.
I'm writing a program that is supposed to create chains of numbers and see witch one is the longest. The problem is that I run out of memory and I have no idea what eats all of the memory. Does anyone know were the problem is?
public static void main(String[] args) {
ArrayList<Integer> longestchain;
ArrayList<Integer> chain = new ArrayList<Integer>();
int size = 0;
int longestsize = 0;
int start;
int number = 0;
for(int i = 3; i < 1000000; i++)
{
start = i;
chain.clear();
chain.add(start);
size = 1;
while(true)
{
if(start == 1)
{
break;
}
if(iseven(start))
{
start = start / 2;
}
else
{
start = start*3 + 1;
}
chain.add(start);
size++;
}
if(size > longestsize)
{
longestsize = size;
longestchain = chain;
number = i;
}
//System.out.println(i);
}
System.out.println(number + ". " + longestsize);
}
public static boolean iseven(int n)
{
return (n % 2 == 0);
}
The problem is that when i=113383 you're running into an integer overflow. This results in an infinite loop that keeps adding to chain until you run out of heap space. Change chain and longestchain to ArrayList<Long>, start to long and iseven() to take long, and you'll solve that particular problem.
Another problem is that longestchain = chain assigns the reference, whereas you need to copy the contents. Otherwise the next iteration will wipe longestchain.
This will probably not be the solution, but a hint:
The default array size for an ArrayList is 10. Initialize your ArrayLists to a value closer to what you are expecting, or many array creations happen under the hood:
// e.g.
ArrayList<Integer> chain = new ArrayList<Integer>(50000);
Don't know why are you running out of memory, but I noticed a bug in your code - longestchain and chain point to the same object, so when you clear chain, you also clear longestchain. You can fix it by creating a new array with contents of chain instead of an assignment.