Это не короткий и, конечно, не эффективный, но это другой путь:
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)
}
}
}
(Хорошо - в данный момент это похоже на код-гольф).