因式分解 - NumPy, Scipy, Math, Python

645 阅读6分钟

阶乘的定义和例子

💡 阶乘函数 ,计算一个集合中的排列数。n!

假设你想对三支足球队曼联队巴塞罗那队拜仁慕尼黑队进行排名--存在多少种可能的排名?

答案是3! = 3 x 2 x 1 = 6

实际例子。假设,英格兰超级联赛有20支足球队。每支球队都有可能在赛季结束后达到20个排名中的任何一个。在20支固定球队的情况下,英超联赛存在多少种可能的排名?

图。英格兰超级联赛中的足球队的三种可能排名的例子。

一般来说,要计算阶乘 n! ,你需要将所有小于或等于n 的正整数相乘。

例如,如果你有5支足球队,就有5! = 5 x 4 x 3 x 2 x 1 = 120 个不同的配对。

在Python中,有许多不同的方法可以轻松计算阶乘函数,请看下面的替代方法。

在你阅读文章时,请随时观看我的解释视频。

如何在NumPy中计算阶乘?

NumPy的数学模块包含基本数学函数的有效实现,如阶乘函数numpy.math.factorial(n)

下面是一个如何用NumPy计算阶乘的例子3!

>>> import numpy as np
>>> np.math.factorial(3)
6

NumPy中的阶乘函数只有一个整数参数n 。如果参数是负数或者不是整数,Python将引发一个值错误。

下面是用Python计算3队的方法。

练习。修改代码以计算20个队的排名数!

如何在Scipy中计算阶乘?

