StackOverflowError in Java while working with arrays and recursion - java

The lifeCycle-method in my MatrixCreatureContainer-class throws a stack overflow error after about 3-4k iterations. Why is that? I assume it has something to do with memory allocation, but I cannot figure out how to solve it. I tried reading about the java garbage collector, but nothing I did seemed to help.
public class MatrixCreatureContainer {
private final static int NUMBER_OF_CREATURES = 20;
private static Random rand;
public static void main(String[] args){
rand = new Random();
List<MatrixCreature> population = new ArrayList<MatrixCreature>();
for(int i = 0; i < NUMBER_OF_CREATURES ; i++){
population.add(new MatrixCreature());
}
Collections.sort(population);
lifeCycle(population,0, 4000);
}
private static void lifeCycle(List<MatrixCreature> population, int generation, int iterations){
if (generation == iterations) return;
List<MatrixCreature> newPopulation = new ArrayList<MatrixCreature>();
while(population.size() != 0){
MatrixCreature mother = population.remove(rand.nextInt(population.size()));
MatrixCreature father = population.remove(rand.nextInt(population.size()));
newPopulation.add(new MatrixCreature(mother,father));
newPopulation.add(new MatrixCreature(mother,father));
newPopulation.add(new MatrixCreature(mother,father));
}
Collections.sort(newPopulation);
newPopulation = newPopulation.subList(0,NUMBER_OF_CREATURES);
lifeCycle(newPopulation,generation + 1, iterations);
}
}
The MatrixCreature-class basically only holds an integer array (int[]) of 20 integers. The constructor takes in two other matrixCreatures, and combines the arrays of two the matrixCreatures given, with a small chance of mutation. Each matrixCreature gets a score (where 0 is the best) of how close the sum of the numbers in the array is to 55. It's that score the population of each generation in the MatrixCreatureContainer is sorted by, such that the 20 "best" of each generation survives.
I can post the code to the MatrixCreature-class if it's relevant.
Thanks in advance :)
-Boye

With this call:
lifeCycle(population,0, 4000);
you're basically asking for a stack with 4000 frames (at least - see later). That isn't totally beyond reason, but in fact there's no reason to make this recursive at all. You can easily just change the method to be iterative - and even remove the generation parameter:
private static void lifeCycle(List<MatrixCreature> population, int iterations) {
for (int generation = 0; generation < iterations; generation++) {
// Body of previous method here
}
}
Additionally, you keep creating views using newPopulation = newPopulation.subList(...). You probably don't want to do that - it means that every operation will need to go through a huge number of stack frames, as each call to a view will delegate to its underlying list... and if that's another view, it needs to keep going, etc. Icky. If those view calls actually require a couple of stack frames per "layer" you could easily end up with a stack of around 12K calls in your original code...
I would suggest creating a copy of the relevant portion of the list on each iteration instead - and then returning the final list.

Each lifecycle starts with its own population and creates a new one for the next generation (with always 3 creatures inside; is this intended ?)
So after 4000 iterations you have those 4000 population lists hanging around as they never went out of scope.

Java does not support tail-recursion optimization, so your last line in the lifeCycle method creates a new stack frame for every iteration.
With your memory size, and the number of local variables, you have determined that you can have about 4000 stack frames. Solution: rewrite your method using a for-loop. It's a small change, you don't really need recursion for your method.

Related

Resizing an Array with a Dedicated method for upsize and downsizing

