【问题标题】:How to avoid java.lang.OutOfMemoryError Exception when calculating all possible combinations in Scala?在Scala中计算所有可能的组合时如何避免java.lang.OutOfMemoryError异常?
【发布时间】:2016-04-21 16:52:41
【问题描述】:

我知道有很多关于java.lang.OutOfMemoryError: GC overhead limit exceeded(例如How to avoid java.lang.OutOfMemoryError?)的相关问题,我知道我的问题是由我的错误实施引起的,但我不知道如何解决它。

所以,我的目标是从列表中获取所有可能的重复组合。我通过使用 question 的 Mark Lister 在 answer 中提出的代码完成了这项任务。它工作得很好,但显然通过使用具有 52 个元素的列表来获得 10 范围内的所有组合对我的计算机来说太多了。有什么想法可以解决这个问题吗?

可能比仅仅收集大量 Lists 组合更好,我可以在生成组合时处理每个 word

这是我的代码:

object MyApp extends App {

  val letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toList

  run(10)

  def mycomb[T](n: Int, l: List[T]): List[List[T]] =
    n match {
      case 0 => List(List())
      case _ => for (el <- l;
                     sl <- mycomb(n - 1, l dropWhile {
                       _ != el
                     }))
        yield el :: sl
    }

  def comb[T](n: Int, l: List[T]): List[List[T]] = mycomb(n, l.distinct)

  def run(n: Int): Unit = {

    for (i <- 1 to n) {
      val combo = comb(i, letters)

      combo.par.foreach { word =>
        println(word.mkString(""))
        // Process each word one by one in parallel
      }
    }
  }

}

【问题讨论】:

  • 你最好 a) 使用不会产生重复的策略,b) 当你得到它们时输出每个结果。这样你就不会耗尽内存。
  • @PeterLawrey 没错,我就是这么想的!我这样做是因为我最初认为我可以使用.par 的并发方法。
  • 使用 Stream 或 Iterator 代替 List [def combination(n: Int): Iterator[Seq[A]]](scala-lang.org/api/current/#scala.collection.Seq)
  • @AndrzejJozwik 感谢您的评论。不幸的是,我是 Scala 初学者。能否提供一个代码示例?

标签: java performance scala


【解决方案1】:

我找到了一些时间和我对所有可能组合的解决方案,而无需重复:

  val digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

  def concat[T](it1: Iterator[T], it2: => Iterator[T]): Iterator[T] = new Iterator[T] {
    def hasNext: Boolean = it1.hasNext || it2.hasNext

    def next(): T = if (it1.hasNext) {
      it1.next()
    } else {
      it2.next()
    }
  }

  def combinationWithoutRepetitions[T](k: Int, seq: Seq[T]): Iterator[Seq[T]] = {
    def combination(k: Int, acc: Seq[T], tail: Seq[T]): Iterator[Seq[T]] = k match {
      case 0 =>
        Iterator.empty
      case 1 =>
        tail.iterator.map { t => t +: acc }
      case _ if tail.nonEmpty =>
        val it1 = combination(k - 1, tail.head +: acc, tail.tail)
        lazy val it2 = combination(k, Seq.empty, tail.tail)
        concat(it1, it2)
      case _ =>
        Iterator.empty
    }

    combination(k, Seq.empty, seq)
  }

  //TEST
  val iterator = combinationWithoutRepetitions(10, digits.toSeq) // it should return immediatelly
  println(s"${iterator.next()}")
  println(s"${iterator.next()}")

  combinationWithoutRepetitions(2, digits.toSeq).foreach{
         el => println(s"$el")
  }

我使用延迟加载。函数 concat 从两个迭代器创建迭代器 - 第一个迭代第一个,当这个迭代器为空时 - 开始第二个迭代。

更新

与重复的组合(基于之前的功能):

  def combinationWithRepetitions[T](k: Int, sequence: Seq[T]): Iterator[Seq[T]] = new Iterator[Seq[T]] {
    val without = combinationWithoutRepetitions(k, sequence)
    var combination: Iterator[Seq[T]] = Iterator.empty

    def hasNext: Boolean = {
      combination.hasNext || without.hasNext
    }

    def next(): Seq[T] = {
      if (combination.isEmpty) {
        combination = without.next().permutations
      }
      combination.next()
    }
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 2015-12-11
    相关资源
    最近更新 更多