Generate 10 random pairs conditionally - java

I have the following for loop which is supposed to generate two random integers and call grid.place(x,y). I'm trying to figure out how to generate a different pair if grid.place(x,y) returns false. The place method returns true if the provided x and y where not previously provided. Thank you.
for (int i = 0; i < 10; i++) {
mx = randomGenerator.nextInt(10);
my = randomGenerator.nextInt(10);
grid.place(mx,my);
}

This is the perfect situation for a do-while loop:
for (int i = 0; i < 10; i++) {
do{
mx = randomGenerator.nextInt(10);
my = randomGenerator.nextInt(10);
}while(!grid.place(mx,my));
}
Another solution would be to make use of the Collections.shuffle() method. like so:
List<Integer> xPos = IntStream.rangeClosed(0,10).boxed().collect(Collectors.toList());
List<Integer> yPos = IntStream.rangeClosed(0,10).boxed().collect(Collectors.toList());
Collections.shuffle(xPos);
Collections.shuffle(yPos);
for(int i=0;i<10;i++)
grid.place(xPos.get(i), yPos.get(i));
This will give you all coords and never repeat in better complexity than the do-while loop

To avoid duplicate, you can use a List for example :
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);//fill your list
}
Random randomGenerator = new Random();
int mx = randomGenerator.nextInt(10);//generate the first value
int x = mx;//to not lost this value we store it in another variable
list.remove(mx);//remove the first value from your list, to avoid duplicate
int my = randomGenerator.nextInt(9);//generate the second value
int y = my;
grid.place(x, y);//now, you are sure that you have two different values

