String 字符的 Scala int 值

发布于 2024-11-13 21:52:47 字数 514 浏览 2 评论 0原文

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

I just want to sum the digits of a BigInt. 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 doesn't work because it maps the characters to their Unicode values.

Both the following work, but is there a more elegant way than using the Java static method or an extra toString conversion?

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

(I've also done it using a recursive function, sidestepping strings altogether, but I'm interested here in a concise 1-liner.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

心碎无痕… 2024-11-20 21:52:47

哇...这些答案到处都是!在这里,执行以下操作:

BigInt(123).toString().map(_.asDigit).sum

Wow... these answers are all over the place! Here, do this:

BigInt(123).toString().map(_.asDigit).sum
活雷疯 2024-11-20 21:52:47

完全不使用字符串怎么样?

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

How about not using Strings at all?

def sumDigits(b:BigInt):BigInt = {
  if (b < 10) b else b%10 + sumDigits(b/10)
}
沫离伤花 2024-11-20 21:52:47

这并不短,当然也效率不高,但它是另一种方法:(

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)
    }
  }
}

(好吧——此时这有点像代码高尔夫。)

This is not short, and certainly not efficient, but it is another way to go:

scala> Iterator.iterate(BigInt(123))(_/10).takeWhile(_>0).map(_%10).sum
res1: scala.math.BigInt = 6

(and you probably want an Int, which is faster anyway but requires .map(i=>(i%10).toInt).)

The problem with this method (and straightforward recursion) is that you have to compute as many divisions as digits. (You could use /% to speed things up by a factor of 2, but that's still a problem.) Converting to a string is much faster because all those explicit BigInt creations can be avoided.

If you actually want something that works fast (not what you asked for, I know), you need a divide-and-conquer approach:

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)
  }
}

Edit: If you want really fast conversions at all sizes, I've modified my scheme as follows. There are some not-entirely-functional bits due largely to a lack of a takeTo method. This should be faster than everything else at all sizes (though it asymptotes to fastDigitSum performance for very large BigInts).

Probably will run better on 64 bit machines than 32.

No strings were harmed in the making of this function.

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)
    }
  }
}

(Okay--this is ending up sort of like code golf at this point.)

悟红尘 2024-11-20 21:52:47

我不认为它比你已经拥有的好多少,但

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

也有效。

另外

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

,根据 Rex Kerr 的评论,这可以简化为

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

I don't think it's much better than the ones you've already got, but

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

also works.

Also

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

and per Rex Kerr's comment, this can be simplified to

BigInt(123).toString().map(_-'0').sum
韵柒 2024-11-20 21:52:47

这不是问题的答案,而是对建议的速度测试。我多次运行测试,以确保虚拟机已预热且结果一致:这些结果具有代表性,并且适用于 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< /code> 是较小 BigInt 的速度和简洁性方面的赢家,但如果您的 BigInt 很大,则 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()
}

This in not an answer to the question but a speed test of the suggestions. I ran the tests several times to ensure the VM was warmed up and results were consistent: these results are representative and are for 10000 iterations. See code for definitions of what these methods are.

For 10-digit BigInts

fastDigitSum: 0.020618047 seconds
stringSum:    0.023708908 seconds
stringSum2:   0.02940999 seconds
stringSum3:   0.021641507 seconds
division:     0.052856631 seconds

For 50-digit BigInts

fastDigitSum: 0.183630732 seconds
stringSum:    0.110235062 seconds
stringSum2:   0.134900857 seconds
stringSum3:   0.096670394 seconds
division:     0.317359989 seconds

For 100-digit BigInts

fastDigitSum: 0.427543476 seconds
stringSum:    0.228062302 seconds
stringSum2:   0.277711389 seconds
stringSum3:   0.20127497 seconds
division:     0.811950252 seconds

For 100,000-digit BigInts (1 iteration)

fastDigitSum: 0.581872856 seconds
stringSum:    2.642719635 seconds
stringSum2:   2.629824347 seconds
stringSum3:   2.61327453 seconds
division:     30.089482042 seconds

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

Benchmark 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()
}
眼眸里的那抹悲凉 2024-11-20 21:52:47

我刚刚注意到 RichChar 类有一个 getNumericValue 方法,所以答案是

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

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

BigInt(123).toString().map(_.getNumericValue).sum
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文