Search code examples
kotlinfactorial

How to implement Stirling's formula with BigDecimal and BigInteger in Kotlin?


I'm trying to do a factorial program. If the input number is below 250 000 I use a tail recursive function to find the factorial of that number. But if the input number exceed the value of 250 000 I try to use the Stirling's formula (enter image description here). I want to be able to work with BigIntegers and BigDecimals, but whenever I try to calculate 250 102 I get NaN (Not a Number ... error). Can you please help me?

Here is my code in Kotlin:

import java.io.File
import java.math.BigDecimal
import java.math.BigInteger

tailrec fun tail_recursion_factorial(n: BigInteger, factorialOfN: BigInteger = BigInteger.valueOf(2)): BigInteger {
    return when(n){
        BigInteger.ZERO -> BigInteger.ONE
        BigInteger.ONE ->  BigInteger.ONE
        BigInteger.valueOf(2) ->  factorialOfN
        else -> tail_recursion_factorial(n.minus(BigInteger.ONE), n.times(factorialOfN))
    }
}

// calculate approximate value of n!
//  using Stirling's approximation:

fun Stirling_factorial(n: BigInteger): BigDecimal {
    return BigDecimal.valueOf(Math.sqrt((2*n.toDouble()*1.0/3.0)*Math.PI))*BigDecimal.valueOf(Math.pow(n.toDouble(),n
            .toDouble()))*BigDecimal.valueOf(Math.pow(Math.E,-1.0*n.toDouble()))
}


fun main(args: Array<String>){
    print("n == ")
    var n = readLine()
    try {
        when {
            BigInteger(n) < BigInteger.ZERO -> println("Sorry bro! Can't do a factorial to a negative number.")
            BigInteger(n) >= BigInteger.ZERO -> {
                when{
                    BigInteger(n) <= BigInteger.valueOf(250000) -> {
                        File("factorial.txt").writeText("\"$n! is ${tail_recursion_factorial(BigInteger(n))}\"")
                        println("Check factorial.txt in the project directory!")
                    }
                    else -> {
                        println("Since your number is bigger than 250 000\nwe're calculating using the Stirling's formula which is an approximation of the factorial.\n")
                        File("factorial.txt").writeText("The aproximation of $n! is\n${Stirling_factorial
                        (BigInteger(n))}")
                        println("Check factorial.txt in the project directory!")
                    }
                }

            }
        }
    }catch (e: NumberFormatException){
        println("Sorry bro! Can't do that ...")
    }
}

EDIT: Problem solved

import ch.obermuhlner.math.big.BigDecimalMath
import java.io.File
import java.math.BigDecimal
import java.math.MathContext

and ....

fun Stirling_formula(n: BigDecimal) : BigDecimal {
    val mathContext = MathContext(121)
    return (BigDecimal.ONE + BigDecimal.ONE.divide(BigDecimal.valueOf(12).times(n),mathContext))
            .times(BigDecimalMath.sqrt((n.times(BigDecimal.valueOf(2.0))).times(BigDecimalMath.pi(mathContext)), mathContext))
            .times(BigDecimalMath.pow(n,n,mathContext))
            .times(BigDecimalMath.pow(BigDecimalMath.e(mathContext),-n,mathContext))

All thanks goes to Alexander Romanov

PS: Also I've updated the formula with a slightly more accurate approximation one: enter image description here


Solution

  • You need a proper library for such large computations. Take a look at kotlin-big-math.

    val a = Math.pow(250102.0, 250102.0)
    println(a) // >> Infinity
    
    val b = BigDecimal(a) // Exception in thread "main" java.lang.NumberFormatException:
                          // Infinite or NaN