Here is a more OO like approach:
Most likely the "grid" is not infinite. This means you can create a list of all possible positions in the grid:
class Position{
private final int x;
private final int y;
public Position(int x, int y){
this.x=x;
this.y=y;
}
public getX(){return x;}
public getY(){return y;}
}
List<Position> availablePositions = new ArrayList<>();
for(int i =0; i<MAX_X;i++)
for(int j =0; j<MAX_Y;j++)
availablePositions.add(new Position(i,j));
Then you can retrieve a random index from that list:
for (int i =0;i<10;i++){
Position randomPosition =
availablePositions.remove(
new Random().nextInt(availablePositions.size()));
grid.place(
randomPosition.getX(),
randomPosition.getY());
}
This way you will never get a duplicate, but the positions taken are lost for the programs current execution.
alternatively you can just shuffle the list and take the first 10 elements:
Collections.shuffle(availablePositions);
for (int i =0;i<10;i++)
grid.place(
availablePositions.get(i).getX(),
availablePositions.get(i).getY());
This also ensures distinct positions within the loop but you keep all positions in the list for later use...
My biggest problem with this solution is that it has deterministic time at the cost of O(MAX_X * MAX_Y) memory. Other solutions have O(1) time (for small values of i), though not deterministic, and O(1) memory. – gobernador
On modern computers memory is not the limiting resource (not even on cell phones...). When programming in Java, you should not optimize for memory and/or Performance unless you have proven you have a problem. And if you really have a memory problem then Java is most likely the wrong tool for the task.
But:
When we accept some more OO in the solution then we would not need separate
Position objects waisting memory. We could rather have Cell objects with a state we can change instead of immutable String objects in the grid. Working with a custom class as elements in the grid has some more advantages aside from enabling this selection mechanism, so we should have a look at it.
Cell.java
class Cell{
private String content = " "; // empty
public void placeHere(String newContent){
content = newContent;
}
}
Grid.java
// ...
Cell[][] gameField = new Cell[MAX_X][MAX_Y];
for(int i =0; i
Then we can collect this existing cells in a separate List:
List allCells = new ArrayList<>();
for(Cell[] row : gameField)
allCells.addAll(Arrays.asList(row));
And select random cells to change the state:
Collections.shuffle(allCells);
for(int i =0; i<10; i++)
allCells.get(i).placeHere("*");
Actually we don't even need to know the changed cells coordinates. And that is what OOP is about: solve your task with as less technical knowledge in your code as possible.
And wait, there is more:
How do you reset the game with the pure String array? Right, you iterate over the complete array. but the best performance optimisation hint I ever got is:
the fastest way to do something is not to do it.
So how can we avoid the iteration over the complete Array?
With my approach we can collect the modified cells in a separate list:
List<Cell> modifiedCells = new ArrList<>();
Collections.shuffle(allCells);
for(int i =0; i<10; i++){
Cell currentCell = allCells.get(i);
currentCell.placeHere("*");
modifiedCells.add(currentCell);
}
Then you have to reset 10 cell objects in that extra list instead of MAX_X*MAX_Y strings in an array.

You could do
for(int i = 0; i < 10; i++){
while(true){
mx = randomGenerator.nextInt(10);
my = randomGenerator.nextInt(10);
if(grid.place(mx,my)) break;
}
}
This while loops over, until grid#place returns true, and keeps generating random values.

The longest and most brute-force way to approach this is to check if the coordinates already have been accessed in the grid.
You would have to start with ten and work your way down to zero.
int size = 10;
for (int count = size; count > 0;) {
mx = randomGenerator.nextInt(size);
my = randomGenerator.nextInt(size);
if (!grid.exists(mx, my)) {
grid.place(mx, my);
count--;
}
}
Example
public class Grid {
private boolean[][] self;
public Grid(int size) {
this.self = new boolean[size][size];
}
public int getSize() {
return this.self.length;
}
public boolean exists(int row, int col) {
return this.self[row][col];
}
public void place(int row, int col) {
this.self[row][col] = true;
}
#Override
public String toString() {
StringBuffer buff = new StringBuffer();
for (int row = 0; row < this.self.length; row++) {
if (row > 0) {
buff.append(System.lineSeparator());
}
for (int col = 0; col < this.self[0].length; col++) {
if (col > 0) {
buff.append('\t');
}
buff.append(this.self[row][col] ? '1' : '0');
}
}
return buff.toString();
}
}
public class GridDriver {
public static void main(String[] args) {
int size = 10;
Grid grid = new Grid(size);
for (int count = size; count > 0;) {
int mx = (int) (Math.random() * size);
int my = (int) (Math.random() * size);
if (!grid.exists(mx, my)) {
grid.place(mx, my);
count--;
}
}
System.out.println(grid);
}
}
Output
0 0 0 1 0 0 0 0 0 0
1 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 1 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 1 1 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 1
The best way to solve this would be to create some type of memory map and generate permutations and randomly iterate over those values.

If you need to keep trying generate numbers until its gonna be placed, and fill grid 10x10, basically all you want to do is to check for already placed numbers.
Set<Integer> placedNumbers = new HashSet<>();
while(set.size() != 100) {
mx = randomGenerator.nextInt(10);
my = randomGenerator.nextInt(10);
if(!placedNumbers.contains(mx+my) && grid.place(mx,my)) {
placedNumbers.add(mx+my);
}
}

You just need to check that whichever pair you are passing that is not passed previously.
Using HashSet you can achieve it:
HashSet<String> set = new HashSet<>();
int i = 0;
while(i<10) {
mx = randomGenerator.nextInt(10);
my = randomGenerator.nextInt(10);
String rand = mx + "" + my;
if(!set.add(rand)){
grid.place(mx,my);
i++;
}
}
So set will ensure that whatever pair you are passing, that is not passed previously.

Related

Why is the DP solution not working, Trapping Rain Water?

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
example:
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.
**Constraints**:
n == height.length
0 <= n <= 3 * 104
0 <= height[i] <= 105
The issue with this is that it is outputting 0 for when the input is [2,0,2] but the code should be setting the index 1 to have a leftMax of 2 and rightMax of 2 so 2-0=2 should be the output
class Solution {
public int trap(int[] height) {
if(height == null || height.length == 0){
return 0;
}
int ans = 0;
int size = height.length;
int[] leftMax = new int[size];
int[] rightMax = new int[size];
leftMax[0] = height[0];
for(int i = 1; i < size; i++){
leftMax[i] = Math.max(leftMax[i-1],height[i]);
}
rightMax[0] = height[size-1];
for(int i = size-2; i >= 0; i--){
rightMax[i] = Math.max(rightMax[i+1],height[i]);
}
for(int i = 1; i < size-1; i++){
ans+= Math.min(leftMax[i],rightMax[i])-height[i];
}
return ans;
}
}
The problem is that your rightMax initialization is wrong, it initializes "on the wrong side". It initializes the [0] which was probably copied from the leftMax section. But the leftMax then iterates from the left, the rightMax iterates from the right and therefore the rightmost index should be initialized. Note that you already initialized with the correct height index but for the wrong rightMax - it should look like:
rightMax[size-1] = height[size-1]
This previously worked because the very right was (probably) not part of a water trap and therefore its wrong value did not have any impact. But in the very simply case of 2,0,2 it is part of the trap and messed up the result.
Now the code properly calculates the two given samples:
System.out.println(trap(new int[] {0,1,0,2,1,0,1,3,2,1,2,1})); // 6
System.out.println(trap(new int[] {2,0,2})); // 2

Get largest and smallest group of ones in binary array

I have a 2D array of 0's and 1's.
Every consecutive cells having 1's is considered as single object.
My task is to find the largest consecutive 1's for every row and also smallest consecutive 1's and get the difference of those.
Example:
oth Row --> 0 0 0 0 0
1st Row --> 0 0 1 0 0
2nd Row --> 0 0 1 0 1
3rd Row --> 1 1 1 0 1
4th Row --> 0 0 0 0 0
Explanation:
In 0th Row there are no 1's so no operation needed.
In 1st Row there is a single 1, so I am considering it as largest object of this row is 1.
In 2nd Row there is a 1 at position [2,2], I am considering it as largest object of this row is 1. Now the next 1 at position [2, 4], I am considering it as smallest object of this row is 1.
In 3rd Row there are three 1's at position [3,0],[3,1],[3,2], I am considering it as largest object of this row is 3. Now the next 1 at position [3, 4], I am considering it as smallest object of this row is 1.
In 4th row there are no 1's so no operation needed.
My task is to find the difference between the sum all largest objects and sum of all smallest objects.
In this example:
Sum of largest objects = 0 (0th row) + 1 (1st row) + 1 (2nd row) + 3 (3rd row) + 0 (4th row) = 5
Sum of smallest objects = 0 (0th row) + 0 (1st row) + 1 (2nd row) + 1 (3rd row) + 0 (4th row) = 2
Result = sum of largest - sum of smallest = 5 - 2 = 3.
I have comeup with below logic:
public static int process(int[][] array)
{
int result = 0;
// Get row and column size
int rows = array.length;
int cols = array[0].length;
// variables to store sum of large and small objects
int large = 0;
int small = 0;
for(int i=0; i<rows; i++) {
// variables to store large and small objects per row level
int l_count = 0;
int s_count = 0;
boolean valid = false;
for(int j=0; j<cols-1; j++) {
int curr = array[i][j];
int next = array[i][j+1];
// First occurrence of 1 is considered as large object
if(l_count == 0 && curr == 1) {
l_count++;
valid = true;
}
// consecutive 1's added to large count
if(valid && curr == next) {
l_count++;
} else {
valid = false;
}
// Get small count
if(!valid && curr == 1 || next == 1) {
s_count++;
}
}
// Add row level counts to large and small objects
large += l_count;
small += s_count;
}
result = large - small;
return result;
}
This is not working and giving wrong results, I am not able to find the needed logic to solve this problem.
To find the largest and smallest objects in each row you might first want to get a list of all objects in the row, then filter accordingly, that is have a function that does
0 0 0 0 0 -> []
0 0 1 0 0 -> [1]
0 0 1 0 1 -> [1,1]
1 1 1 0 1 -> [3,1]
0 0 0 0 0 -> []
After that you can use a function such as Collections.max(collection) and Collections.min(Collection) to get the maximum and minimum values. To account for the case in row 1 you could then check if(foundObjects.size < 2) and set min to 0 in that case.
A possible implementation for this, assuming you are using Java 1.8 or newer would be the following:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
class ObjectCalculator {
static int compute(int[][] array){
return Arrays.stream(array)
.map(ObjectCalculator::findObjects)
.mapToInt(objects -> {
int min, max;
if (objects.size() < 2) min = 0;
else min = Collections.min(objects);
if (objects.size() < 1) max = 0;
else max = Collections.max(objects);
return max - min;
}).sum();
}
static List<Integer> findObjects(int[] array) {
ArrayList<Integer> objects = new ArrayList<>();
int curSize = 0;
for(int i : array){
if(i == 1)curSize++;
else if(curSize > 0){
objects.add(curSize);
curSize = 0;
}
}
if(curSize > 0){
objects.add(curSize);
}
return objects;
}
}
Perhaps the changes below could solve your problem. Do not look over to the next element, just keep a count and keep working with the current element always.
public static int process(int[][] array)
{
int result = 0;
// Get row and column size
int rows = array.length;
int cols = array[0].length;
// variables to store sum of large and small objects
int large = 0;
int small = 0;
for(int i=0; i<rows; i++) {
// variables to store large and small objects per row level
int l_count = 0;
int s_count = 0;
int count = 0;
for(int j=0; j<cols; j++) {
if (array[i][j]) {
count++;
} else if (count > 0) {
if (count > l_count) {
l_count = count;
} else if (count < s_count || s_count == 0) {
s_count = count;
}
count = 0;
}
}
// Add row level counts to large and small objects
large += l_count;
small += s_count;
}
result = large - small;
return result;
}

Putting a number at random spots in 2D array

I have a 2D Array that has 5 rows and 5 columns. I want it so that at 8 random spots in that 2D array (make it pick a random row and column) to put a char of '1'.
What I did was call the Random class and generate a number between 0 and 4 (for the 5 spots of the array) then I have two for loops that run 8 times (for the 8 random spots I want), one that goes through the row, the other through the column.
This is the code I have so far:
char[][] battleship = new char[5][5];
//I didn't include this but I have a for loop that populates all the rows and columns with a char of '0'
Random random = new Random();
int randomNum = random.nextInt(4);
for (int i = 0; i < 8; i++)
{
for (int o = 0; o < 8; o++)
{
battleship[randomNum][randomNum] = '1';
}
}
The issue I am getting is that instead of it putting the '1' at 8 random spots, it's putting in 5 spots back to back.
How do I correct this?
Here is an example of the output:
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
0
0
0
0
0
The '1' isn't in 8 random spots.
Where did I go wrong?
Having nested loop running 8 times each will iterate it 64 times. You don't need nested loops to do that. One of the easy ways will be using a while-loop and distribute the 8 random spots till all 8 spots are taken:
int occupiedSpots = 0;
Random random = new Random();
while(occupiedSpots < 8){
int x = random.nextInt(array.length);
int y = random.nextInt(array[0].length);
if(battleship[x][y] == 0){
battleship[x][y] = 1;
occupiedSpots++;
}
}
Also ensure you are generating new random numbers in every iteration, else you will always be using the same random values.
Using a while-loop also ensures all 8 spots are on different locations. If you simply implement it with a for-loop without checking, there is a tendency some spots may fall on the same location twice.
You are getting a random number before the loop, so it never changes. Basically, randomNum variable was rolled and assigned once - you should call the nextInt method multiple times. Try this:
for (int i = 0; i < 8; i++) {
int randomX = random.nextInt(battleship.length);
int randomY = random.nextInt(battleship[randomX].length);
battleship[randomX][randomY] = '1';
}
Note that this do not address the issue of collisions - you can be unlucky enough to get the same position multiple times and fill only 1-7 spots.
From the documentation of nextInt(int):
Returns a pseudorandom, uniformly distributed int value between 0
(inclusive) and the specified value (exclusive), drawn from this
random number generator's sequence.
I would take a different approach. If you pretend your 5x5 2D array is actually one long 25-element one-dimensional array, than basically all you need to do is produce 8 distinct numbers between 0 and 25.
Your code also doesn't guarantee the 8 random numbers are all different.
Try this:
// Initialize random number array
int[] positions = new int[25];
for (int i = 0; i < 25; i++) {
positions[i] = i;
}
char[][] battleship = new char[5][5];
// Fill the battleship field
for (int i = 0; i < 8; i++) {
int random = (int)(Math.random() * (25 - i - 1));
int position = positions[random];
positions[random] = positions[25 - i - 1];
int row = position / 5;
int col = position % 5;
battleship[row][col] = '1';
}
// Show the field
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
System.out.print(battleship[row][col] + " ");
}
System.out.println();
}

