【问题标题】:Java Math.random with arrays带有数组的 Java Math.random
【发布时间】:2015-01-07 14:46:43
【问题描述】:

我正在尝试使用二维数组中随机坐标中的字符(例如 A)填充 5 x 5 的二维数组。当我使用我的嵌套 for 循环时,我想制作 2d 数组的坐标,其中我的 char 将是随机的。所以假设我要求 40% 的 A,在 5 x 5 中我应该得到 10 个 A,但我得到 8 个。当我运行它时,它有时不会显示我想要的 A 的百分比。它只会像 6 一样打印出来。这是因为当 if 语句中的 row 和 col 是随机的,for 循环中的 row 和 col 也是随机的吗?这就是为什么 char 有时填充的数量少于要求的数量,因为如果数字随机化 2d 数组的长度为 5,for 循环就会停止?

此外,当它打印出 10 个字符时,有时它们会超过 5 乘 5。例如,第 2 行中的 2 As 和第 3 行中的 7 和第 3 行中的 1。这是为什么呢?

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();
  }
}

【问题讨论】:

  • 这是您的原始代码吗? cellx 看起来应该是 charx
  • 是的,我首先做了 cellx 来制作我的 char 'X' 但后来意识到它可能是任何 char 所以改了名字,错过了谢谢你指出来!
  • 还有一堆类的代码有些相同,可以尝试不同的方法,这可能就是我错过它的原因。

标签: java arrays random


【解决方案1】:

你的代码应该是这样的

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();
  }

如果您只是想在随机位置插入一个字符,则无需遍历数组并使用嵌套循环。

还有

这是因为当if语句中的row和col是 随机化,那么 for 循环中的 row 和 col 是吗?这就是为什么 char 有时会填充的原因 少于要求,因为如果数字,for循环停止 随机化二维数组的长度为 5?此外,当它打印出 10 个字符时, 有时他们会超过 5 乘 5。一个例子是 2 如 第 2 行和第 7 行,第 3 行第 1 行。这是为什么呢?

或多或少。您将行和列随机化,但这样做可能会导致通过数组的迭代过早结束。作为最坏的情况,请考虑如果您第一次输入if 语句时随机函数将这4 个值分配给rowcol 会发生什么情况。一般来说,你确定grid方法结束时charx总是等于0吗?


注意事项

正如马特在下面的 cmets 中指出的,这个方法没有检查数组;因此,它假设数组始终是一个正方形(即行 X 列 = n X n)。 如果您想强制使用方形数组,您可能需要创建一个包装类,例如

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();
    }
}

然后,您可以简单地将grid 代码更改为

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();
}

【讨论】:

  • +1 同意这一点,尽管如果他希望在网格中插入特定百分比的 A,他还应该在减少 charx 之前检查目标单元格是否已经包含“A”。我还考虑将charx 重命名为charsToPlace 之类的合理名称......而且看起来这段代码假设网格是方形的
  • @MattCoubrough 是的,代码中有一些不愉快的命名,我已经修改了我的答案。
  • 请注意,当 percentOfChar 很高时,这开始变得非常低效,因为您开始尝试更多的尝试放置在已经包含“A”的单元格中......我过去解决这个问题的一种方法在我的代码中是创建所有可能的单元格位置的列表,打乱该列表,然后将一个对象放入打乱列表中的第一个 n 单元格位置......这个解决方案在初始列表创建后变为 O(N) 并且洗牌是理想的。
  • @MattCoubrough 很棒,这应该是另一个有用的技巧。另外,关于方格部分,我认为您是对的。在正方形之前,它假设数组也是矩形的,但情况并非总是如此。那么,您认为什么是一个好的解决方法?一些想法:1)遍历整个数组(听起来效率太低); 2) 提供列和行长度作为参数; 3)让一切保持原样,并警告用户array参数应该是一个方形数组。
  • 已添加我的随机播放方法答案以确保完整性
【解决方案2】:

为了完整起见,这是我在 tigerjack's 解决方案下的 cmets 中提到的内容。根据 cmets,我会为网格使用包装器,而不是原始多维数组。

我的随机放置解决方案稍微复杂一些,但对于更高的放置百分比(即,如果您尝试填充超过 90% 的单元格)会更有效,并且始终会填充精确指定的位置字符的百分比。

如果需要,您可以在percentOfCellsToSet 较低时使用tigerjack 的方法进行随机放置,而当percentOfCellsToSet 较高时使用if 方法中的if 语句使用此方法。

这是我使用随机列表方法的完整可编译示例:

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();
    }

}

这是main() 方法中代码的示例输出:

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

希望有人觉得这很有帮助。

【讨论】:

  • 嗯,你知道,我就像 S. Thomas,我用我的和你的代码进行了一些测试,这些是 100,000 次模拟的结果,具有最小值、最大值和平均值(假设 15x15大批)。使用 percentOfCellToSet = 90% pastebin.com/W90WUALR 使用 percentOfCellToSet = 40% pastebin.com/XAgaBru5
  • 很好地使用分析:在 1000 x 1000 网格和 95% 的百分比下,我在机器上使用我的方法可以获得稍微更好的性能(省略任何可能会扭曲结果的控制台输出)。在 1000 x 1000 网格上 100% 覆盖时,我的方法的性能提高了 3 倍以上。在 90% 时,您的方法似乎总是更快。显示分析的重要性!
  • 您使用的是第一种方法(没有包装类),对吧?我已经使用第二个(使用 IntegerSquareArray 类)对其进行了测试。此外,我已经修改了我的测试用例,以考虑您使用 IntegerSquareArray 的随机播放方法。我会在几分钟后显示结果。
  • 这是我使用的测试代码pastebin.com/nAbVvdyZ(它应该在某些方面进行改进,但它确实有效,所以...... :))这些是一些输出pastebin.com/1udyV48J非常有趣跨度>
  • 作为总结,假设 x 要设置的单元格百分比: - 0
【解决方案3】:

您为循环变量分配随机值...现在这可能是不确定的...

基本上,如果第一个 Math.random() 结果为 arr.length - 1 而第二个结果为 arr[arr.length - 1].length -1,则您的嵌套循环将结束。

我严重怀疑这是你想要的。

要控制将多少随机As 放入数组,只需为此使用循环,但不要将随机值分配给循环变量:

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

另请注意,这仍然可能导致A 比百分比表示的要少,因为可能会多次生成相同的随机位置,因此可能会多次将相同的数组元素设置为A

如果要生成As 的精确计数,如果随机位置已经是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] + "  ");
}

【讨论】:

    【解决方案4】:

    您的代码会随机放置“A”,因此某些“A”可能会放置在同一个位置。

    让我们计算看到 10 个“A”作为结果的可能性。

    第一个“A”总是空的,所以你会在 100% 处看到 1 个“A” 对于放置第二个“A”,有 1 个位置被“A”占据,24 个空位,因此在放置第二个“A”后您会看到 2 个“A”,占 96%。 (第二个 A 可以放置在第一个“A”放置在 25 (4%) 中的可能性 1 中。 第三,可能性是 (24/25) * (23/25)。 ...在第 4 到第 9 中省略。 对于 10 日,您会在 (24/25)(23/25)(22/25)(21/25)(20/) 中看到 10 个“A” 25)(19/25)(18/25)(17/25)(16/25)。 (数值约为12.4%)

    这个计算表明你的代码可能会在结果中显示 10 个“A”,大约每 8 个。

    【讨论】:

    • 好吧,其实这不是 As 显示不好的主要原因。如果您再次检查代码,您可以看到随机函数可能导致通过数组的迭代过早结束。
    • 我认为“A”是随机放置的。问题是没有人可以从打印的“A”中区分单个“A”和多个“A”。
    • 不正确。如果第一次输入 if 语句时随机函数将值 4 分配给 rowcol 怎么办?一般来说,你确定grid方法结束时charx总是等于0吗?
    猜你喜欢
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    相关资源
    最近更新 更多