【问题标题】:Populating a list is Scala with random double taking forever填充列表是 Scala 随机双取永远
【发布时间】:2011-07-15 07:29:06
【问题描述】:

我是 Scala 新手,正在尝试获取随机双精度值列表:

问题是,当我尝试运行它时,与它的 Java 对应物相比,它花费的时间太长了。关于为什么会这样的任何想法或关于更有效方法的建议?

def random: Double = java.lang.Math.random()
var f = List(0.0)
for (i <- 1 to 200000)
 ( f = f ::: List(random*100)) 
 f = f.tail

【问题讨论】:

  • 通过以这种方式使用 :::,您将在每次迭代时重建整个列表。理想情况下,您实际上会使用惰性数据结构,但如果您真的想要一个列表,请使用 f = (random * 100) :: f
  • 另外,以var f: List[Double] = Nil 开头 - 无需定义随机,只需导入即可:import java.lang.Math.random
  • 顺便说一句,java.lang.Math.random() 并不是特别随机的(参见:http://alife.co.uk/nonrandom/)。一个不错的选择(还有其他)是http://www.cs.gmu.edu/~sean/research/mersenne/MersenneTwisterFast.java
  • 还有一个scala.util.Random,不用Java版的。
  • @Jesper:对。但仍应注意scala.util.Random 只是java.util.Random() 的包装。

标签: performance list scala


【解决方案1】:

你可以构造一个无限的随机双打流:

def randomList(): Stream[Double] = Stream.cons(math.random, randomList)

val f = randomList().take(200000)

这将利用惰性求值,因此在您真正需要之前不会计算值。不过,即使评估所有 200,000 个也很快。另外,f 不再需要是 var

【讨论】:

  • 感谢 Byrne 先生,您知道我为什么会死得这么惨吗?我认为这是因为 ::: 导致每次都生成一个新列表,因此是 CPU 上的杀手?
  • 是的,你是对的。每次迭代都在重建整个列表。
【解决方案2】:

如果你仍然使用可变状态,你应该使用一个可变集合,比如缓冲区,你也可以使用+= 添加它(这将是 real java 代码的对应物)。

但是你为什么不使用列表理解呢?

val f = for (_ <- 1 to 200000) yield (math.random * 100)

顺便说一句:var f = List(0.0) ... f = f.tail 在您的示例中可以替换为 var f: List[Double] = Nil。 (没有更多的性能,但更多的美丽;)

【讨论】:

  • 一个小调整:val f = for (_
  • 感谢 Martin,非常简洁明了的替代方案。
  • 嘿,谢谢 Martin,但我注意到了关于这个替代方案的一些事情 - 如果我试图将该列表传递给一个演员,它需要永远(超过 20 个双打的列表) - 但我的原始代码没有超过尺寸 20 的列表。
【解决方案3】:

另一种可能是:

val it = Iterator.continually(math.random)
it.take(200000).toList

Stream 也有一个 continually 方法,如果你愿意的话。

【讨论】:

  • 使用math.random,这就是我应该给出的答案:)
  • @Kevin:当然。改变了。我的借口是我通常使用Mersenne Twister,无论如何......
【解决方案4】:

你也可以这样实现:

List.fill(200000)(math.random)

同样适用于例如数组...

Array.fill(200000)(math.random)

等等……

【讨论】:

  • 这是 Scalarific 的做法,“Scalarific”是我创造的词,意思是“符合普遍接受的 Scala 习语”,与“Pythonic”并行——任何替代方案的提名欣然接受的话。
  • @Malvolio:“标量”怎么样? :)
  • @Malvolio:我提名“Scalacious”
【解决方案5】:

还有更多选择!尾递归:

def randlist(n: Int, part: List[Double] = Nil): List[Double] = {
  if (n<=0) part
  else randlist(n-1, 100*random :: part)
}

或映射范围:

(1 to 200000).map(_ => 100*random).toList

【讨论】:

    【解决方案6】:

    首先,它不会比 java 花费更长的时间,因为没有 java 对应物。 Java 没有不可变的列表。如果是这样,性能将大致相同。

    第二,它需要很多时间,因为附加列表具有线性性能,所以整个事情具有二次性能。

    而不是附加,前置,它具有恒定的性能。

    【讨论】:

      【解决方案7】:

      看起来您想使用 Vector 而不是 List。 List 有 O(1) 前缀,Vector 有 O(1) 附加。由于您正在追加,但使用连接,使用 Vector 会更快:

      def random: Double = java.lang.Math.random()
      var f: Vector[Double] = Vector()
      for (i <- 1 to 200000)
        f = f :+ (random*100)
      

      知道了吗?

      【讨论】:

      • 你的随机数倒退真的重要吗?
      • 谢谢。顺序无关紧要(在我的例子中),因为无论如何它们都会被传递给演员进行选择排序。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      相关资源
      最近更新 更多