Java 1 million index array of 1x3 arrays - memory and efficiency - java

I am doing some coding practice during my free time using some coding challenges posted on AdventOfCode.com
Challenge #6 involves turning 1 million (virtual) lights on and off with a set of hundreds of instructions. I find myself unable to even populate an ArrayList that is large enough to handle all of these "lights"
I decided to start the challenge by making a 1000x1000 array of 1x3 arrays which contain "x-coordinate" "y-coordinate" "on/off"
I have very limited experience with java efficiency (besides common sense) and I would greatly appreciate some direction.
Here is the challenge if you are interested: http://adventofcode.com/day/6
And here is my code so far, which sadly does not run past index ~7000 on my craptop (4gb RAM) - i do have a fancy desktop at home though.
I would appreciate some guidance regarding how to approach this problem with better memory and speed efficiency. I am a learning programmer and I will appreciate legitimately any guidance, whatever it may be. (Links/locations for learning are appreciated as well)
import java.util.ArrayList;
public class Day6 {
public static void main(String[] args) {
ReadFileClass myReaderClass = new ReadFileClass();
ArrayList<String> allLines = myReaderClass.readFile("/C:/Users/Steven/Desktop/Day6.txt");
ArrayList<ArrayList<Object>> lightArray = new ArrayList<>();
for(int i = 0; i<=999999; i++){
if(i%1000 ==0) System.out.println(i); //To see how fast/far the array populates
int x;
int y;
String status = "off";
for(int j = 0; j<=999; j++){
x = j;
y = i%999;
ArrayList<Object> innerArray = new ArrayList<>();
innerArray.add(x);
innerArray.add(y);
innerArray.add(status);
lightArray.add(innerArray);
}
}
System.out.println(lightArray);
}
}
Edit 1: I dunno what happened with the ArrayLists exactly (garbage accumulation maybe?) but I switched to a byte array and handle coordinate-->index conversions with a method now and I am smooth sailing. I'll post my final code once I'm finished fixing a small algorithm mistake (I misread the problem slightly). Thank you for your various suggestions!
Edit 2: My code works as intended now. Here it is, for anyone who is interested :)
import java.util.ArrayList;
public class Day6 {
public static void main(String[] args) {
ReadFileExample myReaderClass = new ReadFileExample();
ArrayList<String> allLines = myReaderClass.readFile("/C:/Users/Steven/Desktop/Day6.txt");
byte[] lightArray = new byte[1000000];
for(int i = 0; i<=999999; i++){
lightArray[i] = 0;
}
for(String s : allLines){
int x1, x2, y1, y2;
int indexOfFirstNumber = -1;
String nums = "0123456789";
for(int i = 0; i<s.length();i++){
if(nums.indexOf(s.substring(i,i+1))!=-1){
indexOfFirstNumber = i;
break;
}
}
int IOfirstComma = s.indexOf(",");
int indexOfThrough = s.indexOf("through");
int indexOfX2 = indexOfThrough+8;
int IOsecondComma = s.indexOf(",", IOfirstComma+1);
x1 = Integer.parseInt(s.substring(indexOfFirstNumber, IOfirstComma));
y1 = Integer.parseInt(s.substring(IOfirstComma+1, s.indexOf(" ", IOfirstComma)));
x2 = Integer.parseInt(s.substring(indexOfX2, IOsecondComma));
y2 = Integer.parseInt(s.substring(IOsecondComma+1,s.length()));
ArrayList<Integer> indexesToChange = coordinateRangeToIndexArray(x1, y1, x2, y2);
if(s.substring(0,8).equals("turn off")){
for(int i : indexesToChange){
lightArray[i] = 0;
}
}
else if(s.substring(0,7).equals("turn on")){
for(int i : indexesToChange){
lightArray[i] = 1;
}
}
else if(s.substring(0,6).equals("toggle")){
for(int i : indexesToChange){
if(lightArray[i] == 1){
lightArray[i] = 0;
}
else{
lightArray[i] = 1;
}
}
}
else{
System.out.println("Error: Command not recognized");
}
}
//calculate number of lights that are on
int totalNumLightsOn = 0;
for(int i = 0; i<lightArray.length;i++){
if(lightArray[i] == 1){
totalNumLightsOn++;
}
}
System.out.println(totalNumLightsOn);
}
static ArrayList<Integer> coordinateRangeToIndexArray(int x1, int y1, int x2, int y2){
//Given two coordinates, create an array of all the indexes the smallest possible rectangle encompasses
final int ROW_SIZE = 1000;
ArrayList<Integer> affectedIndexes = new ArrayList<>();
for(int row = x1; row<=x2; row++){
for(int column = y1; column<=y2; column++){
affectedIndexes.add(column*ROW_SIZE+row+1);
}
}
return affectedIndexes;
}
}

