在 Java 中生成随机矩阵的最佳方法
The best way to generate a randomised matrix in Java
所以我在我的程序中使用了一个偏好矩阵(二维数组),其中每个人都对其他成员进行排名,从最喜欢到最不喜欢。个人在数组中将自己排在最后。
例如:
{[1, 2, 3, 4, 5, 0],
[2, 3, 4, 5, 0, 1],
[3, 4, 5, 0, 1, 2],
[4, 5, 0, 1, 2, 3],
[5, 0, 1, 2, 3, 4],
[0, 1, 2, 3, 4, 5]}
如何随机生成一个像上面的矩阵,其中最后一个元素代表用户的编号0-n,其余n-1个元素是随机序列中的任意数字?
编辑:
首先生成矩阵,其中每行中的数字为 0,1,2...n-1
之后在每个矩阵的行上调用 shuffle 方法。
这是简短的代码片段:
public int[][] generateRandomMatrix(int rows,int cols){
int[][] matrix = new int[rows][cols];
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
matrix[i][j]=j;
}
shuffle(matrix[i]);
}
return matrix;
}
public void shuffle(int[] arrray){
Random r = ThreadLocalRandom.current();
int n = arrray.length;
for (int i=0;i<n;i++){
int index = r.nextInt(i + 1);
int a = arrray[index];
arrray[index] = arrray[i];
arrray[i] = a;
}
}
您可以使用 Collections.shuffle(list)
来随机排列任何列表,但奇怪的是,java API 没有 Arrays.shuffle(arr)
,并且转 int[]
放入一个集合中,这样你就可以将它提供给 Collections.shuffle
效率相当低。
对于你拥有的那么大的集合,这真的无关紧要;只有当我们谈论 100,000 个或更多数字时,这才会成为一个问题。
因此,简单易读的方法是列出除个人索引之外的所有元素,将其洗牌,在最后扔掉用户的索引,瞧:
public int[][] makeRandom(int n) {
int[][] out = new int[n][];
for (int i = 0; i < n; i++) { // for each person
// make a list of their preferences
List<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < n; j++) {
// add everybody except yourself
if (j != i) list.add(j);
}
Collections.shuffle(list); // randomize
list.add(i); // add yourself
// turn into an int array
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
// set the int array, representing person i's prefs.
out[i] = arr;
}
return out;
}
如果你真的需要一个运行尽可能高效的算法,你必须创建一个新的 java.util.Random
实例,并使用它的 .nextInt()
方法,这样你就可以应用Fisher-yates 自己的洗牌算法,in-place,在现有的 int 数组列表上,甚至在洗牌期间方便地跳过数组中的最后一个数字。那将是更多的代码,需要更多复杂的注释。无论如何,请随意,但我会将其作为练习留给 reader.
How can I randomly generate a matrix like the one above, where the last element represents the user's number 0-n and the remaining n-1 elements are arbitrary numbers in a random sequence?
Here is another way to do it.
int n = 6;
这定义了一个简单的 lambda
- 有效地将用户号码移动到数组末尾
- 打乱数组,排除最后一个元素(用户编号)。
BiFunction<int[],Integer,int[]> shuffle = (ar,ii)->{
int len = ar.length-1;
ar[len] = ar[ii];
ar[ii] = len--;
while (len >= 0) {
int i = (int)(Math.random()*len);
int t = ar[i];
ar[i] = ar[len];
ar[len--]=t;
}
return ar;
};
现在构建一个来自 0 to n
.
的单个数组
int[] arr = IntStream.range(0, n).toArray();
时间 shuffle
阵列和 move
用户到
结束并创建最终的数组数组。
int[][] darr = IntStream.range(0, n)
.mapToObj(i -> shuffle.apply(arr.clone(), i))
.toArray(int[][]::new);
现在打印它们
for (int[] a : darr) {
System.out.println(Arrays.toString(a));
}
对于n = 6
,打印
[2, 4, 3, 1, 5, 0]
[2, 4, 5, 0, 3, 1]
[4, 5, 3, 0, 1, 2]
[4, 0, 5, 1, 2, 3]
[5, 3, 0, 2, 1, 4]
[3, 0, 4, 2, 1, 5]
所以我在我的程序中使用了一个偏好矩阵(二维数组),其中每个人都对其他成员进行排名,从最喜欢到最不喜欢。个人在数组中将自己排在最后。
例如:
{[1, 2, 3, 4, 5, 0],
[2, 3, 4, 5, 0, 1],
[3, 4, 5, 0, 1, 2],
[4, 5, 0, 1, 2, 3],
[5, 0, 1, 2, 3, 4],
[0, 1, 2, 3, 4, 5]}
如何随机生成一个像上面的矩阵,其中最后一个元素代表用户的编号0-n,其余n-1个元素是随机序列中的任意数字?
编辑: 首先生成矩阵,其中每行中的数字为 0,1,2...n-1 之后在每个矩阵的行上调用 shuffle 方法。
这是简短的代码片段:
public int[][] generateRandomMatrix(int rows,int cols){
int[][] matrix = new int[rows][cols];
for(int i=0;i<rows;i++) {
for(int j=0;j<cols;j++) {
matrix[i][j]=j;
}
shuffle(matrix[i]);
}
return matrix;
}
public void shuffle(int[] arrray){
Random r = ThreadLocalRandom.current();
int n = arrray.length;
for (int i=0;i<n;i++){
int index = r.nextInt(i + 1);
int a = arrray[index];
arrray[index] = arrray[i];
arrray[i] = a;
}
}
您可以使用 Collections.shuffle(list)
来随机排列任何列表,但奇怪的是,java API 没有 Arrays.shuffle(arr)
,并且转 int[]
放入一个集合中,这样你就可以将它提供给 Collections.shuffle
效率相当低。
对于你拥有的那么大的集合,这真的无关紧要;只有当我们谈论 100,000 个或更多数字时,这才会成为一个问题。
因此,简单易读的方法是列出除个人索引之外的所有元素,将其洗牌,在最后扔掉用户的索引,瞧:
public int[][] makeRandom(int n) {
int[][] out = new int[n][];
for (int i = 0; i < n; i++) { // for each person
// make a list of their preferences
List<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < n; j++) {
// add everybody except yourself
if (j != i) list.add(j);
}
Collections.shuffle(list); // randomize
list.add(i); // add yourself
// turn into an int array
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
// set the int array, representing person i's prefs.
out[i] = arr;
}
return out;
}
如果你真的需要一个运行尽可能高效的算法,你必须创建一个新的 java.util.Random
实例,并使用它的 .nextInt()
方法,这样你就可以应用Fisher-yates 自己的洗牌算法,in-place,在现有的 int 数组列表上,甚至在洗牌期间方便地跳过数组中的最后一个数字。那将是更多的代码,需要更多复杂的注释。无论如何,请随意,但我会将其作为练习留给 reader.
How can I randomly generate a matrix like the one above, where the last element represents the user's number 0-n and the remaining n-1 elements are arbitrary numbers in a random sequence? Here is another way to do it.
int n = 6;
这定义了一个简单的 lambda
- 有效地将用户号码移动到数组末尾
- 打乱数组,排除最后一个元素(用户编号)。
BiFunction<int[],Integer,int[]> shuffle = (ar,ii)->{
int len = ar.length-1;
ar[len] = ar[ii];
ar[ii] = len--;
while (len >= 0) {
int i = (int)(Math.random()*len);
int t = ar[i];
ar[i] = ar[len];
ar[len--]=t;
}
return ar;
};
现在构建一个来自 0 to n
.
int[] arr = IntStream.range(0, n).toArray();
时间 shuffle
阵列和 move
用户到
结束并创建最终的数组数组。
int[][] darr = IntStream.range(0, n)
.mapToObj(i -> shuffle.apply(arr.clone(), i))
.toArray(int[][]::new);
现在打印它们
for (int[] a : darr) {
System.out.println(Arrays.toString(a));
}
对于n = 6
,打印
[2, 4, 3, 1, 5, 0]
[2, 4, 5, 0, 3, 1]
[4, 5, 3, 0, 1, 2]
[4, 0, 5, 1, 2, 3]
[5, 3, 0, 2, 1, 4]
[3, 0, 4, 2, 1, 5]