How to distribute integers topdown a set of persons?

I have a sorted set of persons, and I want to distribute a defined count topdown.
Example:
Scenario:
int i = 8;
personA = 0;
personB = 0;
personC = 0;
Desired result:
personA = 3;
personB = 3;
personC = 2;
Of course I could iterate with for/while loops, but if i is very large that might be inefficient.
Can the result also be optained by devision, without loops?
Pseudocode:
distributed = 0;
while (distributed < i) {
for (int p : persons) {
p++;
distributed++;
}
}
interation steps:
1-1-1 > start over > 2-2-2 > start over > 3-3-2
Yes, of course it's possible. Since you didn't give any particular implementation, I'll just put the numbers in an array. This is just a demonstration, of course. Assume NUM_OF_PERSONS is the number of persons in your array, and NUM_TO_DISTRIBUTE is the number you want to distribute. In your example, 8.
int persons[] = new int[NUM_OF_PERSONS];
int basicRation = NUM_TO_DISTRIBUTE / NUM_OF_PERSONS;
int peopleGettingExtra = NUM_TO_DISTRIBUTE % NUM_OF_PERSONS;
for ( int i = 0; i < NUM_OF_PERSONS; i ++ ) {
persons[i] = basicRation + ( i < peopleGettingExtra ? 1 : 0 );
}
Test case 1: 9 to give, 3 people. The base ration is 3. The number of people getting extra is zero. In the loop, since no i will be less than 0, everybody will get basicRation + 0 which means 3.
Test case 2: 8 to give, 3 people. The base ration is 2. The number of people getting extra is 2. In the loop, people with indexes 0 and 1 will get 2 + 1, and the last person gets 2 + 0.
distributed = 0;
int howManyForEach = (int)(i/persons.size())
for(int p : person){
p = howManyForEach ;
}
distrubuted = howManyForEach * persons.size();
while (distributed < i) {
for (int p : persons) {
p++;
distributed++;
}
}
The following code will run for i=8 5 times, i=10 4 times, as i increase , number of time loop executed is proportionately less
int i = 10;
int[] arr = new int[3] { 0, 0, 0 };
int BaseVal = i / (arr.Length);
int Remainder = i - (BaseVal * arr.Length);
for (int x = 0; x < arr.Length ;x++)
{
arr[x] = BaseVal;
}
for (int y = 0; y < Remainder; y++)
{
arr[y] = arr[y] + 1;
}
from the problem it is clear, that the maximal difference between any person is 1.
Just use #persons / i = base value for every person
and #persons % i = # top persons with +1 value
Example
i = 5 , persons = 3:
baseValue = 1
#topPersons = 2
=> 2 2 1

