📜  dart shuffle list - Dart (1)

📅  最后修改于: 2023-12-03 15:14:36.171000             🧑  作者: Mango

Dart Shuffle List

Introduction

In Dart, shuffling a list means randomly re-arranging the order of its elements. This is a common operation in many programming scenarios, such as implementing randomized algorithms, generating test data, or creating user interfaces. In this article, we will explore various ways to shuffle a list in Dart, and discuss their pros and cons.

Naive Approach

The simplest way to shuffle a list is to use the shuffle() method provided by the dart:math library. This method re-arranges the elements in the list in a random order, using the current time as the random seed. Here is an example:

import 'dart:math';

void main() {
  var list = [1, 2, 3, 4, 5];
  list.shuffle();
  print(list); // prints [3, 5, 1, 4, 2], for example
}

However, this approach has some drawbacks. First, it is not guaranteed to produce truly random shuffles if the list has repetitive elements, since shuffle() only considers the order of elements, not their values. Second, it is not easily customizable, as it always uses the current time as the random seed. Third, it modifies the original list in place, which may not be desirable in some cases.

Fisher-Yates Algorithm

A better approach to shuffling a list is to use the Fisher-Yates algorithm, also known as the Knuth shuffle. This algorithm works by traversing the list from the last to the first element, and swapping each element with a randomly chosen element from the entire list up to its current position. Here is an implementation of the Fisher-Yates algorithm in Dart:

import 'dart:math';

void fisherYatesShuffle(List list) {
  var random = Random();
  for (var i = list.length - 1; i > 0; i--) {
    var j = random.nextInt(i + 1);
    var temp = list[i];
    list[i] = list[j];
    list[j] = temp;
  }
}

void main() {
  var list = [1, 2, 3, 4, 5];
  fisherYatesShuffle(list);
  print(list); // prints a random permutation of [1, 2, 3, 4, 5]
}

This implementation has several advantages over the naive approach. First, it produces truly random shuffles even for repetitive elements, since it considers the entire range of elements at each step. Second, it allows for customizing the random number generator used by passing it as an argument. Third, it does not modify the original list in place unless explicitly requested.

Reservoir Sampling

Another variant of shuffling a list is called reservoir sampling. This algorithm works by selecting a sample of fixed size from the list, where each element has an equal probability of being chosen. The sample is then re-arranged in a random order, and the remaining elements are iteratively inserted into the sample with a decreasing probability of replacing an existing element. Here is an implementation of reservoir sampling in Dart:

import 'dart:math';

List reservoirSamplingShuffle(List list, int sampleSize) {
  var sample = List.filled(sampleSize, null);
  var random = Random();
  for (var i = 0; i < sampleSize; i++) {
    sample[i] = list[i];
  }
  for (var i = sampleSize; i < list.length; i++) {
    var j = random.nextInt(i + 1);
    if (j < sampleSize) {
      sample[j] = list[i];
    }
  }
  fisherYatesShuffle(sample); // using the Fisher-Yates algorithm
  return sample + list.sublist(sampleSize);
}

void main() {
  var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  var shuffled = reservoirSamplingShuffle(list, 5);
  print(shuffled); // prints a random sample of 5 elements from list
}

This implementation has some interesting properties. First, it ensures that each element in the list has an equal probability of being selected, regardless of its position or frequency. Second, it allows for controlling the sample size, and is memory-efficient since it only stores the selected elements. Third, it can be combined with other shuffling algorithms, such as the Fisher-Yates algorithm, to achieve deeper randomness. However, it is more complex to implement and may not be suitable for small lists or fixed sample sizes.