在 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() ) );
}
这里有一个权衡。您必须扫描每个点并将其存储在列表中。当你有几行时,我希望这比随机选择一行要慢。一旦你有很多行,那么这会更快。此外,如果您别无选择,这会让您知道。
我正在编写的程序的一个功能要求我生成随机数。然而,这些数字中大约有 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() ) );
}
这里有一个权衡。您必须扫描每个点并将其存储在列表中。当你有几行时,我希望这比随机选择一行要慢。一旦你有很多行,那么这会更快。此外,如果您别无选择,这会让您知道。