Predicament with arrays and expansion

So I know that the java convention is to use ArrayList<> when it comes to expansions and many other applications. The typical array cannot expand. My java course is elementary so we are still reviewing over arrays right now. As much as I want to use an arraylist I cant. How do I make it to where I store only elements that satisfy the condition in my counter array?
public int[] above100Degrees()
{
int[] blazing = new int[temps.length];
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
blazing[i] = temps[i];
}
}
return blazing;
}
Output
The temperature above 100 degrees is: 0 0 0 0 0 0 0 103 108 109
Just count how many elements match your filter first, then create the array, then populate it. It means you'll need to go through the array twice, but there are no really nice alternatives unless you want to end up creating multiple arrays. So something like:
public int[] above100Degrees() {
// First work out how many items match your filter
int count = 0;
// Are you allowed to use the enhanced for loop? It's not necessary, but it
// makes things simpler.
for (int temp : temps) {
if (temp > 100) {
count++;
}
}
// Create an array of the right size...
int[] ret = new int[count];
// ... and populate it.
int index = 0;
for (int temp : temps) {
if (temp > 100) {
ret[index++] = temp;
}
}
return ret;
}
I would use a loop to find how many are above 100 before assigning the array.
public int[] above100Degrees()
{
int newArrayLength=0;
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
newArrayLength++;
}
}
int[] blazing = new int[newArrayLength];
int positionInNewArray = 0;
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
blazing[positionInNewArray] = temps[i];
positionInNewArray++;
}
}
return blazing;
}
You could do a ternary operation
resultString = "The temperature above 100 degrees is: ";
for(int i = 0; i < blazing.length; i++){
resultString += blazing[i] != 0 ? blazing[i] : "";
}
Note: This would require more memory than JonSkeets answer, but could potentially be more efficient. If your expect your array length to get very large, go with JonSkeet's answer. In other words, this won't scale well.
One way is to count things before setting up the array. Another way is to set up the array first and keep track of the count, then create a new array:
public int[] above100Degrees()
{
int[] blazing = new int[temps.length];
int count = 0;
for( int i = 0; i < temps.length; i++ )
{
if( temps[i] > 100 )
{
blazing[count++] = temps[i];
}
}
// At this point, `count` is the number of elements you're going to return;
// and the first `count` elements of `blazing` hold those elements, while the
// remaining elements of `blazing` are garbage
int[] result = new int[count];
for ( int i = 0; i < count; i++ )
result[i] = blazing[i];
return result;
}
This approach would be better if the condition you're testing for takes a lot of time to calculate (as opposed to temps[i] > 100, which hardly takes any time). You could use Arrays.copy to create the result array, but if you can't use ArrayList you probably can't use Arrays.copy either.
Your code is keeping the cells in blazing array for i <= 100; You need to ignore these and start populating from i = 101.

Categories

Resources