0

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();
  }
}
3
  • Is this your original code? cellx looks like it should be charx? Commented Nov 11, 2014 at 5:49
  • yeah, I did cellx first to make my char 'X' but then realized it could be any char so changed the name , missed that thanks for pointing it out! Commented Nov 11, 2014 at 16:13
  • also had a bunch of classes with somewhat the same code to try different ways so that could of been why i missed it. Commented Nov 11, 2014 at 16:30

4 Answers 4

2

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 (i.e. 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, e.g.

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();
}
Sign up to request clarification or add additional context in comments.

10 Comments

+1 agree with this, although if he's looking to insert a specific percentage of A's in the grid he ought to also check whether the target cell already contains an 'A' before decrementing charx. I'd also consider renaming charx to something sensible like charsToPlace... also it appears this code is assuming the grid is square
@MattCoubrough yeah, some unhappy naming in the code, I've modified my answer.
Be warned that this starts to become horribly inefficient when percentOfChar is high, because you start getting more attempts to place in cells that already contain 'A's... One way I have got around this in the past in my code is to create a list of all possible cell positions, shuffle that list, then place an object into the first n cell positions from the shuffled list... this solution becomes O(N) after the initial list creation and shuffle which is ideal.
@MattCoubrough great, it should be another useful trick. Also, about the grid square part, I think you're right. Before square, it assumes that the array is rectangular also, that is not always the case. So, what do you think could be a good workaround? Some ideas: 1) iterate through the whole array (sounds too unefficient); 2) provide a column and row length as parameters; 3) let everything as is and warn the user that array parameter should be a square array.
Have added my shuffle approach answer for completeness
|
1

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.

5 Comments

well, you know, I'm like S. Thomas and I tested a bit with mine and your code and these are the results of 100,000 simulations with min, max and average values (it assumes a 15x15 array). With percentOfCellToSet = 90% pastebin.com/W90WUALR With percentOfCellToSet = 40% pastebin.com/XAgaBru5
Nice work with profiling: With a 1000 x 1000 grid and a percent of 95% I get marginally better performance with my method on my machine (omitting any console output which might skew results). At 100% coverage on a 1000 x 1000 grid I get more than 3x faster performance with my method. At 90% your method seems to always be quicker. Shows the importance of profiling!
you're using the first method (without the wrapper class), right? I've tested it using the second one (with the IntegerSquareArray class). Also, I've modified my test cases to take into account your shuffle approach with my IntegerSquareArray. I'll show the result in a few minutes.
This is the test code I used pastebin.com/nAbVvdyZ (it should be improved in some ways, but it works, so... :) ) and these are some outputs pastebin.com/1udyV48J Very interesting
As summary, assuming x the percentage of cells to be set: - 0<x<70 the simple method is better (almost 2 times faster on average) - 70<x<98 there is little difference; at 80% random placement method became a little more faster - 98<x<100 simple method raise the white flag: from 2 to 7 times slower than random placement method
0

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 As 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 As, 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] + "  ");
}

Comments

0

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.

3 Comments

well, actually it isn't the main reason why the As aren't displayed well. If you check the code again, you can see that the random functions could lead to the premature end of the iteration through the array.
I think "A"s are placed well randomly. Problem is Nobody can distinguish single "A" and multiple "A"s from the printed "A".
Not true. What if the first time you enter the if statement the random functions assign the value 4 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?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.