1

我正在尝试从Scala 中的 25 行数独求解器并行化数独求解器的递归调用。我把他们Fold改成了reduce

def reduce(f: (Int, Int) => Int, accu: Int, l: Int, u: Int): Int = {
  accu + (l until u).toArray.reduce(f(accu, _) + f(accu, _))
}

如果按顺序运行可以正常工作,但是当我将其更改为

  accu + (l until u).toArray.par.reduce(f(accu, _) + f(accu, _))

递归更频繁地到达底部并产生错误的解决方案。我认为,它将执行底层递归并向上工作,但似乎没有这样做。
我也试过期货

def parForFut2(f: (Int, Int) => Int, accu: Int, l: Int, u: Int): Int = {
  var sum: Int = accu
  val vals = l until u
  vals.foreach(t => scala.actors.Futures.future(sum + f(accu, t)))
  sum
}

这似乎与par.reduce. 我会很感激任何评论。整个代码在这里:

object SudokuSolver extends App {
// The board is represented by an array of string 
val source = scala.io.Source.fromFile("./puzzle")
val lines = (source.getLines).toArray
var m: Array[Array[Char]] = for (
  str <- lines;
  line: Array[Char] = str.toArray
) yield line
source.close()

// For printing m
def print = {
  Console.println("");
  refArrayOps(m) map (carr => Console.println(new String(carr)))
}

// The test for validity of n on position x,y
def invalid(i: Int, x: Int, y: Int, n: Char): Boolean =
  i < 9 && (m(y)(i) == n || m(i)(x) == n ||
  m(y / 3 * 3 + i / 3)(x / 3 * 3 + i % 3) == n || invalid(i + 1, x, y, n))

// Looping over a half-closed range of consecutive Integers [l..u) 
// is factored out Into a higher-order function
def parReduce(f: (Int, Int) => Int, accu: Int, l: Int, u: Int): Int = {
  accu + (l until u).toArray.par.reduce(f(accu, _) + f(accu, _))
}

// The search function examines each position on the board in turn, 
// trying the numbers 1..9 in each unfilled position 
// The function is itself a higher-order fold, accumulating the value 
// accu by applying the given function f to it whenever a solution m 
// is found 
def search(x: Int, y: Int, f: (Int) => Int, accu: Int): Int = Pair(x, y) match {
  case Pair(9, y) => search(0, y + 1, f, accu) // next row 
  case Pair(0, 9) => f(accu) // found a solution - print it and continue
  case Pair(x, y) => if (m(y)(x) != '0') search(x + 1, y, f, accu) else
    parForFut1((accu: Int, n: Int) =>
      if (invalid(0, x, y, (n + 48).asInstanceOf[Char])) accu else {
        m(y)(x) = (n + 48).asInstanceOf[Char];
        val newaccu = search(x + 1, y, f, accu);
        m(y)(x) = '0';
        newaccu
      }, accu, 1, 10)
}

// The main part of the program uses the search function to accumulate 
// the total number of solutions 
Console.println("\n" + search(0, 0, i => { print; i + 1 }, 0) + " solution(s)")
} 
4

2 回答 2

1

据我所知,递归本质上是顺序的(即不可并行化)。

我会这样推理:递归意味着(简单地说)“打电话给自己”。函数调用总是发生在一个且恰好是一个线程中。

如果您告诉该函数调用自己,那么您将停留在该线程中 - 您没有划分工作(即使其并行)。

递归和并行性虽然是相关的,但不是在函数调用级别。它们是相关的,因为任务可以递归地分解成更小的部分,可以并行执行。

于 2013-05-27T07:10:34.040 回答
0

在 Andreas 发表评论后,我将其更改m: Array[Array[Char]]m: List[List[Char]]which 以防止对其进行任何不必要和不需要的更改。最终的循环方法是

    def reduc(f: (Int, Int) => Int, 
                  accu: Int, l: Int, u: Int, m1: List[List[Char]]):Int =
    accu + (l until u).toArray.par.reduce(f(accu, _) + f(accu, _))

而且我必须将 m 作为参数传递给每个使用过的函数,因此每个函数都有自己的实例。整个代码:

    object SudokuSolver extends App{
      // The board is represented by an Array of strings (Arrays of Chars),
      val source = scala.io.Source.fromFile("./puzzle")

      val lines = source.getLines.toList             
      val m: List[List[Char]] = for (
        str <- lines;
        line: List[Char] = str.toList
      ) yield line 
      source.close()

      // For prInting m
      def printSud(m: List[List[Char]]) = {
        Console.println("")
        m map (println)
      }                                               

      Console.println("\nINPUT:")                     
      printSud(m)  

      def invalid(i:Int, x:Int, y:Int, n:Char,m1: List[List[Char]]): Boolean =
        i < 9 && (m1(y)(i) == n || m1(i)(x) == n ||
          m1(y / 3 * 3 + i / 3)(x / 3 * 3 + i % 3) == n ||
          invalid(i + 1, x, y, n, m1))

      def reduc(f: (Int, Int) => Int, accu: Int, l: Int, u: Int, 
                m1: List[List[Char]]): Int =
        accu + (l until u).toArray.par.reduce(f(accu, _) + f(accu, _))

      def search(x: Int, y: Int, accu: Int, m1: List[List[Char]]): Int = 
        Pair(x, y) match {
          case Pair(9, y) => search(0, y + 1, accu, m1) // next row
          case Pair(0, 9) => { printSud(m1); accu + 1 } // found a solution
          case Pair(x, y) =>
            if (m1(y)(x) != '0')
              search(x + 1, y, accu, m1) // place is filled, we skip it.
            else // box is not filled, we try all n in {1,...,9}
              reduc((accu: Int, n: Int) => {
                if (invalid(0, x, y, (n + 48).asInstanceOf[Char], m1))
                  accu
                else { // n fits here
                  val line = List(m1(y).patch(x, Seq((n + 48).asInstanceOf[Char]), 1))
                  val m2 = m1.patch(y, line, 1)
                  val newaccu = search(x + 1, y, accu, m2);
                  val m3 = m1.patch(y, m1(y).patch(x, Seq(0), 1), 1)
                  newaccu
                }
            }, accu, 1, 10, m1)
      }                                               

      Console.println("\n" + search(0, 0, 0, m) + " solution(s)")

    }
于 2013-06-19T06:36:48.450 回答