流行的 [scipy](https://blog.finxter.com/best-10-scipy-cheat-sheets/)库是一个帮助你进行科学计算的库和模块的集合。

Scipy包含一个强大的功能集合--建立在NumPy库之上。因此,SciPy的阶乘函数scipy.math.factorial() 实际上是对NumPy的阶乘函数numpy.math.factorial() 的引用,这并不奇怪。

事实上,如果你用关键字 is 来比较它们的内存地址,就会发现两者都是指代同一个函数对象

>>> import scipy, numpy
>>> scipy.math.factorial(3)
6
>>> numpy.math.factorial(3)
6
>>> scipy.math.factorial is numpy.math.factorial
True

所以你可以同时使用scipy.math.factorial(3)numpy.math.factorial(3) 来计算阶乘函数3!

由于这两个函数都指向同一个对象,其性能特征也是一样的--一个并不比另一个快。

让我们来看看math.factorial() --所有阶乘函数之母。😉


如何在Python的数学库中计算阶乘?

事实证明,不仅NumPy和Scipy带有阶乘函数的打包 "实现",而且Python的强大数学库也有。

你可以用 [math.factorial(n)](https://blog.finxter.com/python-math-factorial-2/)函数来计算阶乘n!

这里有一个例子。

>>> import math
>>> math.factorial(3)
6

3的阶乘是6--这里没有什么新东西。

让我们检查一下这是否真的与NumPy和Scipy的阶乘函数的实现相同。

>>> import scipy, numpy, math
>>> scipy.math.factorial is math.factorial
True
>>> numpy.math.factorial is math.factorial
True

哈!NumPy和Scipy两个库都依赖于数学库的同一个阶乘函数。

💡 注意:因此,为了节省 你代码中的宝贵空间 ,如果你已经导入了 库,请使用 阶乘函数。如果没有,就使用NumPy或Scipy的阶乘函数别名。math math

所以到现在为止,我们已经在三个不同的瓶子里看到了同样的老酒。NumPy、Scipy和数学库都提到了同一个阶乘函数的实现。

如何在 Python 中计算阶乘?

自己实现一个函数通常是个好主意。这将帮助你更好地理解底层细节,并给你带来信心和专业知识。

因此,让我们在Python中实现阶乘函数!

要计算一个给定的n 元素集合的排列数,你可以使用阶乘函数n! 。阶乘的定义如下。

n!= n × (n - 1) × ( n - 2) × ... × 1

比如说

  • 1! = 1
  • 3!= 3 × 2 × 1 = 6
  • 10!= 10 × 9 × 8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 3,628,800
  • 20! = 20 × 19 × 18 × . . . × 3 × 2 × 1 = 2,432,902,008,176,640,000

递归地,阶乘函数也可以定义如下。

n!= n × (n - 1)!

递归基例的定义如图所示。

1! = 0! = 1

这些基例背后的直觉是,一个有一个元素的集合有一个包络,一个有零个元素的集合有一个包络(有一种方法是将零个元素分配给零个桶)。

现在,我们可以使用这个递归定义,以递归的方式计算阶乘函数。

>>> factorial = lambda n: n * factorial(n-1) if n > 1 else 1
>>> factorial(3)
6

自己试试吧。在我们的交互式代码外壳中运行这个单行程序。

练习一下。输出是什么?

lambda 关键字被用来在一行中定义一个匿名函数。

你创建了一个有一个参数的lambda函数n ,并将这个lambda函数赋值为名称factorial 。最后,你调用命名的函数factorial(n-1) 来计算函数调用的结果factorial(n)

粗略地说,你可以使用factorial(n-1) 的较简单的解决方案来构建较难的问题factorial(n) 的解决方案,将前者与输入参数n 相乘。

一旦你到达递归基例n <= 1 ,你只需返回硬编码的解决方案factorial(1) = factorial(0) = 1

另一种方法是像这样使用迭代计算。

def factorial(n):
    fac = n
    for i in range(1, n):
        fac *= i
    return fac

print(factorial(3))
# 6

print(factorial(5))
# 120

在函数factorial(n) 中,我们将变量fac 初始化为值n 。然后,我们遍历1到n-1(包括)之间的所有数值i ,并将其与当前存储在变量fac 中的数值相乘。其结果是整数值n 的阶乘。

速度比较

让我们比较一下三种不同的阶乘函数计算方式的速度

请注意,NumPy、Scipy和数学阶乘函数引用的是同一个函数对象--它们具有相同的速度属性。

因此,我们只将math.factorial() 函数与我们在Python中的两种实现方式(递归和迭代)进行比较。

想先猜一猜吗?

我使用我自己的笔记本电脑(四核,英特尔酷睿i7,第八代)和Python 3.7,使用以下代码为每种方法运行900次阶乘计算。

import time

num_runs = 900
speed = []


## SPEED TEST MATH.FACTORIAL ##
import math


start = time.time()
for i in range(num_runs):
    math.factorial(i)
stop = time.time()

speed.append(stop-start)

    
## SPEED TEST RECURSIVE ##
factorial = lambda n: n * factorial(n-1) if n > 1 else 1

start = time.time()
for i in range(num_runs):
    factorial(i)
stop = time.time()

speed.append(stop-start)

    
## SPEED TEST ITERATIVE ##
def factorial(n):
    fac = n
    for i in range(1, n):
        fac *= i
    return fac


start = time.time()
for i in range(num_runs):
    factorial(i)
stop = time.time()

speed.append(stop-start)


## RESULT
print(speed)
# [0.011027336120605469, 0.10074210166931152, 0.0559844970703125]
import matplotlib.pyplot as plt
plt.bar(["Math", "Recursive", "Iterative"], height=speed)
plt.show()

哇--明显的赢家是math 模块!一个明显的迹象是,你应该总是倾向于使用库的代码而不是自己的实现!

math 库的实现几乎比迭代的实现快600%,比递归的实现快1000%。

方法math.factorial递归迭代法
秒数0.010.100.05

自己试试吧。你可以在交互式代码外壳中自己进行这个速度比较。

练习。你在你的浏览器中收到类似的结果吗?运行shell来看看吧!

今后的发展方向

三个库的实现numpy.math.factorial(),scipy.math.factorial(), 和math.factorial() 指向内存中的同一个函数对象--它们是相同的,所以使用它们中的任何一个。

在更高的层次上,你已经了解到流行的库如NumPy的库实现是非常快速和高效的。帮你自己一个忙,尽可能地使用库的实现。

NumPy库是一个很好的开始,它是Python中许多高级数据科学和机器学习库的基础,如matplotlib、pandas、tensorflow和scikit-learn。学习NumPy将为你建立你的Python事业奠定基础。

程序员的幽默

Q: How do you tell an introverted computer scientist from an extroverted computer scientist?

A: An extroverted computer scientist looks at your shoes when he talks to you.