Reputation: 421
I am making fuction that calculates factorial in Swift. Like this:
func factorial(factorialNumber: UInt64) -> UInt64 {
if factorialNumber == 0 {
return 1
} else {
return factorialNumber * factorial(factorialNumber - 1)
}
}
let x = factorial(20)
This fuction can calculate until 20!.
I think factorial(21) is bigger than UINT64_MAX.
How to accurately calculate 21! (21 factorial) in Swift?
Upvotes: 18
Views: 20659
Reputation: 891
func factorial(_ n: Int) -> Double {
return (1...n).map(Double.init).reduce(1.0, *)
}
(1...n)
: We create an array of all the numbers that are involved in the operation (i.e: [1, 2, 3, ...]
).
map(Double.init)
: We change from Int
to Double
because we can represent bigger numbers with Doubles than with Ints (https://en.wikipedia.org/wiki/Double-precision_floating-point_format). So, we now have the array of all the numbers that are involved in the operation as Doubles
(i.e: [1.0, 2.0, 3.0, ...]
).
reduce(1.0, *)
: We start multiplying 1.0
with the first element in the array (1.0*1.0 = 1.0
), then the result of that with the next one (1.0*2.0 = 2.0
), then the result of that with the next one (2.0*3.0 = 6.0
), and so on.
Step 2 is to avoid the overflow issue.
Step 3 is to save us from explicitly defining a variable for keeping track of the partial results.
Upvotes: 14
Reputation: 3660
Did you think about using a double perhaps? Or NSDecimalNumber?
Also calling the same function recursively is really bad performance wise.
How about using a loop:
let value = number.intValue - 1
var product = NSDecimalNumber(value: number.intValue)
for i in (1...value).reversed() {
product = product.multiplying(by: NSDecimalNumber(value: i))
}
Upvotes: 3
Reputation: 1
If you are willing to give up precision you can use a Double to roughly calculate factorials up to 170:
func factorial(_ n: Int) -> Double {
if n == 0 {
return 1
}
var a: Double = 1
for i in 1...n {
a *= Double(i)
}
return a
}
If not, use a big integer library.
Upvotes: 0
Reputation: 3072
Unsigned 64 bit integer has a maximum value of 18,446,744,073,709,551,615. While 21! = 51,090,942,171,709,440,000. For this kind of case, you need a Big Integer type. I found a question about Big Integer in Swift. There's a library for Big Integer in that link.
BigInteger equivalent in Swift?
Upvotes: 9