The most memory efficient way would be to use a BitSet, in which case it would take you 1,000,000 bits (or one megabit) of memory (and the obvious additional few bytes) to store the on/off status. The individual bits can be operated on easily with the included methods.
Congratulations on solving your challenge.

1.000.000 Objects of each 1kb should take about 1.000 mb or ~ 1gb.
When I read your post I thought
Map <Integer,Lightswitch> = new HashMap <Integer,Lightswitch>();
Maybe start with a basic (empty) Lightswitch
public class Lightswitch {
}
and add 1.000.000 of those into the map.
Or (just thinking):
byte [] switches = new byte [1000000];
And then the byte value represents the lightswitch status ('0', '1', ...)

Just make 2-dimensional integer array of 1000X1000 and fill it with 0's then follow the instructions using file handling. For example for turning on light change 0 to 1.

Related

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.

Creating a method of type class Matrix to add two matrices

so, I'm supposed to make a matrix using HashMap<Integer,ArrayList<Number>>, where Number is a class who's instance variables are numerator and denominator.
Class Matrix inherits Class Number, which have methods like fillMatrix(), printMatrix(), addMatrix(Matrix,Matrix) and subMatrix(Matrix,Matrix), problem is in those last two methods, i made them but I'm pretty sure they are completely wrong since i get a NullPointerxception, How do i make such methods?
here is the code.
public class Matrix extends Number implements Calculation
{
public static int rows;
public static int cols;
private ArrayList<Number> myArray;
private ArrayList<Double> myArray2;
private ArrayList<Double> myArray3;
private ArrayList<Double> myArray4;
private HashMap <Integer, ArrayList<Number>> hm;
private HashMap <Integer, ArrayList<Double>> hm2;
public Matrix(int r, int c)
{
hm = new HashMap<>();
hm2 = new HashMap<>();
rows = r;
cols = c;
}
public void fillMatrix()
{
Scanner input = new Scanner(System.in);
myArray = new ArrayList<>();
myArray2 = new ArrayList<>();
System.out.println("Enter the number of rows");
rows = input.nextInt();
System.out.println("Enter the number of columns");
cols = input.nextInt();
Number n = new Number();
for (int i = 0; i < cols;i++)
{
n.setNumerator(i);
n.setDenominator(i+1);
myArray.add(new Number(i,i+1));
double xn = n.getNumerator();
double xd = n.getDenominator();
myArray2.add(xn/xd);
}
for (int i = 0; i < rows; i++)
hm2.put(rows,myArray2);
}
public void printMatrix()
{
for (int i = 0; i < rows; i++)
{hm.put(rows,myArray);
System.out.println(myArray3.toString());
}
}
public Number getItem(int rowNO,int colNO)
{
rows = rowNO - 1;
cols = colNO - 1;
hm.get(rows);
return myArray.get(cols);
}
public void addMatrices(Matrix a, Matrix b)
{
Matrix x1 = new Matrix(rows,cols);
Matrix x2 = new Matrix(rows,cols);
for(int i = 0; i < rows; i++)
{ x1.hm2.get(rows);
x2.hm2.get(rows);
for(int j = 0; j< cols;j++)
{
double a1 = x1.myArray2.get(cols);
double a2 = x2.myArray2.get(cols);
double sum = a1+a2;
myArray3 = new ArrayList<>();
myArray3.add(sum);
}
x1=a;
x2=b;
}
}
public void subMatrices(Matrix a, Matrix b)
{
Matrix x1 = new Matrix(rows,cols);
Matrix x2 = new Matrix(rows,cols);
for(int i = 0; i < rows; i++)
{ x1.hm2.get(rows);
{ x2.hm2.get(rows);
for(int j = 0; j< cols;j++)
{
double a1 = x1.myArray2.get(cols);
double a2 = x2.myArray2.get(cols);
double sub = a1-a2;
myArray4 = new ArrayList<>();
myArray4.add(sub);
}
x1=a;
x2=b;
}
}
}
}
I'm a bit confused here, so I'll point out some things I noticed.
First off it's difficult to understand your code because of the variable names.
Also none of the methods are static, so i don't really understand why you're bringing in two Matrices for the calculation, then setting them equal to the first two you create in the beggining of both methods.
Next, you have two brackets after your for loop in the subtraction method, I'm guessing it was just a typo when pasting in StackOverflow.
The myArray objects you're adding the sum too is not going to be accumulating all the sums because you are creating a new arraylist each time that loop goes through. Create it outside the loop.
The NullPointerException could be anywhere really, it's hard to tell because the code is a bit confusing.
I would recommend using the LWJGL library which has has classes like Matrix4fand methods like Matrix4f.add();which will help you accomplish this.
Try running your program in debug mode so you can understand which statement gives you null pointer exception.
And I've noticed you have your rows and columns as static but you can change change them within constructor. It seems there is a logical mistake there. These two may be the cause of your exception if you didn't ever give initial value of them before.
If your matrixes can have different rows and columns then you definitely shouldn't use static for them.
To actually understand where nullpointerexception is given, you should also write your main code. But like I said, in debug mode, you can also see it yourself.

