0

So I'm using a preference matrix (2d array) within my program, in which each person ranks the other members from most favorable to least favorable. The individual ranks themselves last in the array.

For example:

{[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]}

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?

amazmus
  • 3
  • 1
  • 3
    What have you tried so far? Also, please define "*best*" since it can mean different things to different people. – Turing85 Aug 11 '20 at 21:15
  • What part of this don't you know how to do yourself? Are you able to generate a single row satisfactorily, for example? – Karl Knechtel Aug 11 '20 at 21:57

3 Answers3

0

You can use Collections.shuffle(list) to shuffle any list, but bizarrely, the java API does not have an Arrays.shuffle(arr), and turning an int[] into a collection so you can feed it to Collections.shuffle is rather inefficient.

For sets as large as you have, that really doesn't matter; that only becomes an issue once we're talking about 100,000 numbers or more.

So, the easy, readable way, is to make a list of all elements except the person's own index, shuffle that, toss the user's index at the very end, and voila:

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

If you really do need an algorithm that runs as efficient as can be, you'd have to make a new instance of java.util.Random, and use its .nextInt() method so that you can apply the Fisher-yates shuffle algorithm yourself, in-place, on an existing int array list, even conveniently skipping the very last number in the array during your shuffle. That'd be a lot more code that requires a lot more convoluted comments. By all means, feel free, but I'll leave that as an exercise to the reader.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
0

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;

This defines a simple lambda to

  • effectively move the user's number to the end of the array
  • shuffle the array, excluding the last element (user's no).
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;
};
    

Now build a single array that goes from 0 to n.

int[] arr = IntStream.range(0, n).toArray();

Time to shuffle the array and move the user to the end and create the final array of arrays.

int[][] darr = IntStream.range(0, n)
        .mapToObj(i -> shuffle.apply(arr.clone(), i))
        .toArray(int[][]::new);

Now print them

for (int[] a : darr) {
    System.out.println(Arrays.toString(a));
}

For n = 6, prints

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

WJS
  • 36,363
  • 4
  • 24
  • 39
-1

Edit: First generate matrix in which number in each row goes 0,1,2...n-1 After that call shuffle method on each matrix's row.

Here is short code snippet:

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;
        }
      }
Bane2000
  • 187
  • 1
  • 9
  • This is not useful; the question involves generating each sub-array as containing all the numbers exactly once, with the very last entry always being a fixed value. Yours may skip numbers and duplicate others, as random is wont to do. That's not what OP wants. – rzwitserloot Aug 11 '20 at 21:26
  • Oh i didn't get that. I will correct my code – Bane2000 Aug 11 '20 at 21:31