I'm fairly new to coding and am struggling with an assignment for my class. The program takes a user input for the size of an Array and prompts the user to enter each value 1 at a time. The array size starts at 3 and if the array needs to be bigger when the array has filled a new array that's 2x size is created and all info is copied into it. I was able to figure out this part but I just can't see what I'm doing wrong in the downsizing part. After the info is copied I have to remove the trailing zeroes. I think I have the downsize method right but I don't know if I'm calling it right
import java.util.Scanner;
public class Lab6 {
public static void main(String args[]) {
int[] myarray = new int[3];
int count = 0;
int limit, limitcount = 1;
Scanner kbd = new Scanner(System.in);
System.out.print("How many values would you like to enter? ");
limit = kbd.nextInt();
while (limitcount <= limit) {
System.out.println("Enter an integer value ");
int input = kbd.nextInt();
limitcount++;
if (count < myarray.length) {
myarray[count] = input;
}
else {
myarray = upsize(myarray);
myarray[count] = input;
}
count++;
}
myarray = downsize(myarray, count)
printArray(myarray);
System.out.println("The amount of values in the arrays that we care about is: " + count);
}
static int[] upsize(int[] array) {
int[] bigger = new int[array.length * 2];
for (int i =0;i<array.length; i++) {
bigger[i] = array[i];
}
return bigger;
}
static void printArray( int[] array ) {
for ( int number : array ) {
System.out.print( number + " ");
}
System.out.println();
}
static int[] downsize(int[] array,int count) {
int[] smaller = new int[count];
for (int i =0; i<count; i++) {
smaller[i] = array[i];
}
return array;
}
}
Giving you a full response rather than a comment since you're new here and I don't want to discourage you with brevity which could be misunderstood.
Not sure what happened to your code when you pasted it in here, you've provided everything but the format is weird (the 'code' bit is missing out a few lines at the top and bottom). Might be one to double-check before posting. After posting, I see that someone else has already edited your code to fix this one.
You're missing a semi-colon. I'm not a fan of handing out answers, so I'll leave you to find it :) If you're running your code in an IDE, it should already be flagging that one up for you. If you're not, why on earth not??? IntelliJ is free, easy to get going with, and incredibly helpful. There are others out there as well which different folk prefer :) An IDE will help you spot all sorts of useful things quickly.
I have now run your code, and you do have a problem! It's in your final method, downsize(). Look very, very carefully at the return statement ;) Your questions suggests you aren't actually sure whether or not this method is right, which makes me wonder: have you actually run this code with different inputs to see what results you get? Please do that.
Style-wise: blank lines between methods would make the code easier to look at, by providing a visual gap between components. Please be consistent with putting your opening { on the same line as the method signature, and with having spaces between items, e.g. for (int i = 0; i < count; i++) rather than for (int i =0; i<count; i++). The compiler couldn't care less, but it is easier for humans to look at and just makes it look like you did care. Always a good thing!
I think it is awesome that you are separating some of the work into smaller methods. Seriously. For extra brownie points, think about how you could move that while() block into its own method, e.g. private int[] getUserData(int numberOfItems, Scanner scanner). Your code is great without this, but the more you learn to write tiny units, the more favours you will be doing your future self.
Has your class looked at unit testing yet? Trust me, if not, when you get to this you will realise just how important point 5 can be. Unit tests will also help a lot with issues such as the one in point 3 above.
Overall, it looks pretty good to me. Keep going!!!
Simple mistake in your downsize method. If you have an IDE like Eclipse, Intellij, etc. you would have seen it flagged right away.
return array; // should return smaller
I have a few suggestions since you mentioned being new to coding.
The "limitcount" variable can be removed and substituted with "count" at every instance. I'll leave it to you to figure that out.
Try using more descriptive and understandable variable names. Other people will read your code (like now) and appreciate it.
Try to use consistent spacing/indentation throughout your code.
Your upsize method can be simplified using a System.arraycopy() call which generally performs better and avoids the need for writing out a for loop. You can rewrite downsize in a similar manner.
static int[] upsize(int[] array) {
int[] bigger = new int[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
return bigger;
}
Edit: All good points by sunrise above - especially that you've done well given your experience. You should set up an IDE when you have the time, they're simple to use and invaluable. When you do so you should learn to step through a debugger to explore the state of your program over time. In this case you would have noticed that the myarray variable was never reassigned after the downsize() call, quickly leading you to a solution (if you had missed the warning about an unused "smaller" array).

Is it better to use arrays or a queue when merging two sorted arrays?

I'm working on a programming practice site that asked to implement a method that merges two sorted arrays. This is my solution:
public static int[] merge(int[] arrLeft, int[] arrRight){
int[] merged = new int[arrRight.length + arrLeft.length];
Queue<Integer> leftQueue = new LinkedList<>();
Queue<Integer> rightQueue = new LinkedList<>();
for(int i = 0; i < arrLeft.length ; i ++){
leftQueue.add(arrLeft[i]);
}
for(int i = 0; i < arrRight.length; i ++){
rightQueue.add(arrRight[i]);
}
int index = 0;
while (!leftQueue.isEmpty() || !rightQueue.isEmpty()){
int largerLeft = leftQueue.isEmpty() ? Integer.MAX_VALUE : leftQueue.peek();
int largerRight = rightQueue.isEmpty() ? Integer.MAX_VALUE : rightQueue.peek();
if(largerLeft > largerRight){
merged[index] = largerRight;
rightQueue.poll();
} else{
merged[index] = largerLeft;
leftQueue.poll();
}
index ++;
}
return merged;
}
But this is the official solution:
public static int[] merge(int[] arrLeft, int[] arrRight){
// Grab the lengths of the left and right arrays
int lenLeft = arrLeft.length;
int lenRight = arrRight.length;
// Create a new output array with the size = sum of the lengths of left and right
// arrays
int[] arrMerged = new int[lenLeft+lenRight];
// Maintain 3 indices, one for the left array, one for the right and one for
// the merged array
int indLeft = 0, indRight = 0, indMerged = 0;
// While neither array is empty, run a while loop to merge
// the smaller of the two elements, starting at the leftmost position of
// both arrays
while(indLeft < lenLeft && indRight < lenRight){
if(arrLeft[indLeft] < arrRight[indRight])
arrMerged[indMerged++] = arrLeft[indLeft++];
else
arrMerged[indMerged++] = arrRight[indRight++];
}
// Another while loop for when the left array still has elements left
while(indLeft < lenLeft){
arrMerged[indMerged++] = arrLeft[indLeft++];
}
// Another while loop for when the right array still has elements left
while(indRight < lenRight){
arrMerged[indMerged++] = arrRight[indRight++];
}
return arrMerged;
}
Apparently, all the other solutions by users on the site did not make use of a queue as well. I'm wondering if using a Queue is less efficient? Could I be penalized for using a queue in an interview for example?
As the question already states that the left and right input arrays are sorted, this gives you a hint that you should be able to solve the problem without requiring a data structure other than an array for the output.
In a real interview, it is likely that the interviewer will ask you to talk through your thought process while you are coding the solution. They may state that they want the solution implemented with certain constraints. It is very important to make sure that the problem is well defined before you start your coding. Ask as many questions as you can think of to constrain the problem as much as possible before starting.
When you are done implementing your solution, you could mention the time and space complexity of your implementation and suggest an alternative, more efficient solution.
For example, when describing your implementation you could talk about the following:
There is overhead when creating the queues
The big O notation / time and space complexity of your solution
You are unnecessarily iterating over every element of the left and right input array to create the queues before you do any merging
etc...
These types of interview questions are common when applying for positions at companies like Google, Microsoft, Amazon, and some tech startups. To prepare for such questions, I recommend you work through problems in books such as Cracking the Coding Interview. The book covers how to approach such problems, and the interview process for these kinds of companies.
Sorry to say but your solution with queues is horrible.
You are copying all elements to auxiliary dynamic data structures (which can be highly costly because of memory allocations), then back to the destination array.
A big "disadvantage" of merging is that it requires twice the storage space as it cannot be done in-place (or at least no the straightforward way). But you are spoiling things to a much larger extent by adding extra copies and overhead, unnecessarily.
The true solution is to copy directly from source to destination, leading to simpler and much more efficient code.
Also note that using a sentinel value (Integer.MAX_VALUE) when one of the queues is exhausted is a false good idea because it adds extra comparisons when you know the outcome in advance. It is much better to split in three loops as in the reference code.
Lastly, your solution can fail when the data happens to contain Integer.MAX_VALUE.

java multithread loop with collecting results

sorry for limited code, as i have quite no idea how to do it, and parts of the code are not a code, just an explanation what i need. The base is:
arrayList<double> resultTopTen = new arrayList<double();
arrayList<double> conditions = new arrayList<double(); // this arrayList can be of a very large size milion+, gets filled by different code
double result = 0;
for (int i = 0, i < conditions.size(), i++){ //multithread this
loopResult = conditions.get(i) + 5;
if (result.size() < 10){
resultTopTen.add(loopResult);
}
else{
//this part i don't know, if this loopResult belongs to the TOP 10 loopResults so far, just by size, replace the smallest one with current, so that i will get updated resultTopTen in this point of loop.
}
}
loopResult = conditions.get(i) + 5; part is just an example, calculation is different, in fact it is not even double, so it is not possible simply to sort conditions and go from there.
for (int i = 0, i < conditions.size(), i++) part means i have to iterate through input condition list, and execute the calculation and get result for every condition in conditionlist, Don't have to be in order at all.
The multithreading part is the thing i have really no idea how to do, but as the conditions arrayList is really large, i would like to calculate it somehow in parallel, as if i do it just as it is in the code in a simple loop in 1 thread, i wont get my computing resources utilized fully. The trick here is how to split the conditions, and then collect result. For simplicity if i would like to do it in 2 threads, i would split conditions in half, make 1 thread do the same loop for 1st half and second for second, i would get 2 resultTopTen, which i can put together afterwards, But much better would be to split the thing in to as many threads as system resources provide(for example until cpu ut <90%, ram <90%). Is that possible?
Use parallel stream of Java 8.
static class TopN<T> {
final TreeSet<T> max;
final int size;
TopN(int size, Comparator<T> comparator) {
this.max = new TreeSet<>(comparator);
this.size = size;
}
void add(T n) {
max.add(n);
if (max.size() > size)
max.remove(max.last());
}
void combine(TopN<T> o) {
for (T e : o.max)
add(e);
}
}
public static void main(String[] args) {
List<Double> conditions = new ArrayList<>();
// add elements to conditions
TopN<Double> maxN = conditions.parallelStream()
.map(d -> d + 5) // some calculation
.collect(() -> new TopN<Double>(10, (a, b) -> Double.compare(a, b)),
TopN::add, TopN::combine);
System.out.println(maxN.max);
}
Class TopN holds top n items of T.
This code prints minimum top 10 in conditions (add 5 to each element).
Let me simplify your question, from what I understand, please confirm or add:
Requirement: You want to find top10 results from list called conditions.
Procedure: You want multiple threads to process your logic of finding the top10 results and accumulate the results to give top10.
Please also share the logic you want to implement to get top10 elements or it is just a descending order of list and it's top 10 elements.

Java copy multi-d array, random errors

I have been debugging my implementation of Game of Life, and my main problem looks like its coming from how I use arrays.
public boolean[][] oldState;
public boolean[][] newState;
private boolean gameState = true;
public LifeBoard(Seed seed) {
oldState = seed.getSeed();
newState = new boolean[oldState.length][oldState[0].length];
run();
}
public void run() {
//debug code to run for x generations
for (int i = 0; i < 5; i++) {
BoardPrinter.print(oldState);
evaluateCells();
oldState = newState;
}
BoardPrinter.print(oldState);
System.out.println("game over");
}
the boolean[][] from Seed is a 5x5 grid, all false (dead) except the 3 horizontal middle cells in the middle row
00000
00000
0+++0
00000
00000
evaluateCells() looks at each cell in the grid, looks at the 8 cells around it, counts them, and based on the number of neighbors it writes a new value to newState.
What should happen: use oldState to calculate newState, copy newState to oldState, then go back through newState, writing each cell again based on the new oldState.
What really happens: the first generation works correctly, but after that the results are increasingly weird, it evaluates cells to be false that I know to be true, etc. The problem seems to be in the way I am copying the arrays.
If I initialize a 3rd blank array blankState[][] = new boolean[5][5];
and during the loop in run say:
public void run() {
//debug code to run for x generations
for (int i = 0; i < 5; i++) {
BoardPrinter.print(oldState);
evaluateCells();
oldState = newState;
newState = blankState;
}
BoardPrinter.print(oldState);
System.out.println("game over");
}
...then the game works correctly for an additional 1 generation, then the weird garbage returns.
I have 2 questions: first, it looks like I have to use System.arraycopy(), but unless someone tells me about a version for multidimensional arrays, I will have to write a simple loop.
My real question: why do you have to use this special System method to copy an array? Why can't you use the = operator?
EDIT: the conceptual answer is accepted below. Here is the code that solved the implementation problem:
for (int n = 0; n < oldState.length; n++) {
System.arraycopy(newState[n], 0, oldState[n], 0, oldState.length);
}
for (int t = 0; t < newState.length; t++) {
System.arraycopy(blankState[t], 0, newState[t], 0, newState.length);
}
Also for the record, System.arraycopy(boolean[][], 0, boolean[][], 0, boolean.length); did not work correctly, you have to iterate through each line.
My real question: why do you have to use this special System method to copy an array? Why can't you use the = operator?
This is actually an important Java lesson, so pay attention. Are you paying attention? Good. This will be important in the future.
Now, this applies for all objects in Java, as arrays are objects. If you use = then you only set the reference, and encounter all kinds of fun as seen here. Namely, if I have a simple class with a getter and setter called TestClass with a public int test, then the following code will work:
TestClass t=new TestClass();
t.test=1;
TestClass t1=t;
t1.test=6;
System.out.println(t.test); //prints 6.
Now, why? Because the references t and t1 point to the same object on the heap. And this holds for arrays as well. To get copies, you need to perform tasks that are object-specific. Some let you call clone() directly(Cloneable interface), some let you pass another instance into the constructor to get a new instance(Like the ArrayList constructor), and some use utility methods, like arrays here.
evaluateCells() looks at each cell in the grid, looks at the 8 cells
around it, counts them, and based on the number of neighbors it writes
a new value to newState.
What should happen: use oldState to calculate newState, copy newState
to oldState, then go back through newState, writing each cell again
based on the new oldState.
What really happens: the first generation works correctly, but after
that the results are increasingly weird, it evaluates cells to be
false that I know to be true, etc. The problem seems to be in the way
I am copying the arrays.
Without seeing your bit shifting code, I'd bet this is caused by a higher level problem. Most likely your bit shifting algorithm has a bug somewhere.
Have you stepped through your code with Eclipse's debugger? If not, read this tutorial and try a few different seeds. This will tell you where, if anywhere, in your algorithm the bit shift error occurs.
http://www.vogella.com/articles/EclipseDebugging/article.html

Procedural World Generation

I am generating my world (random, infinite and 2d) in sections that are x by y, when I reach the end of x a new section is formed. If in section one I have hills, how can I make it so that in section two those hills will continue? Is there some kind of way that I could make this happen?
So it would look something like this
1221
1 = generated land
2 = non generated land that will fill in the two ones
I get this now:
Is there any way to make this flow better?
This seems like just an algorithm issue. Your generation mechanism needs a start point. On the initial call it would be say 0, on subsequent calls it would be the finishing position of the previous "chunk".
If I was doing this, I'd probably make the height of the next point plus of minus say 0-3 from the previous, using some sort of distribution - e.g. 10% of the time it's +/1 3, 25% of the time it is +/- 2, 25% of the time it is 0 and 40% of the time it is +/- 1.
If I understood your problem correctly, here is a solution:
If you generated the delta (difference) between the hills and capped at a fixed value (so changes are never too big), then you can carry over the value of the last hill from the previous section when generating the new one and apply the first randomly genenarted delta (of the new section) to the carried-over hill size.
If you're generating these "hills" sequentially, I would create an accessor method that provides the continuation of said hill with a value to begin the next section. It seems that you are creating a random height for the hill to be constrained by some value already when drawing a hill in a single section. Extend that functionality with this new accessor method.
My take on a possible implementation of this.
public class DrawHillSection {
private int index;
private int x[50];
public void drawHillSection() {
for( int i = 0; i < 50; i++) {
if (i == 0) {
getPreviousHillSectionHeight(index - 1)
}
else {
...
// Your current implementation to create random
// height with some delta-y limit.
...
}
}
}
public void getPreviousHillSectionHeight(int index)
{
return (x[49].height);
}
}

Categories