Array not returning strings

This is assessed work so please don't give the answer, just advice!
I'm trying to get my program to return the strings pass, compensation pass or fail depending on the values inputted by the user. However, it's not returning the values and I'm receiving an error for 'weighting'. Earlier it was working, however not in a suitable way because it wouldn't always return the correct before results. I added the array because i think that's what is needed, but now I'm just getting an error. Cheers.
enter code here
public class MarkCalculator {
static int[] marks = new int[12];
static int[] weighing = new int[6];
// public static Scanner keyboard = new Scanner(System.in);
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
int weighting;
int coursework;
int exammark;
for (int i = 0; i < marks.length / 2; i++) {
System.out.println("Please enter course work weighting");
weighting = kb.nextInt();
weighing[i] = weighting;
}
for (int i = 0; i < marks.length / 2; i++) {
System.out.println("Please enter course work mark ");
coursework = kb.nextInt();
marks[i] = coursework;
}
for (int i = 0; i < marks.length / 2; i++) {
System.out.println("Please enter exam mark ");
exammark = kb.nextInt();
marks[i + 6] = exammark;
}
System.out.println("Calculating Marks");
MarkCalculator mc = new MarkCalculator();
String[] results = mc.computeMarks(marks, weighing);
for (String result : results) {
System.out.println("Results are " + result);
}
}
public String[] computeMarks(int[] marks, int[] weighing) {
int[] formula = new int[12];
String[] results = new String[weighing.length];
for (int i = 0; i < weighing.length; i++) {
int exam = marks[i];
int cw = marks[i+weighing.length];
int weight = weighing[i];
formula [i]= ((cw + weight) + (exam * (100 - weight)) / 100);
if ((formula[i]<=39) && (formula[i] > 35)) {
results[i] = "COMPENSATION PASS";}
else if (formula[i] >= 40) {
results[i] = "PASS";
}
else {
results[i] = "FAIL";
}
}
return results;
}
public static void computeResult (int[] coursework, int[] exammark)
{
computeResult(coursework,exammark);
}
}
Was posted as comment:
You could separate the marks into two arrays which will be easier to debug? Also it seems like you might be going over the array index for weightings on this line
for (int i = 0; i < marks.length;i++)
{
int exam = marks[i];
int cw = marks[i];
int weight = weighing[i]; // Error is here
//...
}
Because "weighing" has a range 0-5 and you are cycling through to 0-11 (with the marks array)
weighting and marks are different length arrays, yet you are doing the loop
for (int i = 0; i < marks.length; i++)
which will go out of bounds for weighting when i > 5.
It looks like you need to do something like this:
for (int i = 0; i < weighting.length; i++) {
int cw = marks[i];
int exam = marks[i+weighting.length];
int weight = weighing[i];
But that will depend on how you are storing the marks for the cw and exam in the marks array. I would recommend creating separate arrays for cw and exam as these are different items and will make things a lot easier to read and debug for yourself.
As you've asked for tips to improve your program as well, without specific code, then I would consider doing the following:
1) Have separate arrays for exam and cw marks. You're making it hard for yourself to debug your program by concatenating them together and this is also the source of your error.
2) Assuming that you always have the same number of exams as you do cw elements then I would consider having a class variable in MarkCalculator that stores the number of tests. Something like this:
private static int NUM_TESTS.
This way you can initialise arrays like this:
private static int[] examMarks = new int[NUM_TESTS]
and you can do the looping in computeMarks like this:
for (int i = 0; i < NUM_TESTS; i++)
This way if you decide you want more tests you only have to update the code in one place. It would also be easy to change your program so that the user could define how many tests should be calculated.
3) Where you have:
weighting = kb.nextInt();
weightings[i] = weighting;
replace it with:
weightings[i] = kb.nextInt();
as the variable weighting only seem to be used in this place and is therefore unnecessary. This will result in fewer operations the program has to perform and reduces the amount of code on the screen. In practice the compiler will likely remove this redundant variable, but it is good practice to think about how many operations you are performing and which of them aren't necessary.
4) It's better practice to explicitly set access modifiers on fields in a class. So you should have:
'private static int[] weightings = new int[NUM_TESTS];`
If you want to access it from another class you would then typically specify a getter method like so:
public int[] getWeightings() { return weightings; }
5) This is less important, but I would move main to the bottom of the class. In Java it's more typical to see the classes fields first, then the constructor, then public methods, then private methods and have the main at the bottom. In large projects it helps keeping to good style as it makes the code easier to read and understand.
These reference might help you learn more:
Java Coding Style Guide
Oracle Tutorial on access-modifiers
You will encounter an error as "Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6"
This is because in method computeMarks. The length of marks (int[]) is 12.
and you just declare a variable with length 6 to handle:
int[] formula = new int[6];
when variable i in for loop reaches 6. The following code will throw out an error.
formula [i]= ((cw + weight) + (exam * (100 - weight)) / 100);
Have a try to declare it with length of 12.
int[] formula = new int[12];
Just paste code for method computeMarks.
public String[] computeMarks(int[] marks, int[] weighing) {
int[] formula = new int[12];
String[] results = new String[weighing.length];
StringBuilder sb = new StringBuilder();
for (int i = 0; i < weighing.length; i++) {
sb.setLength(0);
int exam = marks[i];
int cw = marks[i + weighing.length];
int weight = weighing[i];
formula[i] = ((cw + weight) + (exam * (100 - weight)) / 100);
if ((formula[i] <= 39) && (formula[i] > 35)) {
sb.append("COMPENSATION PASS");
} else if (formula[i] >= 40) {
sb.append("PASS");
} else {
sb.append("FAIL");
}
sb.append(" cw mark is ").append(cw).append(" and exam mark is ")
.append(exam);
results[i] = sb.toString();
}
return results;
}

