Scala int string character value

I just want to sum the BigInt digits. I can do

scala> "1 2 3".split(" ").map(_.toInt).sum
res23: Int = 6

So i tried

scala> BigInt(123).toString().map(_.toInt).sum
res24: Int = 150

This does not work because it maps characters to their Unicode values.

Both of the following work, but is there a more elegant way than using the static Java method or the optional toString transformation?

BigInt(123).toString().map(Character.getNumericValue(_)).sum
BigInt(123).toString().map(_.toString.toInt).sum

(I also did this using a recursive function, workarounds in general, but I'm interested in a short 1-liner here.)

+3
source share
6 answers

Wow ... these answers are everywhere! Here do the following:

BigInt(123).toString().map(_.asDigit).sum
+7
source

How not to use strings at all?

def sumDigits(b:BigInt):BigInt = {
  if (b < 10) b else b%10 + sumDigits(b/10)
}
+6
source

, , , :

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. , ( fastDigitSum BigInts).

, 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

, , , ,

BigInt(123).toString().split("").tail.map(_.toInt).sum

.

BigInt(123).toString().map(_.toInt - '0'.toInt).sum

,

BigInt(123).toString().map(_-'0').sum
+2

, . , , , : 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

So, it seems to BigInt(123).toString().map(_-'0').sumbe the winner of speed and conciseness for small BigInts, but the Rex Kerr method is good if your BigInts are huge.

Checkpoint Code:

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()
}
+2
source

I just noticed that the RichChar class has a getNumericValue method, so the answer will be

BigInt(123).toString().map(_.getNumericValue).sum
+1
source

All Articles