【发布时间】:2019-09-01 23:24:28
【问题描述】:
我正在尝试解决collatz problem on project Euler 的变体:
n → n/2(n 为偶数)
n → 3n + 1(n 为奇数)
使用上面的规则并从 13 开始,我们生成以下内容 顺序:
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
我的尝试是找出小于的数的最长 Collatz 序列 一百万
为此,我在 Scala 中编写了一个蛮力解决方案并对其进行了优化。
虽然优化后的版本可以非常快速有效地解决问题,但蛮力解决方案从未完成,最终导致内存不足错误:
object LongestCollatz {
def apply(upperbound: Long) = {
bruteForce(upperbound)
}
def bruteForce(upperBound: Long) = (1l to upperBound)
.map(toCollatzSequence)
.map(list => list.length)
.max
def toCollatzSequence(start: Long): ListBuffer[Long] = {
var term = start
var retList = ListBuffer(start)
if(start<=1) return retList
while(term > 1) {
if(term % 2 == 0) {
term = term/2l
} else {
term = (3l * term) + 1
}
retList.addOne(term)
}
retList.addOne(1l)
retList
}
}
object Solver extends App {
println("sol is " + LongestCollatz(1000000))
}
使用 visualvm 和一些日志记录,我看到蛮力方法中范围流的每一步都将针对所有 1M 数字完全完成,然后再进入下一步。所以:
.map(toCollatzSequence)
正在加载所有 1M 项的序列并将它们保存在堆中,直到它可以到达下一个 .map() 语句以将它们转换为单个数字长度。我可以通过修改函数来解决这个问题:
def bruteForce(upperBound: Long) = (1l to upperBound)
.map(l => toCollatzSequence(l).length)
.max
因此将两个 map 语句合并为一个。我的问题是,我把两张地图分开是不是出错了?或者对于此类问题,功能解决方案通常不是一个好主意?
【问题讨论】:
-
我不是 scala 程序员,但通常在 FP 中你编写函数并将 that 传递给 map。
标签: scala functional-programming visualvm