Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 使用ApacheSpark RDD进行递归计算可能吗?_Scala_Apache Spark_Recursion_Rdd_Chess - Fatal编程技术网

Scala 使用ApacheSpark RDD进行递归计算可能吗?

Scala 使用ApacheSpark RDD进行递归计算可能吗?,scala,apache-spark,recursion,rdd,chess,Scala,Apache Spark,Recursion,Rdd,Chess,我正在使用Scala和ApacheSpark开发国际象棋引擎(我需要强调的是,我的理智不是这个问题的主题)。我的问题是,Negamax算法本质上是递归的,当我尝试朴素的方法时: class NegaMaxSparc(@transient val sc: SparkContext) extends Serializable { val movesOrdering = new Ordering[Tuple2[Move, Double]]() { override def compare

我正在使用Scala和ApacheSpark开发国际象棋引擎(我需要强调的是,我的理智不是这个问题的主题)。我的问题是,Negamax算法本质上是递归的,当我尝试朴素的方法时:

class NegaMaxSparc(@transient val sc: SparkContext) extends Serializable  {
  val movesOrdering = new Ordering[Tuple2[Move, Double]]() {
    override def compare(x: (Move, Double), y: (Move, Double)): Int =
      Ordering[Double].compare(x._2, y._2)
  }

  def negaMaxSparkHelper(game: Game, color: PieceColor, depth: Int, previousMovesPar: RDD[Move]): (Move, Double) = {
    val board = game.board

    if (depth == 0) {
      (null, NegaMax.evaluateDefault(game, color))
    } else {
      val moves = board.possibleMovesForColor(color)
      val movesPar = previousMovesPar.context.parallelize(moves)

      val moveMappingFunc = (m: Move) => { negaMaxSparkHelper(new Game(board.boardByMakingMove(m), color.oppositeColor, null), color.oppositeColor, depth - 1, movesPar) }
      val movesWithScorePar = movesPar.map(moveMappingFunc)
      val move = movesWithScorePar.min()(movesOrdering)

      (move._1, -move._2)
    }
  }

  def negaMaxSpark(game: Game, color: PieceColor, depth: Int): (Move, Double) = {
    if (depth == 0) {
      (null, NegaMax.evaluateDefault(game, color))
    } else {
      val movesPar = sc.parallelize(new Array[Move](0))

      negaMaxSparkHelper(game, color, depth, movesPar)
    }
  }
}

class NegaMaxSparkBot(val maxDepth: Int, sc: SparkContext) extends Bot {
  def nextMove(game: Game): Move = {
    val nms = new NegaMaxSparc(sc)
    nms.negaMaxSpark(game, game.colorToMove, maxDepth)._1
  }
}
我得到:

org.apache.spark.SparkException: RDD transformations and actions can only be invoked by the driver, not inside of other transformations; for example, rdd1.map(x => rdd2.values.count() * x) is invalid because the values transformation and count action cannot be performed inside of the rdd1.map transformation. For more information, see SPARK-5063.

问题是:这个算法可以使用Spark递归实现吗?如果没有,那么解决这个问题的正确方法是什么?

这是一个在实现方面有意义的限制,但它可能是一个痛苦的工作

您可以尝试将递归拉到顶层,就在创建RDD并使用RDD操作的“驱动程序”代码中?比如:

def step(rdd: Rdd[Move], limit: Int) =
  if(0 == limit) rdd
  else {
    val newRdd = rdd.flatMap(...)
    step(newRdd, limit - 1)
  }

或者,通过手动显式管理“堆栈”,总是可以将递归转换为迭代(尽管这可能会导致更麻烦的代码)。

只有驱动程序可以在RDD上启动计算。原因是,尽管RDD“感觉”像常规的数据集合,但在幕后它们仍然是分布式集合,因此对它们启动操作需要协调所有远程从机上的任务执行,而spark在大多数情况下对我们隐藏着这些任务

因此,从从属服务器递归,即直接从从属服务器动态启动新的分布式任务是不可能的:只有驱动器可以处理这种协调

这里有一个简化问题的可能替代方案(如果我理解正确的话)。其思想是连续构建
移动
的实例,每个实例代表从初始状态开始的
移动
的完整序列

移动
的每个实例都能够将自身转换为一组
移动
,每个移动对应于相同的
移动序列
加上一个可能的下一个
移动

从那里,驱动程序只需依次将
移动
映射到我们想要的深度,得到的RDD[Moves]将为我们并行执行所有操作

该方法的缺点是所有深度级别保持同步,即我们必须在进入下一个级别之前计算
n
级别的所有移动(即
RDD[moves]

下面的代码没有经过测试,可能有缺陷,甚至没有编译,但希望它能提供一个解决问题的方法

/* one modification to the board */
case class Move(from: String, to: String)

case class PieceColor(color: String)

/* state of the game */ 
case class Board {

    // TODO
    def possibleMovesForColor(color: PieceColor): Seq[Move] = 
        Move("here", "there") :: Move("there", "over there") :: Move("there", "here") :: Nil

    // TODO: compute a new instance of board here, based on current + this move
    def update(move: Move): Board = new Board
}


/** Solution, i.e. a sequence of moves*/ 
case class Moves(moves: Seq[Move], game: Board, color: PieceColor) {    
    lazy val score = NegaMax.evaluateDefault(game, color)

    /** @return all valid next Moves  */
    def nextPossibleMoves: Seq[Moves] = 
        board.possibleMovesForColor(color).map { 
            nextMove => 
              play.copy(moves = nextMove :: play.moves, 
                        game = play.game.update(nextMove)
        } 

}

/** Driver code: negaMax: looks for the best next move from a give game state */
def negaMax(sc: SparkContext, game: Board, color: PieceColor, maxDepth: Int):Moves = {

    val initialSolution = Moves(Seq[moves].empty, game, color)

    val allPlays: rdd[Moves] = 
        (1 to maxDepth).foldLeft (sc.parallelize(Seq(initialSolution))) {
        rdd => rdd.flatMap(_.nextPossibleMoves)
    }

    allPlays.reduce { case (m1, m2) => if (m1.score < m2.score) m1 else m2}

}
/*对电路板进行一次修改*/
案例类移动(从:字符串,到:字符串)
案例类PieceColor(颜色:字符串)
/*游戏状态*/
案件类别委员会{
//待办事项
def-possibleMovesForColor(颜色:PieceColor):Seq[Move]=
移动(“这里”,“那里”)::移动(“那里”,“那边”)::移动(“那里”,“这里”)::无
//TODO:根据当前+此移动,在此计算一个新的board实例
def更新(移动:移动):板=新板
}
/**解决方案,即一系列移动*/
案例类移动(移动:Seq[Move],游戏:Board,颜色:PieceColor){
懒惰val分数=NegaMax.EvaluatedDefault(游戏,颜色)
/**@返回所有有效的下一步动作*/
def nextPossibleMoves:Seq[移动]=
board.possibleMovesForColor(color).map{
下一个移动=>
play.copy(moves=nextMove::play.moves,
game=play.game.update(nextMove)
} 
}
/**驱动程序代码:negaMax:在给定游戏状态下寻找最佳下一步*/
def negaMax(sc:SparkContext,游戏:棋盘,颜色:PieceColor,maxDepth:Int):移动={
val initialSolution=移动(顺序[移动]。空,游戏,颜色)
val allPlays:rdd[移动]=
(1到maxDepth).foldLeft(sc.parallelize(Seq(initialSolution))){
rdd=>rdd.flatMap(uu.nextPossibleMoves)
}
allPlays.reduce{case(m1,m2)=>if(m1.score