如何在Swift中计算21!(21的阶乘)?

18

我正在用 Swift 编写一个计算阶乘的函数,代码如下:

func factorial(factorialNumber: UInt64) -> UInt64 {
    if factorialNumber == 0 {
        return 1
    } else {
        return factorialNumber * factorial(factorialNumber - 1)
    }
}

let x = factorial(20)

这个函数可以计算到20。

我认为21的阶乘值大于UINT64_MAX。

那么如何在Swift中计算21!(21的阶乘)?


我认为在纯Swift中这是不可能的。至少不是很简单的事情。我不知道有哪种语言可以直接计算出这样的数字。 - dasdom
我个人没有使用过它,但你可以尝试使用类似于 https://github.com/kirsteins/BigInteger 的库。 - Kevin
它可能看起来不太美观,但如果您将每次迭代的结果存储在字符串中而不是UIint中,则可以像在纸上一样进行乘法运算。虽然不够高效,但BigInteger库似乎更有用,但这也是一个选择。 - Dániel Nagy
你为什么需要阶乘?如果它是方程的一部分(例如在某个除法中),那么你可以通过迭代步骤计算方程,使得子结果足够小以适应变量。另一种方法是使用H,L样式的变量(每个数字使用两个变量),或者使用某种bigint库。如果你选择使用bigints,也许这个快速精确的bigint阶乘能帮上一些忙。 - Spektre
@dasdom 几乎任何 Smalltalk 方言都可以直接计算出 21!。 - aka.nice
1
@dasdom:Python也可以。 - Mark Dickinson
12个回答

13
func factorial(_ n: Int) -> Double {
  return (1...n).map(Double.init).reduce(1.0, *)
}
  1. (1...n):我们创建一个包含所有参与操作的数字的数组(例如:[1, 2, 3, ...])。

  2. map(Double.init):我们将类型从Int更改为Double,因为使用Double可以表示比Int更大的数字(https://en.wikipedia.org/wiki/Double-precision_floating-point_format)。因此,我们现在将参与操作的所有数字作为Doubles数组(例如:[1.0, 2.0, 3.0, ...])。

  3. reduce(1.0, *):我们开始将1.0与数组中的第一个元素相乘(1.0 * 1.0 = 1.0),然后将结果与下一个元素相乘(1.0 * 2.0 = 2.0),再将结果与下一个元素相乘(2.0 * 3.0 = 6.0),以此类推。

步骤2是为了避免溢出问题。

步骤3是为了避免显式定义变量来跟踪部分结果。


4
好的,我会尽力进行翻译。以下是需要翻译的内容:希望你能附加一些关于你答案背后逻辑的评论。 - Simas Joneliunas
1
你能提供一些关于这个的解释吗? - McDonal_11
我添加了一些解释。 - kanobius
如果你向函数传递0或负数,程序将会崩溃。 - Cristik

9

无符号 64 位整数的最大值为 18,446,744,073,709,551,615。而 21! = 51,090,942,171,709,440,000。对于这种情况,您需要使用大整数类型。我在 Swift 中找到一个关于大整数的问题。该链接中有一个专门用于处理大整数的库。

Swift 中的 BigInteger 等效物?


2

你是否考虑过使用双倍精度或NSDecimalNumber?

此外,反复调用同一函数在性能方面非常糟糕。

使用循环怎么样:

let value = number.intValue - 1

var product = NSDecimalNumber(value: number.intValue)

for i in (1...value).reversed() {
    product = product.multiplying(by: NSDecimalNumber(value: i))
}

2
小问题:乘法方程的结果是“积”,而不是“和”。 - Ian Rahman

0

首先,我们需要声明一个类型为double的临时变量,以便它可以存储数字的大小。
然后我们创建一个函数,该函数接受类型为double的参数。
然后我们检查,如果数字等于0,我们可以返回或什么都不做。我们有一个if条件,因此我们可以中断函数的递归调用。最后,我们返回temp,它保存给定数字的阶乘。

var temp:Double = 1.0

func factorial(x:Double) -> Double{
    if(x==0){
        //do nothing
    }else{
        factorial(x: x-1)
        temp *= x
    }
    return temp
}

factorial(x: 21.0)

请在您的键盘上找到标点符号。请注意,如果您需要改进某些内容,可以[编辑]您的帖子。这可能会有所帮助:https://stackoverflow.com/editing-help - Yunnosch
请仔细检查我的编辑。如果我有任何错误,请将其视为重要标点符号对理解的重要性的说明,并随意修复我无意中破坏的内容。 - Yunnosch
谢谢,现在答案看起来更好了 @Yunnosch - eslam mohamed

0

如果你愿意放弃精度,你可以使用Double类型来大致计算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
}

如果不行,使用大整数库。

0
这是一个在Swift中返回整数阶乘值的扩展。它最大支持计算到170!,但希望能解决任何人的编码问题!
extension Int {
 var factorial:Double {
  var num:Double = Double(self)
  var product:Double = 1
   while(num > 0) {
    product *= num
    num -= 1
   }
  return product
 }
}

print(170.factorial) // 7.257415615308004e+306
print(21.factorial) // 5.109094217170944e+19

-1
我这样编写函数来计算阶乘:
func factorialNumber( namber : Int ) -> Int {
    var x = 1
     
    for i in 1...namber {
        x *= i   
    }
    return x 
            
}
print ( factorialNumber (namber : 5 ))

1
问题是询问如何计算21!而不是5!对于任何大于20的数字,此答案都会崩溃。 - HangarRash

-1
这是一个接受符合“Numeric”协议的任何类型的函数,这些类型都是内置数字类型。
func factorial<N: Numeric>(_ x: N) -> N {
    x == 0 ? 1 : x * factorial(x - 1)
}

1
问题是要求计算21!。由于21!对于所有内置整数类型都过大,因此该答案对于所有整数类型均无法成功。 - HangarRash

-2
func factorial(a: Int) -> Int {

    return a == 1 ? a : a * factorial(a: a - 1)
}

print(factorial(a : 5))

print(factorial(a: 9))

如果使用参数21调用,此代码会出现“算术溢出”的崩溃。 - Cristik

-2
func factoruial(_ num:Int) -> Int{
        if num == 0 || num == 1{
            return 1
        }else{
           return(num*factoruial(num - 1))
       }
   }

2
输入21 -> 25321 非法指令 (核心已转储) - General Grievance
如果使用参数21调用,则会因“算术溢出”而崩溃。 - Cristik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接