You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CodeWars Going to zero or to infinity? Kotlin实现优化求助

CodeWars阶乘计算Kata优化记录

这是CodeWars平台的一道编程kata题目:我最初使用Double类型实现逻辑,但计算30!及以上阶乘时必须改用BigDecimal类型;迭代到200!计算场景优化代码后遇到执行超时问题,超时阈值为16000ms。
现有需求是实现可快速计算200!以上阶乘的代码,解决大参数下的运行异常问题。

初始版本(仅支持200!以内计算)

最初编写的代码可正常通过200!以内的全部简单测试,但随机测试用例n超过200时就会触发超时;代码在本地IDEA环境运行无问题,但CodeWars服务器端无法在时限内完成计算,初始代码如下:

fun going(n: Int): Double {
    val factorial = factorial(n)
    val zeroField = zero(factorial)
    val infiniteField = infinite(n)

    val beforeRound = zeroField.multiply(infiniteField)
    return beforeRound.setScale(6, RoundingMode.DOWN).toDouble()
}

fun factorial(n: Int): BigDecimal{
    if (n<=1) return BigDecimal.ONE

    return BigDecimal(n).multiply(factorial(n-1))
}

fun infinite(n: Int): BigDecimal {
    var sumFact = BigDecimal(0)
    var factCounter = 1
    repeat(n){
        sumFact += factorial(factCounter)
        factCounter++
    }
    return sumFact
}

fun zero(factorial: BigDecimal): BigDecimal{
    return BigDecimal.ONE.divide(factorial, MathContext.DECIMAL128)
}

HashMap缓存优化版本(支持n<1000场景)

在其他用户提示下,我尝试使用HashMap缓存阶乘计算结果,该版本可支持n<1000的场景,但n大于约5800时会抛出主线程栈溢出异常java.lang.StackOverflowError。我的目标是支持10000!这类超大阶乘计算,加入HashMap缓存的代码如下:

val myFactorialHash = mutableMapOf<Int, BigDecimal>(1 to BigDecimal.ONE)


    fun going(n: Int): Double {
        
        println(n)

        val factorial = factorial(n)
        val zeroField = zero(factorial)
        val infiniteField = infinite(n)

        val beforeRound = zeroField.multiply(infiniteField)
        return beforeRound.setScale(6, RoundingMode.DOWN).toDouble()
    }

    fun factorial(n: Int): BigDecimal{
        if (n<=1){
            return myFactorialHash[1]!!
        }

        if (myFactorialHash.containsKey(n)){
            return myFactorialHash[n]!!
        } else {
            myFactorialHash[n] = BigDecimal(n).multiply(factorial(n-1))
        }

        return myFactorialHash[n]!!
    }

    fun infinite(n: Int): BigDecimal {
        var sumFact = BigDecimal(0)
        var factCounter = 1
        repeat(n){
            sumFact += factorial(factCounter)
            factCounter++
        }
        return sumFact
    }

    fun zero(factorial: BigDecimal): BigDecimal{
        return BigDecimal.ONE.divide(factorial, MathContext.DECIMAL128)
    }

尾递归修饰符测试

针对递归tailrec尾递归修饰符做了对照测试:实际实验中添加tailrec修饰与不添加没有明显差异,n=7027时运行正常,n=7028时触发栈溢出异常,实验代码如下:

fun main(){
    println(factorial(7027))
}

tailrec fun factorial(n: Int): BigDecimal{
    if (n<1) return BigDecimal.ONE
    return BigDecimal(n).multiply(factorial(n-1))
}

预初始化哈希缓存版本

调试5小时后,我尝试按最大栈容量预初始化哈希缓存,对应代码如下:

val myFactorialHash = mutableMapOf<Int, BigDecimal>(1 to BigDecimal.ONE)

    fun going(n: Int): Double {
        initHash()
        val factorial = factorial(n)
        val zeroField = zero(factorial)
        val infiniteField = infinite(n)

        val beforeRound = zeroField.multiply(infiniteField)
        return beforeRound.setScale(6, RoundingMode.DOWN).toDouble()
    }

    fun factorial(n: Int): BigDecimal{
        if (n<=1){
            return myFactorialHash[1]!!
        }

        var counter = n

        if (myFactorialHash.containsKey(n)){
            return myFactorialHash[n]!!
        } else {
            myFactorialHash[n] = BigDecimal(n).multiply(factorial(n-1))
        }

        return myFactorialHash[n]!!
    }

    fun infinite(n: Int): BigDecimal {
        var sumFact = BigDecimal(0)
        var factCounter = 1
        repeat(n){
            sumFact += factorial(factCounter)
            factCounter++
        }
        return sumFact
    }

    fun zero(factorial: BigDecimal): BigDecimal{
        return BigDecimal.ONE.divide(factorial, MathContext.DECIMAL128)
    }

    fun initHash(){
        repeat(7000){
            myFactorialHash[it] = factorial(it)
        }
    }

极简1行代码实现原理

我最终通过BigDecimal哈希缓存方案通过了题目,但看到题解区的极简实现时大受震撼,该方案仅用1行代码就实现了全部需求,代码如下:

fun going(n: Int): Double = if (n == 0) 0.0 else 1 + going(n - 1) / n

这个实现的核心是对题目要求的计算公式做了数学化简,完全避开了超大数计算:
题目实际要求计算的是(1! + 2! + 3! + ... + n!) / n!,我们记这个结果为f(n),对式子做拆分:

  • f(n) = [n! + (1! + 2! + ... + (n-1)!)] / n!
  • 拆分后得到 f(n) = 1 + (1! + 2! + ... + (n-1)!)/n!
  • 把分母n!拆成n*(n-1)!,就可以得到 f(n) = 1 + [(1! + 2! + ... + (n-1)!)/(n-1)!]/n = 1 + f(n-1)/n

按照这个递推式直接用Double类型计算即可,不需要引入BigDecimal处理超大阶乘,不会出现超时、栈溢出问题,n到十万级都可以瞬间完成计算,精度也完全满足题目保留6位小数的要求。

内容的提问来源于stack exchange,提问作者elenaovchinnikova

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:21:18