Take n random elements from a List?

后端 未结 12 1451
天涯浪人
天涯浪人 2020-11-27 16:09

How can I take n random elements from an ArrayList? Ideally, I\'d like to be able to make successive calls to the take() method to get an

相关标签:
12条回答
  • 2020-11-27 16:29

    Two main ways.

    1. Use Random#nextInt(int):

      List<Foo> list = createItSomehow();
      Random random = new Random();
      Foo foo = list.get(random.nextInt(list.size()));
      

      It's however not guaranteed that successive n calls returns unique elements.

    2. Use Collections#shuffle():

      List<Foo> list = createItSomehow();
      Collections.shuffle(list);
      Foo foo = list.get(0);
      

      It enables you to get n unique elements by an incremented index (assuming that the list itself contains unique elements).


    In case you're wondering if there's a Java 8 Stream approach; no, there isn't a built-in one. There's no such thing as Comparator#randomOrder() in standard API (yet?). You could try something like below while still satisfying the strict Comparator contract (although the distribution is pretty terrible):

    List<Foo> list = createItSomehow();
    int random = new Random().nextInt();
    Foo foo = list.stream().sorted(Comparator.comparingInt(o -> System.identityHashCode(o) ^ random)).findFirst().get();
    

    Better use Collections#shuffle() instead.

    0 讨论(0)
  • 2020-11-27 16:33

    Simple and clear

       // define ArrayList to hold Integer objects
        ArrayList<Integer> arrayList = new ArrayList<>();
    
        for (int i = 0; i < maxRange; i++) {
            arrayList.add(i + 1);
        }
    
        // shuffle list
        Collections.shuffle(arrayList);
    
        // adding defined amount of numbers to target list
        ArrayList<Integer> targetList = new ArrayList<>();
        for (int j = 0; j < amount; j++) {
            targetList.add(arrayList.get(j)); 
        }
    
        return targetList;
    
    0 讨论(0)
  • 2020-11-27 16:34

    The following method returns a new List of Min(n, list.size()) random elements taken from the paramenter List list. Have in mind that the List list is being modified after each call. Therefore, each call will be "consuming" the original list returning n random elements from it:

    public static <T> List<T> nextRandomN(List<T> list, int n) {
      return Stream
        .generate(() -> list.remove((int) (list.size() * Math.random())))
        .limit(Math.min(list.size(), n))
        .collect(Collectors.toList());
    }
    

    Sample usage:

    List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    
    System.out.println(nextRandomN(list, 3).toString());
    System.out.println(nextRandomN(list, 3).toString());
    System.out.println(nextRandomN(list, 3).toString());
    System.out.println(nextRandomN(list, 3).toString());
    

    Sample output:

    [8, 2, 3]
    [4, 10, 7]
    [1, 5, 9]
    [6]
    
    0 讨论(0)
  • 2020-11-27 16:39

    A fair way to do this is to go through the list, on the nth iteration calculating the probability of whether or not to pick the nth element, which is essentially the fraction of the number of items you still need to pick over the number of elements available in the rest of the list. For example:

    public static <T> T[] pickSample(T[] population, int nSamplesNeeded, Random r) {
      T[] ret = (T[]) Array.newInstance(population.getClass().getComponentType(),
                                        nSamplesNeeded);
      int nPicked = 0, i = 0, nLeft = population.length;
      while (nSamplesNeeded > 0) {
        int rand = r.nextInt(nLeft);
        if (rand < nSamplesNeeded) {
          ret[nPicked++] = population[i];
          nSamplesNeeded--;
        }
        nLeft--;
        i++;
      }
      return ret;
    }
    

    (This code copied from a page I wrote a while ago on picking a random sample from a list.)

    0 讨论(0)
  • 2020-11-27 16:40

    Use the following class:

    import java.util.Enumeration;
    import java.util.Random;
    
    public class RandomPermuteIterator implements Enumeration<Long> {
        int c = 1013904223, a = 1664525;
        long seed, N, m, next;
        boolean hasNext = true;
    
        public RandomPermuteIterator(long N) throws Exception {
            if (N <= 0 || N > Math.pow(2, 62)) throw new Exception("Unsupported size: " + N);
            this.N = N;
            m = (long) Math.pow(2, Math.ceil(Math.log(N) / Math.log(2)));
            next = seed = new Random().nextInt((int) Math.min(N, Integer.MAX_VALUE));
        }
    
        public static void main(String[] args) throws Exception {
            RandomPermuteIterator r = new RandomPermuteIterator(100);
            while (r.hasMoreElements()) System.out.print(r.nextElement() + " ");
        }
    
        @Override
        public boolean hasMoreElements() {
            return hasNext;
        }
    
        @Override
        public Long nextElement() {
            next = (a * next + c) % m;
            while (next >= N) next = (a * next + c) % m;
            if (next == seed) hasNext = false;
            return  next;
        }
    }
    
    0 讨论(0)
  • 2020-11-27 16:41

    This solution doesn't modify the original list or otherwise scale in complexity with the list size.

    To get a sample of 4 from a list of 7, we just select a random element out of all 7, then select a random element out of the remaining 6, and so on. If we've already selected indices 4, 0, 3, next we generate a random number out of 0, 1, 2, 3, respectively representing index 1, 2, 5, 6.

    static Random rand = new Random();
    
    static <T> List<T> randomSample(List<T> list, int size) {
        List<T> sample = new ArrayList<>();
    
        for (int sortedSampleIndices[] = new int[size], i = 0; i < size; i++) {
            int index = rand.nextInt(list.size() - i);
    
            int j = 0;
            for (; j < i && index >= sortedSampleIndices[j]; j++)
                index++;
            sample.add(list.get(index));
    
            for (; j <= i; j++) {
                int temp = sortedSampleIndices[j];
                sortedSampleIndices[j] = index;
                index = temp;
            }
        }
    
        return sample;
    }
    
    0 讨论(0)
提交回复
热议问题