How to switch int sort to String sort?

I have written some code for sorting random integers that a user inputted. How would I switch this into sorting randomly inputted letters? Aka, user inputs j, s, g, w, and the programs outputs g, j, s, w?
for (int i = 0; i < random.length; i++) { //"random" is array with stored integers
// Assume first value is x
x = i;
for (int j = i + 1; j < random.length; j++) {
//find smallest value in array (random)
if (random[j] < random[x]) {
x = j;
}
}
if (x != i) {
//swap the values if not in correct order
final int temp = random[i];
random[i] = random[x];
random[x] = temp;
}
itsATextArea.append(random[i] + "\n");// Output ascending order
}
Originally I hoped (though I knew the chances of me being right were against me) that replacing all the 'int' with 'String' would work...naturally I was wrong and realized perhaps I had to list out what letter came before which by using lists such as list.add("a"); etc.
I apologize if this seems like I am asking you guys to do all the work (which I'm not) but I'm not entirely sure how to start going about this, so if anyone can give some hints or tips, that would be most appreciated!
You could use String.compareTo() to do that:
Change this:
int[] random = new int[sizeyouhad];
...
if (random[j] < random[x]) {
...
final int temp = random[i];
to:
String[] random = new String[sizeyouhad];
...
if (random[j].compareTo(random[x]) < 0) {
...
final String temp = random[i];
Trial with your code:
String[] random = new String[3];
random[0] = "b";
random[1] = "c";
random[2] = "a";
int x = 0;
//"random" is array with stored integers
for (int i = 0; i < random.length; i++) {
// Assume first value is x
x = i;
for (int j = i + 1; j < random.length; j++) {
//find smallest value in array (random)
if (random[j].compareTo(random[x]) < 0) {
x = j;
}
}
if (x != i) {
//swap the values if not in correct order
final String temp = random[i];
random[i] = random[x];
random[x] = temp;
}
System.out.println(random[i] + "\n");// Output ascending order
}
If you're just trying to sort a list of strings you should probably use the java.util.Collections.sort method rather than writing your own sorting routine.
Was random originally int[]? If you had changed this to String[], you can use String#compareTo method to discern if one string is "less than" another.
Incidentally, you can change the type of random to Comparable[] and then you can use the same algorithm to sort any object whose class implements the interface!
Try to use Collections.sort() function
List<String> l = Arrays.asList("j","s", "g","w");
Collections.sort(l);
If you consider every character to be a code point[1] and you want to sort by Unicode code point order[2], then there is really no need to change your logic. The work is converting from whatever input you are given (String, char[], etc.) into an int[] of the code points.
[1] - http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#codePointAt(int)
[2] - http://en.wikipedia.org/wiki/Code_point
You can make your code work on any type of Object by using generics.
The following code is very simple and works perfectly (With this library you can solve your problem in few lines):
import static ch.lambdaj.Lambda.sort;
import static ch.lambdaj.Lambda.on;
import java.util.Arrays;
import java.util.List;
public class Test{
public static void main(String[] args) {
List<String> list = Arrays.asList("1","102","-50","54","ABS");
List<String> newList = sort(list, on(String.class));
System.out.println(newList);//[-50, 1, 102, 54, ABS]
}
}
This code uses lambda library (download here, website). Find in the website this example:
List<Person> sorted = sort(persons, on(Person.class).getAge());

Creating Multiple Rectangle Objects at Once (Java)

First off, thanks for just clicking this :) I'm an amateur student coder, and I'm creating a (horrible) version of Pacman. I'm trying to create rectangles for each of my dots on my 1000x650 applet screen, so I can create an if statement when Packages hit box touches them, they disappear.
My problem is, I want to create a class so I can create the rectangles easily and have only 1 if statement, and not one for each dot rectangle (trying to learn to be efficient :P)
If I didn't elaborate enough, I'll be wary to bring edits based on your responses, and thanks!!!
(Edit 1: Fixed a run on sentence xD)
If you need to fill rectangles on both X and Y (Matrix), you most probably need nested loops.
Let's consider you want to a 5 * 5 rectangle every 100 pixels in the width with a 50 pixels spacing in the height:
for(int x = 0;x<1000;x+= 100)
{
for(int y=0;y<650; y+= 50)
{
drawRectangle(x, y, 5, 5); // Considering drawRectangle(x, y, width, height)
}
}
Try this code, it will return true when it collides with the dot.
Object pacman = new Object();
pacman.xcoord = null;
pacman.ycoord = null;
final int dotsInStage = 50;
// add other properties
int xcoords[] = new int[dotsInStage];
int ycoords[] = new int[dotsInStage];
Call this Boolean:
public boolean dotCollison (int xcoords[], int ycoords[], Object pacman) {
loop = 0;
while (loop <= dotsInStage) {
if (pacman.xcoord = xcoords[loop] && pacman.ycoord = ycoords[loop]) {
return true;
break;
}
}
}
To add the rectangles, try:
int loop = 0;
while (loop <= dotsInStage) {
Graphics.drawRectangle (xcoord[loop] , ycoord[loop] , xcoord[loop] + 10 , ycoord[loop] + 10);
}
Hope it works and happy coding!
I'm not totally comprehending your question. But
Couldn't you just create a 2 d array use a nested for loop
For( int I = 0; I<array.length;i++){
For (int j = 0; j <array[0].length;I++){
//fill w/ rectangles
array[I][j]= ;
}}
You can use a for statement which repeadly loops untill a condition is met.
This is the general structure:
for (counterInitialization; terminatingCondition; incrementLoopsCount) {
statement(s);
}
And this is an example in which the loop continue for as long as the counter i<n):
int n = 50;
for(int i = 0;i<n;i++){
//code for creating a rectangle
...
}
I suggest you give this a read.

Categories