在 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]