I'm trying to optimize my program by getting rid of duplicate searches or just by generally make things optimized throughout my program and I came across a method in which I can't find any "better" solution what so ever and would love to know if anyone could point me in a direction for refineing it.
First off here is my code that I'm trying to optimize:
public Player spaceBattle(Player player1, Player player2) {
ArrayList<Ship> listOfShipsPlayer1 = this.getShipsOfPlayer(player1);
ArrayList<Ship> listOfShipsPlayer2 = this.getShipsOfPlayer(player2);
Random random = new Random();
int player1hits = 0, player2hits = 0, rolledDie;
for (Ship aShip : listOfShipsPlayer1) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player1hits += 1;
}
}
for (Ship aShip : listOfShipsPlayer2) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player2hits += 1;
}
}
for (int i = 0; i < player1hits; ++i) {
if (this.getShipsOfPlayer(player2).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer2.get(i));
} else {
break;
}
}
for (int i = 0; i < player2hits; ++i) {
if (this.getShipsOfPlayer(player1).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer1.get(i));
} else {
break;
}
}
As you can see here I run the same algorithm twice in order to check for first Player1 and then Player2 and add update their respective hits. And then for the amount of hits for each player I then remove a ship.
What I would like to know if its possible to change this bit of code to be able to not have the duplicate code for each player but that it automatically can go through player1 first and then player2 in one loop.
Looking forward to hear from you
You can just create methods.
private int hitShips(List<Ship> ships) {
int result = 0;
for (Ship ship : ships) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= ship.getShipType().getCombatValue()) {
result++;
}
}
return result;
}
which makes your code
int player1hits = hitShips(listOfShipsPlayer1);
int player2hits = hitShips(listOfShipsPlayer2);
and similar for the removal of the ships from the list.
void removeShips(List<Ship> ships, int remove) {
int removeCount = Math.max(ships.size(), remove);
Iterator<Ship> it = ships.iterator();
for (int i = 0; i < removeCount; i++) {
it.remove();
}
}
with
removeShips(getShipsOfPlayer(player2), player1hits);
removeShips(getShipsOfPlayer(player1), player2hits);
I'm not sure why you always remove ships from the head of the lists, since combat values seems to be a thing, but that's just a side note.
I wouldn't call this "optimization" so much, but the removal of duplicate code is always good.
You could optimize removeShips as:
void removeShips(List<Ship> ships, int numHits) {
int numToRemove = Math.min(ships.size(), numHits);
if (numToRemove > 0) {
ships.subList(0, numToRemove).clear();
}
}
This method call will result in only one System.arraycopy call, which will shift all remaining elements to the left.
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#subList-int-int-
Hi I am having some problems with using random numbers inside of loops.
private void SetMines()
{
Random randRowGen = new Random();
Random randColGen = new Random();
int mineCount = 0;
int numMines = (ROWS * COLUMNS)* (int)0.156;
while(mineCount <= numMines)
{
int randRow = randRowGen.nextInt(ROWS)+1;
int randCol = randColGen.nextInt(COLUMNS)+1;
grid[randRow][randCol] = new Character('*');
mineCount++;
}
}
Here is my method it is going through an array size 25 * 25 and picking random spots and putting "mines" there. The only problem is it only selects one location to put a "mine" in and it needs to put 97 mines in random spots.
Any help will be appreciated thanks!!
Your numMines calculation will always return 0, because when you cast a double that is less than 1 to an int, it will be set to 0, which means that the statement in your while loop will only be run a single time, hence only a single mine being placed.
The problem isn't Random, it's int numMines = (ROWS * COLUMNS)* (int)0.156;. Have you checked what that value is? It's 0, because (int) 0.156 equals 0.
Perhaps you want int numMines = (int) ((double) 0.156 * ROWS * COLUMNS);. The problem with integer maths is that you can lose a LOT of precision.
Make your computer suffer and make it drop all those mines.
Remember. The definition of "computation" is "to force a machine do a boring job that nobody else would like to do" :-)
public static void main(String[] args) {
int rows = 25;
int cols = 25;
boolean[][] mines = new boolean[rows][cols];
while(mineCount(mines) < 97){
dropMine(mines);
}
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
System.out.print("[");
if (mines[i][j]){
System.out.print("*");
}else{
System.out.print(" ");
}
System.out.print("] ");
}
System.out.println();
}
}
private static void dropMine(boolean[][] mines) {
int x = (int)(Math.random()*25);
int y = (int)(Math.random()*25);
mines[x][y] = true;
}
private static int mineCount(boolean[][] mines) {
int count = 0;
for(int i=0;i<25;i++){
for(int j=0;j<25;j++){
if (mines[i][j]){
count++;
}
}
}
return count;
}
I am creating a concentration game.
I have an buffered image array where I load in a 25 image sprite sheet.
public static BufferedImage[] card = new BufferedImage[25];
0 index being the card back. and 1 - 24 being the values for the face of the cards to check against if the cards match.
What I am tying to do is this I will have 4 difficulties Easy, Normal, Hard, and Extreme. Each difficulty will have a certain amount of cards it will need to draw and then double the ones it chosen. for example the default level will be NORMAL which is 12 matches so it need to randomly choose 12 unique cards from the Buffered Image array and then double each value so it will only have 2 of each cards and then shuffle the results.
This is what I got so far but it always seems to have duplicates about 99% of the time.
//generate cards
Random r = new Random();
int j = 0;
int[] rowOne = new int[12];
int[] rowTwo = new int[12];
boolean[] rowOneBool = new boolean[12];
for(int i = 0; i < rowOneBool.length; i++)
rowOneBool[i] = false;
for(int i = 0; i < rowOne.length; i++){
int typeId = r.nextInt(12)+1;
while(rowOneBool[typeId]){
typeId = r.nextInt(12)+1;
if(rowOneBool[typeId] == false);
}
rowOne[i] = typeId;
j=0;
}
the 3 amounts I will be needing to generate is Easy 6, Normal 12, and Hard 18 extreme will use all of the images except index 0 which is the back of the cards.
This is more or less in the nature of random numbers. Sometimes they are duplicates. You can easily factor that in though if you want them to be more unique. Just discard the number and generate again if it's not unique.
Here's a simple method to generate unique random numbers with a specified allowance of duplicates:
public static void main(String[] args) {
int[] randoms = uniqueRandoms(new int[16], 1, 25, 3);
for (int r : randoms) System.out.println(r);
}
public static int[] uniqueRandoms(int[] randoms, int lo, int hi, int allowance) {
// should do some error checking up here
int range = hi - lo, duplicates = 0;
Random gen = new Random();
for (int i = 0, k; i < randoms.length; i++) {
randoms[i] = gen.nextInt(range) + lo;
for (k = 0; k < i; k++) {
if (randoms[i] == randoms[k]) {
if (duplicates < allowance) {
duplicates++;
} else {
i--;
}
break;
}
}
}
return randoms;
}
Edit: Tested and corrected. Now it works. : )
From what I understand from your question, the answer should look something like this:
Have 2 classes, one called Randp and the other called Main. Run Main, and edit the code to suit your needs.
package randp;
public class Main {
public static void main(String[] args) {
Randp randp = new Randp(10);
for (int i = 0; i < 10; i++) {
System.out.print(randp.nextInt());
}
}
}
package randp;
public class Randp {
private int numsLeft;
private int MAX_VALUE;
int[] chooser;
public Randp(int startCounter) {
MAX_VALUE = startCounter; //set the amount we go up to
numsLeft = startCounter;
chooser = new int[MAX_VALUE];
for (int i = 1; i <= chooser.length; i++) {
chooser[i-1] = i; //fill the array up
}
}
public int nextInt() {
if(numsLeft == 0){
return 0; //nothing left in the array
}
int a = chooser[(int)(Math.random() * MAX_VALUE)]; //picking a random index
if(a == 0) {
return this.nextInt(); //we hit an index that's been used already, pick another one!
}
chooser[a-1] = 0; //don't want to use it again
numsLeft--; //keep track of the numbers
return a;
}
}
This is how I would handle it. You would move your BufferedImage objects to a List, although I would consider creating an object for the 'cards' you're using...
int removalAmount = 3; //Remove 3 cards at random... Use a switch to change this based upon difficulty or whatever...
List<BufferedImage> list = new ArrayList<BufferedImage>();
list.addAll(Arrays.asList(card)); // Add the cards to the list, from your array.
Collections.shuffle(list);
for (int i = 0; i < removalAmount; i++) {
list.remove(list.size() - 1);
}
list.addAll(list);
Collections.shuffle(list);
for (BufferedImage specificCard : list) {
//Do something
}
Ok, I said I'd give you something better, and I will. First, let's improve Jeeter's solution.
It has a bug. Because it relies on 0 to be the "used" indicator, it won't actually produce index 0 until the end, which is not random.
It fills an array with indices, then uses 0 as effectively a boolean value, which is redundant. If a value at an index is not 0 we already know what it is, it's the same as the index we used to get to it. It just hides the true nature of algorithm and makes it unnecessarily complex.
It uses recursion when it doesn't need to. Sure, you can argue that this improves code clarity, but then you risk running into a StackOverflowException for too many recursive calls.
Thus, I present an improved version of the algorithm:
class Randp {
private int MAX_VALUE;
private int numsLeft;
private boolean[] used;
public Randp(int startCounter) {
MAX_VALUE = startCounter;
numsLeft = startCounter;
// All false by default.
used = new boolean[MAX_VALUE];
}
public int nextInt() {
if (numsLeft <= 0)
return 0;
numsLeft--;
int index;
do
{
index = (int)(Math.random() * MAX_VALUE);
} while (used[index]);
return index;
}
}
I believe this is much easier to understand, but now it becomes clear the algorithm is not great. It might take a long time to find an unused index, especially when we wanted a lot of values and there's only a few left. We need to fundamentally change the way we approach this. It'd be better to generate the values randomly from the beginning:
class Randp {
private ArrayList<Integer> chooser = new ArrayList<Integer>();
private int count = 0;
public Randp(int startCounter) {
for (int i = 0; i < startCounter; i++)
chooser.add(i);
Collections.shuffle(chooser);
}
public int nextInt() {
if (count >= chooser.size())
return 0;
return chooser.get(count++);
}
}
This is the most efficient and extremely simple since we made use of existing classes and methods.
I was given this question to test my programming ability. Though I feel it is as much math as it is programming. I have failed honestly, but I would like to know how it is done for future reference.
The ideal answer will use recursion and threads.
Your niece was given a set of blocks for her birthday, and she has decided to build a panel using 3”×1” and 4.5”×1" blocks. For structural integrity, the spaces between the blocks must not line up in adjacent rows. For example, the 13.5”×3” panel below is unacceptable, because some of the spaces between the blocks in the first two rows line up (as indicated by the dotted line).
There are 2 ways in which to build a 7.5”×1” panel, 2 ways to build a 7.5”×2” panel, 4 ways to build a 12”×3” panel, and 7958 ways to build a 27”×5” panel. How many different ways are there for your niece to build a 48”×10” panel? The answer will fit in a 64-bit integer. Write a program to calculate the answer.
That is the issue, I am not sure where to begin in terms of math. I understand I need to compute all possible combinations for the first row. But I am not really sure how. Then you could at this point on a particular thread calculate all possible combos for the next row and so on. And then each first row combination could get its own thread and pass its setup into a recursive algorithm that compares the current row to the last one, and finds a possible answer. But I can't really program it because I don't know how to calculate the possible combinations for any row. If I did, then maybe I could check to see if its a legal row (no two blocks exactly on top of each other (staggered)) and move on to the next one. Possibly each row would be a for loop for nested around the next one in terms of Code. But again I don't know what to do for the mathematical aspect of it.
I think you have a good initial grasp of the problem. If I understand your thoughts correctly, you ought to be recursing on each block placement, not on each row placement - as blocks that are oriented vertically will quickly preclude any ordinary rows.
Here's the approach I would take: you will end up building a tree (either explicitly in memory or implicit: the tree can be an argument that is passed in your recursive function). The node of the tree will be a state of the tree - so the root is "no blocks placed". You place a first block "somehow" (we'll get to that) and that represents a new state.
The goal will be to construct a set of leaf nodes which are all complete (the board is filled in) and legal (no cracks line up). So how do we get there?
For each block placement, there are 4 "alternate realities": a 3x1 block placed horizontally, a 3x1 placed vertically (call this 1x3), a 4.5x1 placed horizontally, and a 1x4.5. For each of these options, you will attempt to "legally" place the block at the next point in the row. If it is legal (legal being someting along the lines of "block doesn't overlap the board edges, block doesn't share a vertical edge") then you can accept that board as an intermediate state and recurse with that new state. If it is not legal, then that state must be abandoned.
Thinking this way, the first 4 children nodes will be a block in the bottom-left corner as [3x1, 1x3, 4.5x1, 1x4.5]. Each of those four states will have a block "just to the right" in one of 4 configurations, and so on.
In order to move across the rows, when you hit the right edge, I would find the "lowest" empty spaces and fill them arbitrarily when they tie from left to right. This is eager enough to prune large sets when the edges are ragged, but still works well when the levels are flat, like when you're first starting out.
In essence, your tree will have (up to) 4 nodes for each intermediate state, with the edges representing an "attempted placement". If you cannot legally place the block at that "attempted placement", you do not recurse, pruning that possibility and all descendents from the tree.
This brute-force method should get you a completed board, even if its computational complexity is astronomical. It is only at the point that you can correctly solve some problems that you should consider parallelizing with threads. Recursive problems tend to lend themselves well to threads, as each recursion can often be parallelized without too much pain. Just make sure you get it right first.
This post is almost 5 years old but maybe my answer will be usefull for someone. There are two solutions below. First is without multithreading. Second one uses four threads to solve the problem. It calculates 48x4 panel (48x10 lasts very long) but it can be easilly modified by changing initial values:
nbOfRows, nbOfCols, wallWidth and wall
Solution without multithreading:
import java.io.*;
import java.util.*;
class WallFlexOld
{
static final float blocks[] = {3.0f, 4.5f};
static final int nbOfRows = 4;
static final int nbOfCols = 16;
static final float wallWidth = 48.0f;
static final float wall[][] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
static long nbOfCombinations = 0;
public static void main(String args[])
{
long startTime = System.currentTimeMillis();
addBlock(0, 0);
long workingTime = System.currentTimeMillis() - startTime;
System.out.println("Working time: " + workingTime + "ms");
System.out.println("noc: " + nbOfCombinations);
}
static void addBlock(int row, int col)
{
for(float b: blocks)
{
wall[row][col] = b;
if(blockFit(row, col))
{
if(rowWidth(row) <= wallWidth)
{
if(rowWidth(row) == wallWidth)
{
if(row == (nbOfRows - 1))
nbOfCombinations++;
else
addBlock(row + 1, 0);
}
else //rowWidth < wallWidth
addBlock(row, col + 1);
}
}
wall[row][col] = 0;
}
}
static float rowWidth(int row)
{
float width = 0;
for(float b: wall[row])
width = width + b;
return width;
}
static boolean blockFit(int row, int col)
{
if(row == 0)
return true;
boolean fit = true;
float currentLenght = 0;
for(int i = 0; i < col; i++)
currentLenght = currentLenght + wall[row][i];
float lowerRowCurLenght = 0;
for(float b: wall[row - 1])
{
lowerRowCurLenght = lowerRowCurLenght + b;
if((currentLenght == lowerRowCurLenght) & (currentLenght != wallWidth))
fit = false;
}
return fit;
}
}
Solution with multithreading:
import java.io.*;
import java.util.*;
class Wall implements Runnable
{
private float blocks[];
private int nbOfRows;
private int nbOfCols;
private float wallWidth;
private float wall[][];
private long nbOfCombinations = 0;
private int row, col;
public long getNbOfCombinations() { return this.nbOfCombinations; }
Wall(float blocks[], int nbOfRows, int nbOfCols, float wallWidth, float wall[][], int row, int col)
{
this.blocks = blocks;
this.nbOfRows = nbOfRows;
this.nbOfCols = nbOfCols;
this.wallWidth = wallWidth;
this.wall = wall;
this.row = row;
this.col = col;
}
private boolean blockFit(int row, int col)
{
if(row == 0)
return true;
boolean fit = true;
float currentLenght = 0;
for(int i = 0; i < col; i++)
currentLenght = currentLenght + wall[row][i];
float lowerRowCurLenght = 0;
for(float b: wall[row - 1])
{
lowerRowCurLenght = lowerRowCurLenght + b;
if((currentLenght == lowerRowCurLenght) & (currentLenght != wallWidth))
fit = false;
}
return fit;
}
private float rowWidth(int row)
{
float width = 0;
for(float b: wall[row])
width = width + b;
return width;
}
private void addBlock(int row, int col)
{
for(float b: blocks)
{
wall[row][col] = b;
if(blockFit(row, col))
{
if(rowWidth(row) <= wallWidth)
{
if(rowWidth(row) == wallWidth)
{
if(row == (nbOfRows - 1))
nbOfCombinations++;
else
addBlock(row + 1, 0);
}
else //rowWidth < wallWidth
{
addBlock(row, col + 1);
}
}
}
wall[row][col] = 0;
}
}
#Override
public void run()
{
addBlock(row, col);
}
}
class WallMT
{
static final float blocks[] = {3.0f, 4.5f};
static final int nbOfRows = 4;
static final int nbOfCols = 16;
static final float wallWidth = 48.0f;
static final float wall[][] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
public static void main(String args[])
{
wall[0][0] = blocks[0];
wall[0][1] = blocks[0];
Wall myWall1 = new Wall(blocks, nbOfRows, nbOfCols, wallWidth, getWallCopy(wall), 0, 2);
Thread t1 = new Thread(myWall1);
wall[0][0] = blocks[0];
wall[0][1] = blocks[1];
Wall myWall2 = new Wall(blocks, nbOfRows, nbOfCols, wallWidth, getWallCopy(wall), 0, 2);
Thread t2 = new Thread(myWall2);
wall[0][0] = blocks[1];
wall[0][1] = blocks[0];
Wall myWall3 = new Wall(blocks, nbOfRows, nbOfCols, wallWidth, getWallCopy(wall), 0, 2);
Thread t3 = new Thread(myWall3);
wall[0][0] = blocks[1];
wall[0][1] = blocks[1];
Wall myWall4 = new Wall(blocks, nbOfRows, nbOfCols, wallWidth, getWallCopy(wall), 0, 2);
Thread t4 = new Thread(myWall4);
long startTime = System.currentTimeMillis();
t1.start();
t2.start();
t3.start();
t4.start();
try
{
t1.join();
t2.join();
t3.join();
t4.join();
}
catch(InterruptedException ie)
{
System.out.println("Thread " + t1 + " interrupted.");
}
long workingTime = System.currentTimeMillis() - startTime;
System.out.println("Working time: " + workingTime + "ms");
System.out.println("noc: " + (myWall1.getNbOfCombinations() + myWall2.getNbOfCombinations() + myWall3.getNbOfCombinations() + myWall4.getNbOfCombinations()));
}
static private float[][] getWallCopy(float wall[][])
{
float tmpWall[][] = new float[nbOfRows][nbOfCols];
for(int i = 0; i < nbOfRows; i++)
for(int j = 0; j < nbOfCols; j++)
tmpWall[i][j] = wall[i][j];
return tmpWall;
}
}