我只想总结BigInt的数字。我能做到
scala> "1 2 3".split(" ").map(_.toInt).sum
res23: Int = 6
所以我试过
scala> BigInt(123).toString().map(_.toInt).sum
res24: Int = 150
这不起作用,因为它将字符映射到它们的Unicode值。
以下两项工作,但是使用Java静态方法或额外的toString转换是否有更优雅的方式?
BigInt(123).toString().map(Character.getNumericValue(_)).sum
BigInt(123).toString().map(_.toString.toInt).sum
(我也使用递归函数完成它,完全回避字符串,但我对这里简洁的1-liner感兴趣。)
答案 0 :(得分:7)
BigInt(123).toString().map(_.asDigit).sum
答案 1 :(得分:6)
根本不使用字符串怎么样?
def sumDigits(b:BigInt):BigInt = {
if (b < 10) b else b%10 + sumDigits(b/10)
}
答案 2 :(得分:3)
这不短,当然效率不高,但这是另一种方式:
scala> Iterator.iterate(BigInt(123))(_/10).takeWhile(_>0).map(_%10).sum
res1: scala.math.BigInt = 6
(你可能想要一个Int
,但速度更快但需要.map(i=>(i%10).toInt)
。)
此方法的问题(以及简单的递归)是您必须计算与数字一样多的除法。 (您可以使用/%
将速度提高2倍,但这仍然是一个问题。)转换为字符串要快得多,因为可以避免所有这些显式BigInt
创建。
如果你真的想要一些有效快速的东西(不是你所要求的,我知道),你需要一种分而治之的方法:
def fastDigitSum(b: BigInt): Int = {
val bits = b.bitLength
if (bits < 63) math.abs(b.toLong).toString.map(_-'0').sum
else {
val many = 256
val zeros = math.ceil(bits*0.150515).toInt // constant is 0.5*log(2)/log(10)
val root = (
if (zeros<many) BigInt("1" + "0"*zeros)
else {
Iterator.iterate((BigInt("1"+"0"*many),many))(x => (x._1 * x._1, 2*x._2)).
find(_._2 > zeros/2).get._1
}
)
val (q,r) = b /% root
fastDigitSum(q) + fastDigitSum(r)
}
}
编辑:如果您想要真正各种规模的快速转换,我已按如下方式修改了我的方案。由于缺少takeTo
方法,有一些不完全功能的位。这应该比所有尺寸的其他所有尺寸都要快(尽管对于非常大的BigInts来说它的渐近性能为fastDigitSum
。)
可能会在64位机器上运行得比32位好。
制作此功能时没有任何字符串受到伤害。
object DigitSum {
val memend = BigInt(10000000000000000L) :: BigInt(100000000) :: Nil
def longSum(l: Long, sum: Int = 0): Int = {
if (l==0) sum else longSum(l/10, sum + (l%10).toInt)
}
def bigSum(b: BigInt, memo: List[BigInt] = Nil): Int = {
val bits = b.bitLength
if (bits < 64) longSum(b.toLong)
else {
val mem = (
if (memo.isEmpty) {
val it = Iterator.iterate(memend.head)(x=>x*x).drop(1)
var xs = memend
while (xs.head.bitLength*4 <= bits) xs = it.next :: xs
xs
}
else memo.dropWhile(_.bitLength > bits/2)
)
val (q,r) = b /% mem.head
bigSum(q,memo) + bigSum(r,memo)
}
}
}
(好的 - 此时最终有点像代码高尔夫。)
答案 3 :(得分:2)
我认为这比你已经获得的要好得多,但是
BigInt(123).toString().split("").tail.map(_.toInt).sum
也有效。
另外
BigInt(123).toString().map(_.toInt - '0'.toInt).sum
根据Rex Kerr的评论,这可以简化为
BigInt(123).toString().map(_-'0').sum
答案 4 :(得分:2)
这不是问题的答案,而是对建议的速度测试。我多次运行测试以确保VM已预热并且结果一致:这些结果具有代表性,适用于10000次迭代。请参阅代码以了解这些方法的定义。
对于10位数的BigInts
fastDigitSum: 0.020618047 seconds
stringSum: 0.023708908 seconds
stringSum2: 0.02940999 seconds
stringSum3: 0.021641507 seconds
division: 0.052856631 seconds
对于50位BigInts
fastDigitSum: 0.183630732 seconds
stringSum: 0.110235062 seconds
stringSum2: 0.134900857 seconds
stringSum3: 0.096670394 seconds
division: 0.317359989 seconds
对于100位BigInts
fastDigitSum: 0.427543476 seconds
stringSum: 0.228062302 seconds
stringSum2: 0.277711389 seconds
stringSum3: 0.20127497 seconds
division: 0.811950252 seconds
对于100,000位BigInts(1次迭代)
fastDigitSum: 0.581872856 seconds
stringSum: 2.642719635 seconds
stringSum2: 2.629824347 seconds
stringSum3: 2.61327453 seconds
division: 30.089482042 seconds
所以似乎BigInt(123).toString().map(_-'0').sum
是小型BigInts速度和简洁性的赢家,但如果你的BigInts很大,Rex Kerr的方法很好。
基准代码:
object Benchmark extends App{
def fastDigitSum(b: BigInt): Int = {
val bits = b.bitLength
if (bits < 63) math.abs(b.toLong).toString.map(_-'0').sum
else {
val many = 256
val zeros = math.ceil(bits*0.150515).toInt // constant is 0.5*log(2)/log(10)
val root = (
if (zeros<many) BigInt("1" + "0"*zeros)
else {
Iterator.iterate((BigInt("1"+"0"*many),many))(x => (x._1 * x._1, 2*x._2)).
find(_._2 > zeros/2).get._1
}
)
val (q,r) = b /% root
fastDigitSum(q) + fastDigitSum(r)
}
}
def stringSum(b: BigInt) = b.toString.map(_.getNumericValue).sum
def stringSum2(b: BigInt) = b.toString.map(_.toString.toInt).sum
def stringSum3(b: BigInt) = b.toString.map(_ - '0').sum
def division(b: BigInt) = sumDig(b, 0)
def sumDig(b: BigInt, sum: Int):Int = {
if (b == 0) sum
else sumDig(b / 10, sum + (b % 10).toInt)
}
def testMethod(f: BigInt => Int):Double = {
val b = BigInt("12345678901234567890123456789012345678901234567890")
val b2 = BigInt("1234567890")
val b3 = BigInt("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890")
val t0 = System.nanoTime()
var i = 0
while(i < 10000){
f(b3)
i += 1
}
(System.nanoTime().toDouble - t0)/1e9
}
def runTest(){
var i = 0
while (i < 5) {
println("fastDigitSum: " + testMethod ({fastDigitSum}) + " seconds")
println("stringSum: " + testMethod ({stringSum}) + " seconds")
println("stringSum2: " + testMethod ({stringSum2}) + " seconds")
println("stringSum3: " + testMethod ({stringSum3}) + " seconds")
println("division: " + testMethod ({division}) + " seconds")
i += 1
}
}
runTest()
}
答案 5 :(得分:1)
我刚注意到RichChar类有一个getNumericValue方法,所以答案是
BigInt(123).toString().map(_.getNumericValue).sum