- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如何使用 Scala actor 计算阶乘?
与实例相比,它会证明更省时吗
def factorial(n: Int): BigInt = (BigInt(1) to BigInt(n)).par.product
非常感谢。
最佳答案
您必须将输入拆分为部分产品。然后可以并行计算此部分产品。然后将部分乘积相乘得到最终乘积。
这可以归结为更广泛的问题类别:所谓的并行前缀计算。您可以在 Wikipedia 上阅读相关信息.
简化版:当你用关联运算_ * _
计算a*b*c*d
时,你可以构造计算a*(b *(c*d))
或 (a*b)*(c*d)
。使用第二种方法,您可以并行计算 a*b
和 c*d
,然后根据这些部分结果计算最终结果。当然,当您有更多的输入值时,您可以递归地执行此操作。
这听起来有点像家庭作业。所以我将提供一个具有两个属性的解决方案:
所以你可以看到解决方案应该如何构建,但没有人可以用它来作弊。
首先我需要一些导入
导入 akka.event.Logging 导入 java.util.concurrent.TimeUnit 导入 scala.concurrent.duration.FiniteDuration 导入 akka.actor._
然后我为参与者之间的通信创建了一些辅助类
case class Calculate[T](values : Seq[T], segment : Int, parallelLimit : Int, fn : (T,T) => T)
trait CalculateResponse
case class CalculationResult[T](result : T, index : Int) extends CalculateResponse
case object Busy extends CalculateResponse
除了告诉接收者您很忙之外,Actor 还可以使用存储或实现自己的队列来获取部分结果。但在这种情况下,我认为发送方应该决定允许多少并行计算。
现在我创建 Actor :
class ParallelPrefixActor[T] extends Actor {
val log = Logging(context.system, this)
val subCalculation = Props(classOf[ParallelPrefixActor[BigInt]])
val fanOut = 2
def receive = waitForCalculation
def waitForCalculation : Actor.Receive = {
case c : Calculate[T] =>
log.debug(s"Start calculation for ${c.values.length} values, segment nr. ${c.index}, from ${c.values.head} to ${c.values.last}")
if (c.values.length < c.parallelLimit) {
log.debug("Calculating result direct")
val result = c.values.reduceLeft(c.fn)
sender ! CalculationResult(result, c.index)
}else{
val groupSize: Int = Math.max(1, (c.values.length / fanOut) + Math.min(c.values.length % fanOut, 1))
log.debug(s"Splitting calculation for ${c.values.length} values up to ${fanOut} children, ${groupSize} elements each, limit ${c.parallelLimit}")
def segments=c.values.grouped(groupSize)
log.debug("Starting children")
segments.zipWithIndex.foreach{case (values, index) =>
context.actorOf(subCalculation) ! c.copy(values = values, index = index)
}
val partialResults: Vector[T] = segments.map(_.head).to[Vector]
log.debug(s"Waiting for ${partialResults.length} results (${partialResults.indices})")
context.become(waitForResults(segments.length, partialResults, c, sender), discardOld = true)
}
}
def waitForResults(outstandingResults : Int, partialResults : Vector[T], originalRequest : Calculate[T], originalSender : ActorRef) : Actor.Receive = {
case c : Calculate[_] => sender ! Busy
case r : CalculationResult[T] =>
log.debug(s"Putting result ${r.result} on position ${r.index} in ${partialResults.length}")
val updatedResults = partialResults.updated(r.index, r.result)
log.debug("Killing sub-worker")
sender ! PoisonPill
if (outstandingResults==1) {
log.debug("Calculating result from partial results")
val result = updatedResults.reduceLeft(originalRequest.fn)
originalSender ! CalculationResult(result, originalRequest.index)
context.become(waitForCalculation, discardOld = true)
}else{
log.debug(s"Still waiting for ${outstandingResults-1} results")
// For fanOut > 2 one could here already combine consecutive partial results
context.become(waitForResults(outstandingResults-1, updatedResults, originalRequest, originalSender), discardOld = true)
}
}
}
使用并行前缀计算并不是最优的。计算较大数字乘积的参与者比计算较小数字乘积的参与者做更多的工作(例如,在计算 1 * ... * 100
时,计算 更快code>1 * ... * 10
比 90 * ... * 100
)。所以打乱数字可能是个好主意,这样大数字就会和小数字混合在一起。这在这种情况下有效,因为我们使用了交换操作。并行前缀计算一般只需要一个关联运算即可。
对于少量数据,参与者解决方案的性能比“朴素”解决方案(使用并行集合)差。当您进行复杂计算或将计算分布在专用硬件(例如图形卡或 FPGA)或多台机器上时,Actor 解决方案将会大放异彩。有了你可以控制的 Actor ,谁做哪个计算,你甚至可以重新启动“挂起的计算”。这可以给出 big speed up .
在单台机器上,当您的内存架构不一致时,actor 解决方案可能会有所帮助。然后,您可以通过将内存固定到特定处理器的方式来组织参与者。
我在 IntelliJ IDEA 中使用 Scala 工作表进行了一些实际性能测量。
首先我设置了 Actor 系统:
// Setup the actor system
val system = ActorSystem("root")
// Start one calculation actor
val calculationStart = Props(classOf[ParallelPrefixActor[BigInt]])
val calcolon = system.actorOf(calculationStart, "Calcolon-BigInt")
val inbox = Inbox.create(system)
然后我定义了一个辅助方法来测量时间:
// Helper function to measure time
def time[A] (id : String)(f: => A) = {
val start = System.nanoTime()
val result = f
val stop = System.nanoTime()
println(s"""Time for "${id}": ${(stop-start)*1e-6d}ms""")
result
}
然后我做了一些性能测量:
// Test code
val limit = 10000
def testRange = (1 to limit).map(BigInt(_))
time("par product")(testRange.par.product)
val timeOut = FiniteDuration(240, TimeUnit.SECONDS)
inbox.send(calcolon, Calculate[BigInt]((1 to limit).map(BigInt(_)), 0, 10, _ * _))
time("actor product")(inbox.receive(timeOut))
time("par sum")(testRange.par.sum)
inbox.send(calcolon, Calculate[BigInt](testRange, 0, 5, _ + _))
time("actor sum")(inbox.receive(timeOut))
我得到了以下结果
> Time for "par product": 134.38289ms res0: scala.math.BigInt = 284625968091705451890641321211986889014805140170279923 079417999427441134000376444377299078675778477581588406214231752883004233994015 351873905242116138271617481982419982759241828925978789812425312059465996259867 065601615720360323979263287367170557419759620994797203461536981198970926112775 004841988454104755446424421365733030767036288258035489674611170973695786036701 910715127305872810411586405612811653853259684258259955846881464304255898366493 170592517172042765974074461334000541940524623034368691540594040662278282483715 120383221786446271838229238996389928272218797024593876938030946273322925705554 596900278752822425443480211275590191694254290289169072190970836905398737474524 833728995218023632827412170402680867692104515558405671725553720158521328290342 799898184493136... Time for "actor product": 1310.217247ms res2: Any = CalculationResult(28462596809170545189064132121198688901480514017027 992307941799942744113400037644437729907867577847758158840621423175288300423399 401535187390524211613827161748198241998275924182892597878981242531205946599625 986706560161572036032397926328736717055741975962099479720346153698119897092611 277500484198845410475544642442136573303076703628825803548967461117097369578603 670191071512730587281041158640561281165385325968425825995584688146430425589836 649317059251717204276597407446133400054194052462303436869154059404066227828248 371512038322178644627183822923899638992827221879702459387693803094627332292570 555459690027875282242544348021127559019169425429028916907219097083690539873747 452483372899521802363282741217040268086769210451555840567172555372015852132829 034279989818449...> Time for "par sum": 6.488620999999999ms res3: scala.math.BigInt = 50005000> Time for "actor sum": 657.752832ms res5: Any = CalculationResult(50005000,0)
你可以很容易地看到 actor 版本比使用并行集合要慢得多。
关于scala - 使用 Scala actors 进行阶乘计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21624029/
我正在做一些关于大 O 表示法的练习题,遇到了这个问题。什么是函数 𝑓(𝑛) = 𝑛^2 + 𝑛 log2(𝑛) + log2(𝑛) 的大 O 阶。展示你的作品。 我的答案是 O(n^2)
是2n吗?只是检查。 最佳答案 术语 B 树的顺序在文献中的定义并不一致。 (例如,参见 terminology section of Wikipedia's article on B-Trees )
我想使用 numpy 创建一个 3 列数组,使得该数组类似于一堆 9x9 2 列数组。这些数组中的每一个都将完全填充有 1、2、3 等。 所以,看立方体的一面,我们看到的是 1,而另一面则是 9。然后
我想将这些数据存储到顺序为 3 (10,20,30,40,50,60,70,80,90) 的 B 树中,我的结果是 并且它与我的书的结果不匹配。可以吗?谢谢:) 最佳答案 这取决于你的意思 Is it
我是 numpy 的新手。创建一个新数组并用一定范围内的随机数填充每个元素的最佳方法是什么? 例如,我想要一个 3×3 数组,其中每个元素都是 0 或 1。 最佳答案 尝试类似的东西 np.rando
我正在尝试学习设计 btree。 以下是开发 5 阶 btree 的值。 1,12,8,2,25,6,14,28,17,7,52,16,48,68,3,26,29,53,55,45,67。 当我插入
我有一个 pandas 数据框,其特征值非常小,数量级为 -322。我正在尝试标准化这些功能,但得到了 ValueError: Input contains NaN, infinity or a va
我是一名优秀的程序员,十分优秀!