简体   繁体   中英

Java Math.random with arrays

I am trying to populate my 2d array that is 5 by 5 with a char such as A in random coordinates in the 2d array. When I use my nested for loop i wanted to make the coordinates of the 2d array where my char will be to be random. So lets say I asked for 40 percent of A's, in a 5 by 5 I should get 10 A's but I get 8. When i run it, it doesn't show the percentage of A's i wanted sometimes. it would only print out like 6. Is this because when the row and col in the if statement are randomized, so is the row and col in the for loop? Is this why the char sometimes populates less then asked for because the for loop stops if the number randomizes the length of the 2d array which is 5?

Also when it does print out 10 char, sometimes they go over the 5 by 5. an example would be 2 As in the line and 7 in the second and 1 in the 3rd. Why is that?

public static void grid(char[][] arr, int percentofchar)
{

  double charx = 0.0;

  double totalpercentchar = 0;
  totalpercentchar = (double) percentofchar / 100;

  cellx = totalpercentchar * (arr.length * arr.length);

  for (int row = 0; row < arr.length; row++)
  {
    for (int col = 0; col < arr[row].length; col++)
    {
      if (charx > 0)
      {
        row = (int) (Math.random() * arr.length);
        col = (int) (Math.random() * arr.length);
        charx--;
        arr[row][col] = 'A';
        System.out.print(arr[row][col] + "  ");
      }
    }
    System.out.println();
  }
}

your code should be something like

public static void grid(char[][] array, int percentOfChar)
  {
    int charsToPlace = (int) (percentOfChar * array.length * array.length / 100.0);
    while (charsToPlace > 0)
    {
        int row = (int) (Math.random() * array.length);
        int column = (int) (Math.random() * array.length);
        if (array[row][column] != 'A');
        {
            array[row][column] = 'A';
            charsToPlace--;
            System.out.print(array[row][column] + "  ");
        }          
    }
    System.out.println();
  }

No need to loop through the array and to use nested loop if you are only trying to insert a char in a random position.

Also

Is this because when the row and col in the if statement are randomized, so is the row and col in the for loop? Is this why the char sometimes populates less then asked for because the for loop stops if the number randomizes the length of the 2d array which is 5? Also when it does print out 10 char, sometimes they go over the 5 by 5. an example would be 2 As in the line and 7 in the second and 1 in the 3rd. Why is that?

More or less. You randomize row and column, but in doing so it could lead to a premature end of the iteration through the array. As a worst case scenario, consider what happens if the first time you enter the if statement the random functions assign the 4 values to both row and col . In general, are you sure that at the end of the grid method charx will always be equals to 0?


Considerations

As Matt pointed out in the below comments, this method has no check on the array; so, it assumes that the array is always a square one (ie row X column = n X n). If you want to force the use of a square array, you may want to create a wrapper class, eg

class IntegerSquareArray
{
    public final int length;
    int[][] array;
    IntegerSquareArray(int length)
    {
        this.length = length;
        this.array = new int[length][length];
    }

    public int getValue(int row, int column)
    {
        if (row < length && column < length)
            return array[row][column];
        throw new IllegalArgumentException();
    }

    public void setValue(int row, int column, int value)
    {
        if (row < length && column < length)
            array[row][column] = value;
        else throw new IllegalArgumentException();
    }
}

Then, you can simply change the grid code to be

public static void grid3(IntegerSquareArray integerSquareArray,
    int percentOfChar)
{
    int charsToPlace = (int) (percentOfChar * integerSquareArray.length
        * integerSquareArray.length / 100.0);
    while (charsToPlace > 0)
    {
        int row = (int) (Math.random() * integerSquareArray.length);
        int column = (int) (Math.random() * integerSquareArray.length);
        if (integerSquareArray.getValue(row, column) != 'A')
        {
            integerSquareArray.setValue(row, column, 'A');
            charsToPlace--;
            System.out.print(integerSquareArray.getValue(row, column) + "  ");
        }
    }
    System.out.println();
}

Just for completeness, here is what I mentioned in the comments under tigerjack's solution. As per the comments, I would use a wrapper for the grid rather than a raw multidimensional array.

