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 (). 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:
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