scala - 递归发生时的Scala StackOverflows
问题描述
嗨,我有一个 BronKerbosch 算法,用于检测使用 scala 实现的社区。它有一个包含超过 100000 个节点的数据集。当我运行代码时,它给出了一个错误提示 stackoverflows。我也添加了@tailrec 注释。下面我添加了代码以及错误
import org.apache.spark.SparkContext
import org.apache.spark.graphx.Graph.graphToGraphOps
import org.slf4j.{Logger, LoggerFactory}
import scala.annotation.tailrec
import scala.collection.mutable.{ArrayBuffer, Set => MutableSet}
import scala.reflect.ClassTag
class BronKerbosch[VD: ClassTag, ED: ClassTag](sc: SparkContext,
inputGraph: Graph[VD, ED]) {
private val logger: Logger =
LoggerFactory.getLogger(classOf[BronKerbosch[VD, ED]]);
private val sparkContext: SparkContext = sc;
private var graph: Graph[VD, ED] = inputGraph;
private var neighbourVerticesMap =
graph.collectNeighborIds(EdgeDirection.Either)
.collect().map(vertex => (vertex._1.asInstanceOf[Long], vertex._2))
.toMap;
def runAlgorithm = {
logger.info("Starting BronKerbosch Algorithm");
var potentialClique = Array[Long]()
var candidates = graph.vertices.map(vertex =>
vertex._1.asInstanceOf[Long]).collect().to[ArrayBuffer];
var alreadyFound = ArrayBuffer[Long]();
var cliques = ArrayBuffer[Array[Long]]()
findCliques(potentialClique, candidates, alreadyFound, cliques);
cliques;
}
private def findCliques(potentialClique: Array[Long],candidates: ArrayBuffer[Long], alreadyFound: ArrayBuffer[Long],cliques: ArrayBuffer[Array[Long]]): Unit = {
if (candidates.isEmpty && alreadyFound.isEmpty) {
cliques.append(potentialClique)
}
@tailrec
var originalCandidates = candidates
candidates.foreach { candidateVertex =>
{
var neighbourVertices = neighbourVerticesMap.getOrElse(candidateVertex, Array[Long]())
findCliques((potentialClique :+ candidateVertex).distinct,candidates.intersect(neighbourVertices),alreadyFound.intersect(neighbourVertices), cliques)
}
}
}
}
下面是错误
Exception in thread "main" java.lang.StackOverflowError
at
scala.collection.mutable.HashTable$class.findEntry(HashTable.scala:132)
at scala.collection.mutable.HashMap.findEntry(HashMap.scala:40)
at scala.collection.mutable.HashMap.apply(HashMap.scala:64)
at scala.collection.SeqLike$$anonfun$occCounts$1.apply(SeqLike.scala:481)
at scala.collection.SeqLike$$anonfun$occCounts$1.apply(SeqLike.scala:481)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:35)
at scala.collection.SeqLike$class.occCounts(SeqLike.scala:481)
at scala.collection.SeqLike$class.intersect(SeqLike.scala:469)
at scala.collection.AbstractSeq.intersect(Seq.scala:41)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply$mcVJ$sp(BronKerbosch.scala:57)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply(BronKerbosch.scala:53)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply(BronKerbosch.scala:53)
at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
at com.creative.graphx.BronKerbosch.com$creative$graphx$BronKerbosch$$findCliques(BronKerbosch.scala:53)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply$mcVJ$sp(BronKerbosch.scala:57)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply(BronKerbosch.scala:53)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply(BronKerbosch.scala:53)
at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
at com.creative.graphx.BronKerbosch.com$creative$graphx$BronKerbosch$$findCliques(BronKerbosch.scala:53)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply$mcVJ$sp(BronKerbosch.scala:57)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply(BronKerbosch.scala:53)
at com.creative.graphx.BronKerbosch$$anonfun$com$creative$graphx$BronKerbosch$$findCliques$1.apply(BronKerbosch.scala:53)
at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
at com.creative.graphx.BronKerbosch.com$creative$graphx$BronKerbosch$$findCliques(BronKerbosch.scala:53)
解决这个问题的最佳方法是什么?
解决方案
这段代码有很多问题,但肯定不是尾递归的。
尾递归要求函数只执行一次递归调用,并且该调用必须是函数中的最后一个操作。(代码中可能有多个调用,但通过代码的任何给定路径只能调用一个)
在您的代码中,递归调用findCliques
位于foreach
循环内,因此可能会多次调用它,每个候选者调用一次。仅此一项就可能是堆栈溢出的原因。
您的@tailrec
注释不起作用,因为它需要在函数定义之前,而不是在函数中间。
代码的其他问题包括格式错误、不必要地使用var
、未使用的值 ( originalCandidates
) 以及过多的可变数据结构。
推荐阅读
- swift - JSONEncoder 不允许类型编码为原始值
- r - 如何添加 - 特别是 Dataframe R 中的列
- c++ - 提升链接错误 LNK2038:“boost_log_abi”“v2s_mt_nt6”与“v2_mt_nt6”不匹配
- javascript - 根据下拉选择在 PUG 模板中加载 HTML 表
- sql - 无论是否存在子记录都返回父记录的 SQL 查询
- mongodb - MongoDB:用点和作为对象查询嵌套字段 - 区别?
- python - Spotipy - 输入任何类型的用户名并询问登录信息
- javascript - dotenv 不适用于无服务器/webpack
- c# - 如何使用 model.id 动态生成 HTML 属性值?
- c# - 如何在选择子句中使用表达式