Я наблюдал следующее неожиданное поведение при использовании ScalaCheck Gen.pic, что (для меня) указывает на то, что его выбор не совсем случайный, хотя его документация говорит так:
/** A generator that picks a given number of elements from a list, randomly */
Я запустил следующие три маленькие программы по порядку (в течение 2 дней, в разное время, если это может иметь значение) после установки
implicit override val generatorDrivenConfig = PropertyCheckConfig(
maxSize = 1000,
minSize = 1000,
minSuccessful = 1000)
чтобы получить приличный размер выборки.
Программа №1
val set = Set(1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50)
// Thanks to @Jubobs for the solution
// See: http://stackoverflow.com/a/43825913/4169924
val g = Gen.pick(3, set).map { _.toList }
forAll (g) { s => println(s) }
Из 3000 чисел, сгенерированных в 2 разных прогонах, я получил удивительно похожее и довольно неслучайное распределение (числа округлены, перечислены только первые 5, как и во всем списке здесь и далее):
- Число: частота в прогоне №1, частота в прогоне №2.
- 15: 33%, 33%
- 47: 22%, 22%
- 4: 15 %, 16 %
- 19: 10%, 10%
- 30: 6%, 6%
(Отказ от ответственности: я не смог найти здесь, как создать таблицу, кроме вот так)
Программа 2
val list: List[Int] = List.range(1, 50)
val g = Gen.pick(3, list)
forAll (g) { s => println(s) }
В случае использования List
числа "застревают" в конце диапазона (3x1000 чисел в случае обоих прогонов):
- 49: 33%, 33%
- 48: 22%, 22%
- 47: 14%, 14%
- 46: 10 %, 10 %
- 45: 6%, 6%
Интересно, что частоты почти такие же, как и в случае программы 1.
Примечание: я повторил прогоны для списков до 10 раз и получил точно такое же распределение с различиями +/- 1%, просто не хотел перечислять все числа здесь, в этой странной "таблице". "формат.
Программа 3
Просто чтобы немного оживить ситуацию, я запустил третий небольшой фрагмент, создав Set
(Программа 1) из List
(Программа 2):
val set: Set[Int] = List.range(1, 50).toSet
val g = Gen.pick(3, set).map { _.toList }
forAll (g) { s => println(s) }
Теперь числа те же, что и для Программы 2 (List
побед!), хотя частоты (опять же, для 3*1000 номеров за 2 прогона) в конце немного изменились:
- 49: 33%, 33%
- 48: 23%, 22%
- 47: 16 %, 15 %
- 46: 9 %, 10 %
- 45: 7 %, 6 %
Вопрос
Несмотря на то, что размер выборки недостаточен (как никогда не бывает достаточно), чтобы определить истинную случайность, я не могу не поставить под сомнение заявленную случайность Gen.pick
(насколько это возможно в готовом виде). , мне может понадобиться установить начальное число, чтобы оно работало «больше» случайным образом), поскольку числа «застряли», а частоты почти одинаковы.
После просмотра исходного кода Gen.pick
< /a>, в строке #672 используется некий seed0
:
def pick[T](n: Int, l: Iterable[T]): Gen[Seq[T]] = {
if (n > l.size || n < 0) throw new IllegalArgumentException(s"invalid choice: $n")
else if (n == 0) Gen.const(Nil)
else gen { (p, seed0) =>
// ...
который я не могу найти нигде (в Исходный код Gen.scala или в scala.util.Random), но у меня есть подозрение, что это может иметь какое-то отношение к наблюдаемому поведению. Это ожидаемое поведение Gen.pick
? Если да, то как я могу получить «больше» случайного выбора?