关于scala:随机骰子卷不是随机的

Random dice rolls aren't random

简单的新手问题,奇怪的是,我还没能想出一个解决办法。

我制作了一个简单的骰子滚动模拟器,这样我就可以比较不可传递的骰子和普通的骰子,但是问题是如果我制作两个面数和面值相同的骰子,两个骰子每次都会滚动相同的值。(也就是说,每个骰子产生不同的数字,但两个骰子的值相同)

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Die(values: Int*) {
  private val rand: util.Random = new util.Random(compat.Platform.currentTime)
  private val high = values.size + 1
  private val low  = values(0)
  def roll(): Int  = rand.nextInt(high - low) + low
  def this(vals: Range) = this(vals: _*)

  def rollAndCompareTo(that: Die): Symbol = {
    val a = this.roll()
    val b = that.roll()
    if(a > b) 'GT
    else if (a < b) 'LT
    else 'EQ
  }
}

object Program extends App {
  val d61 = new Die(1 to 6)
  val d62 = new Die(1 to 6)

  for(_ <- 1 to 100)
    println(d61 rollAndCompareTo d62)
}

100%的时候,程序只打印'EQ,因为这两个骰子,尽管在不同的时间创建了不同的实例,但总是滚动相同的值。

我还试图增加一个延迟,这样种子的差异就更大了,但这也没有帮助。

我该怎么做才能修好这个?


尝试为您的随机实例删除种子,或使用

1
new util.Random(System.currentTimeMillis)

如果你在短时间内打了很多电话,那么你种子的特异性是很重要的。


正如其他评论中所说,您可以在伴生对象中使用一个单独的rand,那么您就不必担心种子的分辨率。考虑使用SecureRandom,否则使用它来为单个正则Util.Random种子,以避免SecureRandom的开销或可能的阻塞行为

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
object Die {
  // consider using java.security.SecureRandom or using that to seed a util.Random
  private[Die] val rand: util.Random = new util.Random(compat.Platform.currentTime)
}

class Die(values: Int*) {
  private val high = values.size + 1
  private val low = values(0)
  def roll(): Int = Die.rand.nextInt(high - low) + low
  def this(vals: Range) = this(vals: _*)

  def rollAndCompareTo(die: Die): Symbol = {
    val a = this.roll()
    val b = die.roll()
    if (a > b) 'GT
    else if (a < b) 'LT
    else 'EQ
  }
}

object Program extends App {
  val d61 = new Die(1 to 6)
  val d62 = new Die(1 to 6)

  for (_ <- 1 to 100)
    println(d61 rollAndCompareTo d62)
}


java.security.SecureRandom为例,它比线性同余生成器中的java.util.Random更加难以预测。

有关更详细的讨论说明,请参阅https://stackoverflow.com/a/11052736/3189923和http://docs.oracle.com/javase/8/docs/api/java/security/securelrandom.html。


你可以简单地选择不同的固定种子。此外,compat.platform.currenttime()和system.currenttimemillis()似乎工作正常(2014 MacBook Pro)。如果两个对象在同一毫秒内被实例化,您可以尝试System.NanoTime()。但实际上,固定种子更好,例如用于测试。

此外,这看起来完全像Java——具有可变的状态、副作用等。如果你正在考虑学习斯卡拉的方式,鼓励它被它的创造者使用(尽可能的功能),查看Paul Chiusano和R.N.BJARNASN的书,叫做"Scala中的函数编程"(曼宁出版社,早期访问,HTTP:/MANYN.COM/BJARNASON))。他们有一整章的纯功能状态使用的例子随机数生成器。或者,查看nicta的示例:https://github.com/nicta/rng