Scala int String字符的值

时间:2011-06-04 17:37:23

标签: string scala char int

我只想总结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感兴趣。)

6 个答案:

答案 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