My random placement solution is a little bit more complicated, but it will be much more efficient for higher placement percentages (ie. if you're trying to fill greater than 90% of the cells) and will always fill the exactly specified percent of characters.

If desired, you could use tigerjack's method for random placements when percentOfCellsToSet is lower, and this method when percentOfCellsToSet is higher using an if statement in the setRandomCells() method.

Here is my complete compileable example using the shuffled list method:

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyGrid
{
    private int width;
    private int height;
    private char[][] grid;

    public MyGrid(int width, int height, char defaultCell)
    {
        this.width = width;
        this.height = height;        
        grid = new char[height][width];

        // populate grid with defaultCells:
        for(int x=0; x < width; ++x)
        {
            for(int y=0; y < height; ++y)
            {
                grid[y][x] = defaultCell;
            }
        }
    }

    public int getWidth() { return width; }

    public int getHeight() { return height; }

    public char getCell(int x, int y) { return grid[y][x]; }

    public void setCell(int x, int y, char cellValue) { grid[y][x] = cellValue; }

    public void setRandomCells(char cellValue, float percentOfCellsToSet)
    {
        // determine the number of cells to set (rounding to nearest int):
        int numCellsToSet = (int)(Math.round((width * height * percentOfCellsToSet) / 100.0));

        // create a list containing all possible cell positions:
        List<Point> listAllCellLocations = new ArrayList<>();
        for(int x=0; x < width; ++x)
        {
            for(int y=0; y < height; ++y)
            {
                listAllCellLocations.add(new Point(x,y));
            }
        }

        // shuffle it
        Collections.shuffle(listAllCellLocations);

        // now set the cells
        for(int i=0; i < numCellsToSet; ++i)
        {
            Point pt = listAllCellLocations.get(i);
            setCell(pt.x, pt.y, cellValue);
        }
    }

    public void debugPrintGrid()
    {
        for(int y=0; y < height; ++y)
        {
            for(int x=0; x < width; ++x)
            {
                System.out.print(getCell(x,y));
            }
            System.out.println();
        }
    }

    public static void main(String[] args) 
    {
        MyGrid myGrid = new MyGrid(10, 10, '0');
        myGrid.setRandomCells('A', 68);
        myGrid.debugPrintGrid();
    }

}

and here is the sample output from the code in the main() method:

AAAA0A0AAA
0AAAAAAAA0
A00A00AA0A
AAAAA0AAA0
AA0A0AAAA0
0A0AAAA0AA
A0AAA0A0AA
A0A00AAAAA
AAA000A0A0
0AA0AAA0A0

Hope someone finds this helpful.

You assign random values to the cycle variables... Now this is as undeterministic as it could be...

Basically your nested loops will end if by any chance the first Math.random() results in arr.length - 1 and the 2nd results in arr[arr.length - 1].length -1 .

I seriously doubt this is what you wanted.

To control how many random A s to put into the array, just use a loop for that but don't assign random values to the cycle variable:

int max = arr.length * arr.length * percentofchar / 100;
for (int i = 0; i < max; i++) {
    // Put `A` at a random location
    int row = (int) (Math.random() * arr.length);
    int col = (int) (Math.random() * arr.length);
    arr[row][col] = 'A';
    System.out.print(arr[row][col] + "  ");
}

Also note that this still might result in less A than the percent would mean because the same random location might be generated multiple times, so the same array element might be set to A multiple times.

If you want to generate exact count of A s, you have to retry if the random location is already an A :

int max = arr.length * arr.length * percentofchar / 100;
for (int i = 0; i < max; i++) {
    // Put `A` at a random location
    int row, col;
    do {
        row = (int) (Math.random() * arr.length);
        col = (int) (Math.random() * arr.length);
    } while (arr[row][col] == 'A');
    arr[row][col] = 'A';
    System.out.print(arr[row][col] + "  ");
}

Your code places "A"s randomly, so some "A" may placed on same place.

Let calculate possibility to see 10 "A"s as result.

First "A" is always empty place, so you see 1 "A" at 100% For placing second "A", there are 1 place occupied by "A" and 24 empty place, so you see 2 "A" after placing second "A" at 96%. (Second A may placed where first "A" placed in possibility 1 out of 25(4%). For third, possibility is (24/25) * (23/25). ... omitted in 4th to 9th . For 10th, you see 10 "A"s in possibility of (24/25) (23/25) (22/25) (21/25) (20/25) (19/25) (18/25) (17/25) (16/25). (The value is about 12.4%)

This calculation says that you code may show 10 "A"s in result about once by eight.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM