在 while 循环中生成随机数,直到生成所需的数字

Generating random numbers inside while loop until desired number is generated

我正在编写的程序的一个功能要求我生成随机数。然而,这些数字中大约有 50% 将无效(哪些数字有效将取决于我的代码的前面部分)所以我最初的想法是在 while 循环内生成随机数,直到生成正确的数字。

不过,我在想这是否是一个好习惯。理论上我应该在几次重复中得到正确的数字(只有 0,5^10 = 0,1% 的机会程序将在 10 次重复中失败)但是它仍然将我的程序的性能绑定到随机数.那么,尽管概率几乎微不足道,但这是一种不好的做法吗?

有一些变通方法不需要我使用循环,但它们会使我的那部分代码更加混乱。有人可以称他们为“过度工程化”。我不需要 4 行代码来生成和检查随机数,而是需要 30-40 行并声明一些新变量(可能是数组)来跟踪有效值和无效值。生成一个数字并检查它是否有效似乎更容易。

编辑: 代码还没写。我正处于计划阶段,但为了清楚起见,我写了一段代码来描述我的问题到底是什么。我还添加了一些评论。每当我尝试代码时,它的性能都没有问题,循环也不会经常重复,但我的确切问题是:这是一种不好的做法吗,因为没有 100% 确定的方法退出循环,并且可能会无限地重复自己(如果我们非常非常倒霉)

public class App {
public static void main( String[] args ) {

    //creating a matrix that will have three horizontal or vertical "walls" of the length 5.
    int[][] matrix = new int[10][10];

    Random randomer = new Random();

    //loop will repeat 3 times creating new wall for each repetition
    for (int i = 0; i < 3; i++) {

        //for 0 wall horizontal, for 1 wall vertical
        int direction = randomer.nextInt(2);

        while (true) {

            // generating position of the first block of the wall. Wall cannot exceed matrix so one number shorter
            int axis1 = randomer.nextInt(10);
            int axis2 = randomer.nextInt(5);

            //walls cannot cross other wall so condition will check if all 5 fields are empty
            //for horizontal
            if (direction == 0 && matrix[axis2][axis1] == 0 && matrix[axis2 + 1][axis1] == 0
                    && matrix[axis2 + 2][axis1] == 0 && matrix[axis2 + 3][axis1] == 0
                    && matrix[axis2 + 4][axis1] == 0) {

                matrix[axis2][axis1] = 1;
                matrix[axis2 + 1][axis1] = 1;
                matrix[axis2 + 2][axis1] = 1;
                matrix[axis2 + 3][axis1] = 1;
                matrix[axis2 + 4][axis1] = 1;
                break;

            //for vertical
            } else if (matrix[axis1][axis2] == 0 && matrix[axis1][axis2 + 1] == 0
                    && matrix[axis1][axis2 + 2] == 0 && matrix[axis1][axis2 + 3] == 0
                    && matrix[axis1][axis2 + 4] == 0) {

                matrix[axis1][axis2] = 1;
                matrix[axis1][axis2 + 1] = 1;
                matrix[axis1][axis2 + 2] = 1;
                matrix[axis1][axis2 + 3] = 1;
                matrix[axis1][axis2 + 4] = 1;
                break;
            }
        }

    }

    //String visualisation of the matrix
    String textualMatrix = "";

    for (int i = 0; i < 10; i++) {
        for (int k = 0; k < 10; k++) {
            textualMatrix = textualMatrix + matrix[i][k] + "  ";
        }
        textualMatrix = textualMatrix + "\n";
    }
    System.out.println(textualMatrix);
}
}

一个输出示例:

0  0  0  0  1  0  0  0  0  0  
0  0  0  0  1  0  0  0  0  0  
0  0  0  0  1  0  0  0  0  0  
0  0  0  0  1  0  0  0  0  0  
0  0  0  0  1  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  
0  0  0  0  0  0  0  0  0  0  
0  0  0  0  0  0  0  0  0  0  
0  0  0  1  1  1  1  1  0  0  

空白区域用 0 表示,3 面墙用 1 表示。

所以重复我的问题:是否有任何好的编码实践原则应该阻止我使用这样的解决方案?我希望代码不仅可以工作,而且是一个质量代码。

快速回答:谢谢大家的帮助!因此,据我从您的回答中了解到:这不是一个坏习惯!然而,在某些情况下(例如救生应用程序或更大的性能负担)跟踪有效值而不是以代码简单为代价随机生成是合理的,并且可能是更好的解决方案。个案应单独评估。

理想情况下,您会在调用随机数之前排除无效的可能性。最终在两个轴上进行一次随机调用(-> 10x10 的可能性)减去无效的。

但是如果这变成了over-complicated,我认为你的方法还可以,没有什么不好的做法。你的使命有多重要?例如,如果涉及到人的生命,那将是太大的风险。如果是游戏或者商店,用户可以re-try的东西就够了。

你真的想要这个无限循环吗?也许限制在 100 或 1000 次。如果结束,则记录并中止。如果可能,然后监控您的日志。如果错误率上升,您可以 re-consider 您的算法(也许如果墙变长或矩阵变小,您将有更多无效组合)。

随机数生成带来的性能不确定是理论上的问题。根据数字的范围,在找到允许的值之前您不会进行多次尝试,并且每次尝试本身都是快速的。但是假设需要 1000 次尝试才能找到允许的坐标 - 最终用户甚至可以感知吗?

尽管如此,另一种方法是维护一个包含矩阵中允许的坐标的列表。从该列表中随机 select,您一定会获得允许的值。这增加了复杂性,因为您必须维护该列表。这种复杂性可能没有必要 - 请参阅第一段。

我记得在大学时,可以使用这种技术轻松地对奇怪的分布进行建模。它被称为 'Rejection Sampling'。

例如,如果您想要一个介于 0 和 100 之间的数字,除了例如42,应该比任何其他数字少 10%。

那么你会有这样的算法:

while true:
  result = random.nextInt(100)
  if result != 42:
    return result
  else:
    // 42 is skipped in 10% of cases
    if random.nextInt(10) != 9:
      return result
    else:
      // do nothing, go back through the while true loop

所以这是一个有效的技术,至少在学术上是这样。

有关更复杂的示例,请观看此视频:https://www.youtube.com/watch?v=IUL0TOLYNLE

但在这种情况下,只有 0.1% 的实际需要重复,0.0001% 的变化需要重复 2 次。

但在现代系统中,有 50% 的机会重复,并将其限制在 1000 次左右可能还不错。

如果您在 10x10 space 中创建 3 面长度为 5 的墙,那么您将始终有一个放置有效墙的位置。所以你的方法会很好用。

如果您更改墙的数量,则不清楚何时可以获得不可能的配置。

如果你改变长度呢?我建议创建一个方法来检查一个点是否有效。

boolean isValidHorizontal( int[][] matrix, int x, int y, length ){
    int length = 5;
    if(y + length > matrix.length ){
        return false;
    }
    for(int j = 0; j<length; j++){
        if(matrix[y][x + j] != 0){
            return false;
        }
    }
    return true;
}

然后你可以过滤你可以过滤所有的点。

int n = matrix.length * matrix[0].length;
List<int[]> hPoints = new ArrayList<>(n);
for(int i = 0; i<matrix.length; j++){
    for(int j = 0; j<matrix[0].length; j++){
        if( isValidHorizontal(matrix, i, j) ){
            hPoints.add(new int[]{i, j});
        }
    }
}

并且 return 随机有效。

if( hPoints.size() > 0){
    return hPoints.get( random.nextInt( hPoints.size() ) );
}

这里有一个权衡。您必须扫描每个点并将其存储在列表中。当你有几行时,我希望这比随机选择一行要慢。一旦你有很多行,那么这会更快。此外,如果您别无选择,这会让您知道。