GC overhead limit exceeded - Arrays - java

i get this error after waiting long time for my code to execute and its pointing me to this method
public Iterable<Board> neighbors() {
Queue<Board> q = new LinkedList<>();
int n = dimension();
int x = 0, y = 0;
outer:
// do some stuff to get the x and y
if (y+1 < n) {
the line where i get the error -> int [][]arr = new int[n][n];
for (int i = 0; i < tiles.length; i++) {
arr[i] = Arrays.copyOf(tiles[i], n);
}
// do some stuff
Board br = new Board(arr);
if(!this.equals(br)) {
q.add(new Board(arr));
}
}
if (y-1 >= 0) {
int [][]arr = new int[n][n];
for (int i = 0; i < tiles.length; i++) {
arr[i] = Arrays.copyOf(tiles[i], n);
}
// do some stuff
Board br = new Board(arr);
if(!this.equals(br)) {
q.add(new Board(arr));
}
}
if (x-1 >= 0) {
int [][]arr = new int[n][n];
for (int i = 0; i < tiles.length; i++) {
arr[i] = Arrays.copyOf(tiles[i], n);
}
// do some stuff
Board br = new Board(arr);
if(!this.equals(br)) {
q.add(new Board(arr));
}
}
if (x+1 < n) {
int [][]arr = new int[n][n];
for (int i = 0; i < tiles.length; i++) {
arr[i] = Arrays.copyOf(tiles[i], n);
}
// do some stuff
Board br = new Board(arr);
if(!this.equals(br)) {
q.add(new Board(arr));
}
}
return q;
}
i basically need to copy tiles array and make changes to the copy "arr" but keep the tiles array without changing to use it later..i really don't like the way i'm doing it copying and pasting code i think its inefficient but no other way comes to my mind so i would like to know why i get this error "i know its because GC taking more time and not doing alot" but i want to know why its happening in this case also if there is better way to copy the array.
also i increased the heap memory to -Xmx1600m
Thanks for your time.

The Problem
It is likely that the problem arises from creating a lot of objects in a short period of time. See this answer for more information.
At the moment, one Board consist of at least four objects:
The Board itself
The array arr inside the board
The three arrays inside arr
Creating Less Objects
Our goal is to create fewer objects (arrays). Since you want to deal with small boards only, we could use one long to store the complete 3×3 board. A long has 64 bit. We use 64 / 9 = 7 bits per field to store the value on that field:
state = ... 0000100 0000011 0000010 0000001 0000000
4th field ↑ 2nd field ↑ 0th field
3rd field 1st field
The following class handles the bit operations.
class Board {
private final static int SIDE_LENGTH = 3;
private final static int FIELDS = SIDE_LENGTH * SIDE_LENGTH;
private final static int BITS_PER_FIELD = 64 / FIELDS;
private final static long FIELD_MASK = (1 << BITS_PER_FIELD) - 1;
private long state;
public Board() {
for (int field = 0; field < FIELDS; ++field) {
set(field, field);
}
}
/** Copy constructor. */
public Board(Board other) {
this.state = other.state;
}
public int get(int x, int y) {
return get(coordinatesToField(x, y));
}
public void set(int x, int y, int value) {
set(coordinatesToField(x, y), value);
}
private int coordinatesToField(int x, int y) {
return SIDE_LENGTH * y + x;
}
private int get(int field) {
return (int) ((state >>> (field * BITS_PER_FIELD)) & FIELD_MASK);
}
private void set(int field, int value) {
int shift = field * BITS_PER_FIELD;
state &= ~(FIELD_MASK << shift);
state |= (long) value << shift;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int field = 0; field < FIELDS; ++field) {
sb.append(get(field));
sb.append((field + 1) % SIDE_LENGTH == 0 ? "\n" : "\t");
}
return sb.toString();
}
// TODO implement equals and hashCode
}
When using this class, you don't have to deal with arrays anymore, which saves not only a lot of objects, but also the copy code in your prorgram.
The class also works for 1×1, 2×2, and 4×4 boards, but not for larger ones due to the 64 bit limit.
Usage Examples
public static void main(String[] args) {
// Create and print the initial board
// 0 1 2
// 3 4 5
// 6 7 8
Board b = new Board();
System.out.println(b);
// Copy an existing board
Bord copy = new Board(b);
// Set the upper right field to value 8
copy.set(2, 0, 8);
// Print the center field
// 4
Syste.out.println(copy.get(1, 1));
}
Additional Ideas
You even could avoid creating Board objects at all, and just store the long values. But that doesn't help when you are using generics (such as LinkedList) because of Java's auto boxing.
Also note that LinkedList wraps each entry in an additional node object. Maybe you can use a more efficient DataStructure like a circular buffer.
Depending on what you are doing, you might as well have a look at the Flyweight design pattern.

Related

How to copy data from old array to new array using while or for loop

I have this code but it prints null word 20 times.
Please help me understand why the elements are not copied instead.
public class StrList {
public void add(String y) {
if (currsize == elements.length) {
System.out.println("no more room to add " + y); // fix this to increase space instead!
}
int newSize = elements.length * 2;
newElements = new String [newSize];
for (int i = 0; i <currsize; i++){
elements = newElements; }
elements[currsize] = y;
currsize += 1; // or currsize++
}
I see a reassignment to elements done currSize times. Maybe you forgot a [i]?

Adding Numbers and printing the sum vertically using arrays

I have to create a program which adds two integers and prints the sum vertically.
For example, I have.
a=323, b=322.
The output should be:
6
4
5
I've created the code for when the integers are up to two digits, but I want it to work for at least three digits.
Below is the best I could think of.
It may be completely wrong, but the only problem I'm facing is the declaration of array.
It says that the array might not be initialized.
If I set it to null then also it won't assign values to it later.
I know maybe I'm making a big mistake here, but I'll really appreciate if anyone could help me out.
Please keep in mind that I must not use any other functions for this code.
Hope I'm clear.
public class Vert
{
public static void main(String args[])
{
int n,i=0,j,a=323,b=322;
int s[];
n=a+b;
while(n>9)
{
s[i]=n%10;
i++;
s[i]=n/10;
if(s[i]>9)
{
n=s[i];
}
}
j=i;
for(j=i;j>=0;j--)
{
System.out.println(+s[j]);
}
}
}
String conversion seems like cheating, so here's a Stack.
int a = 323, b = 322;
java.util.Stack<Integer> stack = new java.util.Stack<>();
int n = a + b;
while (n > 0) {
stack.push(n % 10);
n = n / 10;
}
while (!stack.isEmpty())
System.out.println(stack.pop());
If an array is required, you need two passes over the sum
int a = 323, b = 322;
// Get the size of the array
int n = a + b;
int size = 0;
while (n > 0) {
size++;
n = n / 10;
}
// Build the output
int s[] = new int[size];
n = a + b;
for (int i = size - 1; n > 0; i--) {
s[i] = n % 10;
n = n / 10;
}
// Print
for (int x : s) {
System.out.println(x);
}
To initialize an array, you need to specify the size of your array as next:
int s[] = new int[mySize];
If you don't know the size of your array, you should consider using a List of Integer instead as next:
List<Integer> s = new ArrayList<Integer>();
Here is how it could be done:
// Convert the sum into a String
String result = String.valueOf(a + b);
for (int i=0; i <result.length();i++) {
// Print one character corresponding to a digit here per line
System.out.println(result.charAt(i));
}
I'd do it like this:
int a = 322;
int b = 322;
int sum = a + b;
String s = Integer.toString(sum);
for(int i = 0; i < s.length(); i++) {
System.out.println(s.charAt(i));
}
But your problem looks like an array is required.
The steps are same as in my solution:
Use int values
Sum the int values (operation)
Convert the int value in an array/string
Output the array/string

I have an array that is being altered even though I have not specifically changed it

I am writing an AI to play Mancala and this is my method in which the AI's calculations are done by examining the outcomes of all 6 possible moves. I use the array staticBoardState to restore boardState (which stores the information about all of the holes on the board) back to its original values after each examination of move outcomes, but staticBoardState seems to be changing in odd ways even though I believe that I do not change it. I am a beginner amateur coder, so please ask questions if my code does not make sense. This is my code:
public int getBotCalc(int boardState[]) {
int[] staticBoardState = boardState;
double[] movePoints = new double[6];
int initialScore = boardState[6];
int scorePoints;
int freeTurnPoints;
double bestMovePoints;
int bestMove;
for(int f = 0; f <= 5; f++) {
boardState = staticBoardState;
int botChoice = f;
int botHole = boardState[botChoice];
boardState[botChoice] = 0;
for(int g = 0; g < botHole; g++) {
botChoice++;
if(botChoice>12) {
botChoice = 0;
}
boardState[botChoice]++;
}
if(botChoice<=5&&boardState[botChoice]==1&&boardState[12-botChoice]>=1) {
boardState[6] += boardState[12 - botChoice] + 1;
boardState[botChoice] = 0;
boardState[12 - botChoice] = 0;
}
scorePoints = boardState[6] - initialScore;
if(botChoice==6) {
freeTurnPoints = 1;
} else {
freeTurnPoints = 0;
}
movePoints[f] = scorePoints + (1.5 * freeTurnPoints);
}
bestMovePoints = movePoints[0];
bestMove = 0;
for(int f = 1; f <= 5; f++) {
if(movePoints[f]>bestMovePoints) {
bestMovePoints = movePoints[f];
bestMove = f;
}
}
boardState = staticBoardState;
return bestMove;
}
Any help is greatly appreciated.
It looks like you're confusing value-type assignment with reference assignment. When you write
staticBoardState = boardState
what happens is that staticBoardState simply holds a reference to the array in memory that boardState is also already referring to. Not they both refer to the same array in memory, which is why staticBoardState is apparently being modified through the use of boardState. What you need to do to fix this is allocate staticBoardState as a new array and explicitly copy its contents, for example using a boardState.clone(), and perform similar copying each time you want to restore your boardState.

Minmax for ConnectFour

I am trying to implement a minmax algorithm to create an AI for connect four. I'm having quite a bit of trouble with it though as I feel like I have overcomplicated things (and it doesn't work properly), perhaps someone here can help. I'm going to post my code first and then the issue I'm having with it below.
This is the initial call to the minmax algorithm
public int getColumnForMove(ConnectFour game)
{
game.minimax(2, game.getCurrentPlayer(), game);
int column = game.getBestMove();
return column;
}
This is the initial minimax method (it is inside the ConnectFour class which is not where the initial method is called from that is in a separate AI class) that is called and a subclass that holds each column the user moves into as well as the min/max'ed score if it moves into that column.
class ColumnsAndScores
{
int column;
int score;
ColumnsAndScores(int column, int score)
{
this.column = column;
this.score = score;
}
}
List<ColumnsAndScores> cas = new ArrayList<>();
public void minimax(int depth, int turn, ConnectFour game)
{
cas = new ArrayList<>();
minimaxHelper(depth, depth, turn, game);
}
The following are methods that get the min or max score from each set of possible moves:
public int getMax(List<Integer> list)
{
int max = Integer.MIN_VALUE;
int index = -1;
for (int i = 0; i < list.size(); i++)
{
if (list.get(i) > max)
{
max = list.get(i);
index = i;
}
}
return list.get(index);
}
public int getMin(List<Integer> list)
{
int min = Integer.MAX_VALUE;
int index = -1;
for (int i = 0; i < list.size(); i++)
{
if (list.get(i) < min)
{
min = list.get(i);
index = i;
}
}
return list.get(index);
}
This is the actual minimax method (it has a bunch of commented out code that shows it should return a range of values depending on how good the board is if its not a clear cut win or loss but right now I'm just trying to have it make decisions based on a win or loss (if none of that happens in the requested depth it makes a random move)).
public int minimaxHelper(int originalDepth, int depth, int turn, ConnectFour game)
{
//holds future game states
ConnectFour futureGameState;
//holds the current scores
List<Integer> scores = new ArrayList<>();
//if (not at the lowest depth)
if (depth !=0)
{
if (checkForWin(turn))
{
//return Integer.MAX_VALUE or Integer.MIN_VALUE respectively based on who's turn it is
return (turn % 2 == 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
//recursively call getColumnForMove(depth--, otherTurn) for each column if the column isnt full
for (int i = 1; i <= ConnectFour.NUM_OF_COLUMNS; i++)
{
futureGameState = new ConnectFour();
futureGameState.setCurrentGameState(game.getCurrentGameState());
futureGameState.setCurrentPlayer(game.getCurrentPlayer());
if (futureGameState.isValidMove(i))
{
futureGameState.makeMove(i);
futureGameState.switchPlayer();
scores.add(minimaxHelper(originalDepth, depth - 1, futureGameState.getCurrentPlayer(), futureGameState));
}
else //if move isnt valid return the worst possible value so this column doesnt get chosen
{
return (turn % 2 == 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
if (depth == originalDepth)
{
ColumnsAndScores newScore;
if (turn % 2 == 0)
newScore = new ColumnsAndScores(i, getMax(scores));
else
newScore = new ColumnsAndScores(i, getMin(scores));
cas.add(newScore);
}
}
if (turn % 2 == 0)
return getMax(scores);
else
return getMin(scores);
}
else
{
if (checkForWin(turn))
{
//return Integer.MAX_VALUE or Integer.MIN_VALUE respectively based on who's turn it is
return (turn % 2 == 0) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
else
{
return 0;
}
//else
//if 3 in a row with 2 open spaces that have pieces under those spaces
//return 100
//else if 3 in a row with 1 open space that has a piece under that space
//return 80;
//else if 3 in a row
//return 60;
//else if 2 in a row
//return 40
//else
//return 0
}
}
and finally this is a method that is called by the AI to get the best move from the list that minimax added the ColumnAndScores too.
public int getBestMove()
{
int highestScore = Integer.MIN_VALUE;
int best = -1;
for (int i = 0; i < cas.size(); ++i) {
if (highestScore < cas.get(i).score) {
highestScore = cas.get(i).score;
best = i;
}
}
if (highestScore == 0)
return 1 + ((int) (Math.random() * 7));
else
return best;
}
While I believe there are a couple of logic errors the thing I am having the most difficulty with at the moment is that when I dofutureGameState = new ConnectFour();
futureGameState.setCurrentGameState(game.getCurrentGameState());
This should put it into a separate instance so that when I then make a move it should only last for that branch of the tree and not corrupt the actual game being played but that isn't the case. It is changing the actual state of the game being passed in.
The issue is most probably caused by the implementation of ConnectFour, something like
private int[][] state;
public void setCurrentGameState(int[][] newState) {
this.state = newState;
}
That's okay, but causes your "copy" of the game state to actually reference the same int[][] state, thus any modifications to it will apply to both states. What you want is
public class ConnectFour implements Cloneable<ConnectFour> {
private static final int NUM_ROWS = 6;
private static final int NUM_COLS = 7;
private int[][] state = new int[NUM_ROWS][NUM_COLS];
// ...
public ConnectFour clone() {
int[][] stateCopy = new int[NUM_ROWS][NUM_COLS];
for (int i = 0; i < NUM_ROWS; i++)
for (int j = 0; j < NUM_COLS; j++)
stateCopy[i][j] = this.state[i][j];
ConnectFour cloned = new ConnectFour();
cloned.setCurrentGameState(stateCopy);
// copy other fields over to cloned
return cloned;
}
}
I'm just going to address one issue. You should try not to have too many per question, and include the code relevant to your question, such as your ConnectFour class here.
If you want to make a copy of the board you can modify without changing the original, you need to make a deep copy, not a copy of the reference. To make a shallow copy of your house, you make a copy of your house key. If you give it to someone, you shouldn't be surprised to see changes when you get home. To make a deep copy of your house, you get a second lot and build a new house from blueprints and photos of your house. If you give a key to the new house to someone, he/she might not notice the difference immediately, but any changes shouldn't affect you directly, and changes you make won't affect the recipient.
"Deep copy" is actually ambiguous because your object may contain object members that have object members. When you make a deep copy, you have to decide whether to make deep copies or shallow copies of any member objects. If your ConnectFour class contains an ArrayList of Move objects, each of which is a wrapper for an int representing a column, you have 3 choices:
You can copy a reference to the ArrayList.
You can make a new ArrayList with references to the same set of moves.
You can make a new ArrayList with references to copies of the moves.
Anyway, my guess is that you don't yet have nested member objects, so your deep copy method can look something like the following:
public class ConnectFour{
private int[][] board = new int[6][7];
public setCurrentGameState(int[][] state){
for(int i = 0; i<6; i++)
for(int j=0; j<7; j++)
board[i][j] = state[i][j];
}
...

About the CharMatcher.WHITESPACE implementation

When i looked up the implementation of CharMatcher and notice a field WHITESPACE_MULTIPLIER=1682554634 , then i set this value to 1582554634 , running the testcase CharMatcherTest#testWhitespaceBreakingWhitespaceSubset, of course it failed.
After that I changed testWhitespaceBreakingWhitespaceSubset to only invoke WHITESPACE.apply((char)c) without assert, print the index in the method of WHITESPACE.matches
int index=(WHITESPACE_MULTIPLIER * c) >>> WHITESPACE_SHIFT)
finally found that index collided after changed the WHITESPACE_MULTIPLIER from 1682554634 to 1582554634
No doubt, 1682554634 is well designed , my question is how can I infer this "magic number"?`
Upon Martin Grajcar's proposal, I try to write the "magic number generator" as follows and worked :
char[] charsReq = WHITESPACE_TABLE.toCharArray();
Arrays.sort(charsReq);
OUTER:
for (int WHITESPACE_MULTIPLIER_WANTTED = 1682553701; WHITESPACE_MULTIPLIER_WANTTED <= 1682554834; WHITESPACE_MULTIPLIER_WANTTED++) {
int matchCnt = 0;
for (int c = 0; c <= Character.MAX_VALUE; c++) {
int position = Arrays.binarySearch(charsReq, (char) c);
char index = WHITESPACE_TABLE.charAt((WHITESPACE_MULTIPLIER_WANTTED * c) >>> WHITESPACE_SHIFT);
if (position >= 0 && index == c) {
matchCnt++;
} else if (position < 0 && index != c) {
matchCnt++;
} else {
continue OUTER;
}
}
// all valid
if ((matchCnt - 1) == (int) (Character.MAX_VALUE)) {
System.out.println(WHITESPACE_MULTIPLIER_WANTTED);
}
}
if changed the sequence of characters(swap \u2001 \u2002 position) in WHITESPACE_TABLE the algorithms has no solution (changed the loop end condition to Integer.MAX_VALUE).
as the IntMath.gcd implementation is refer to http://en.wikipedia.org/wiki/Binary_GCD_algorithm
my question is : where can i find the material of CharMatcher.WHITESPACE.match implementation?
I'm not sure if the generator still exists somewhere, but it can be recreated easily. The class Result contains the data used in the implementation of CharMatcher.WHITESPACE:
static class Result {
private int shift;
private int multiplier;
private String table;
}
// No duplicates allowed.
private final String allMatchingString = "\u2002\r\u0085\u200A\u2005\u2000"
+ "\u2029\u000B\u2008\u2003\u205F\u1680"
+ "\u0009\u0020\u2006\u2001\u202F\u00A0\u000C\u2009"
+ "\u2004\u2028\n\u2007\u3000";
public Result generate(String allMatchingString) {
final char[] allMatching = allMatchingString.toCharArray();
final char filler = allMatching[allMatching.length - 1];
final int shift = Integer.numberOfLeadingZeros(allMatching.length);
final char[] table = new char[1 << (32 - shift)];
OUTER: for (int i=0; i>=0; ++i) {
final int multiplier = 123456789 * i; // Jumping a bit makes the search faster.
Arrays.fill(table, filler);
for (final char c : allMatching) {
final int index = (multiplier * c) >>> shift;
if (table[index] != filler) continue OUTER; // Conflict found.
table[index] = c;
}
return new Result(shift, multiplier, new String(table));
}
return null; // No solution exists.
}
It generates a different multiplier, but this doesn't matter.
In case no solution for a given allMatchingString exists, you can decrement shift and try again.

Categories