专栏名称: Python编程
人生苦短,我用 Python !关注 Python 编程技术和运用。分享 Python 相关技术文章、开发工具资源、热门信息等。
目录
相关文章推荐
HR笔记本  ·  优秀员工是如何被逼走的! ·  昨天  
人力资源管理  ·  绩效系数设计 ·  2 天前  
清华经管学院职业发展中心  ·  招聘 | 嘉实基金2026届校园招聘全面开启! ·  2 天前  
HR新逻辑  ·  华为组织与个人绩效实践.ppt ·  2 天前  
中国企业家杂志  ·  滴滴2024年全年GTV达3927亿元,经调 ... ·  3 天前  
51好读  ›  专栏  ›  Python编程

仅用 10 行 Python 代码,搞定 10 种数学运算!

Python编程  · 公众号  ·  · 2024-06-29 16:04

正文

本文经授权转自公众号CSDN(ID:CSDNnews)

翻译 | 郑丽媛

【编者按】在如今这个 AI 飞速发展的时代,Python 以其简洁、易读的语法和强大的表现力,深受程序员和科学家的喜爱。本文将展示在仅仅 10 行代码内,Python 如何实现复杂的数学运算,包括向量加法、点积、矩阵乘法、矩阵转置以及线性系统求解器等。

原文链接:https://wordsandbuttons.online/how_much_math_can_you_do_in_10_lines_of_python.html


记得我刚开始用 Python 时,还没有 NumPy 或 SymPy 。那时,我们常常在 MatLab 中做研究,在 Delphi 中进行快速原型设计……说出来你可能不信,我们还会在 Prolog 中做符号计算。那时的 Python 类似于一个好用版本的 Perl,但研究人员并不爱用。

不过,用它来编写程序还是挺有趣的,所以我选择用 Python 来实现我的论文中的几个实验,其中需要用到一些线性代数。起初,我写了一些 Pascal 风格的循环嵌套程序,感觉不是很有意思,但随着我对 Python 研究得越来越深入,我发现事情开始变得有趣起来。

某一刻,我感觉这门语言不仅仅是写循环嵌套,于是开始深入研究这个语言,而不仅限于我的论文内容——这可能是我做过的最好的错误决定。

Python 以其友好的学习曲线而闻名,但这也可能会带来问题。你可以用 Python 写 Pascal 程序,可以用 Python 写 C++ 程序,还可以用 Python 写 Lisp 程序,但只有当你用 Python 的方式写 Python 程序时,才能真正发挥它的优势。

好在,就算仅掌握 Python 的基本知识可能会错过精彩部分,对用户来说体验依然很好。

为了说明这一点,我选择了线性代数领域。按理说,Python 有许多优秀的库可以处理线性代数,所以不必重新实现下文中提到的任何内容。而我选择线性代数,是想要证明在 Python 中,以如此少量的代码来表达丰富含义的语法是完全可行的。 (免责声明:为了保证可读性,部分代码示例并没有严格限制在一行内,示例中的所有缩进完全是为了提升阅读体验。)

1、列表解析

列表解析是 Python 单行代码的精髓,这是一种描述列表转换的特殊语法。比方说,如果我们要用一个向量乘以一个标量,在类似 Pascal 的 Python 代码中,可能会这样写:

def scaled(A, x):    B = list()    for i in range(len(A)):        B.append( A[i] * x )    return B

怎么说呢,这种写法没啥问题,也有它的好处,例如总能找到一行放置断点,但这种写法有些“冗长”。在 Python 中,你可以这样简单地写:

def scaled(A, x): return [ai*x for ai in A]

然后它便会这样工作:

List comprehension
[1.0, 4.0, 3.0]* 2.0 [, ...
Step 1
[1.0, 4.0, 3.0] * 2.0 [2.0, ...
Step 2
[1.0, 4.0, 3.0] * 2.0 [2.0, 8.0, ...
Step 3
[1.0, 4.0, 3.0] * 2.0 [2.0, 8.0, 6.0]

不过,列表解析不是 Python 所独有的,Haskell 和 Clojure 也有类似功能,甚至 C# 的 LINQ 也提供了针对范围的特殊语法。而鲜少有人知道的是,Python 还支持字典解析和元组解析。

>>> [2*v for v in [1.0, 2.0, 3.0]][2.0, 4.0, 6.0]
>>> {k:2*v for k, v in {0:1.0, 1:4.0, 2:3.0}.items()}{0: 2.0, 1: 8.0, 2: 6.0}
>>> tuple(2*v for v in (1.0, 4.0, 3.0))(2.0, 8.0, 6.0)

2、列表压缩

列表压缩可将多个可迭代对象作为一个整体进行迭代,并将所有对象都转化为一个元组列表。虽然标题上写的是“列表”,但它也同样适用于元组、字典、生成器等任何可迭代的对象。

>>> A = [1, 2, 3]>>> B = ('a', 'b', 'c')>>> C = {1:'a', 2:'b', 3:'c'}>>> D = xrange(123)>>> zip(A, B, C, D)
[(1, 'a', 1, 0), (2, 'b', 2, 1), (3, 'c', 3, 2)]

同样地,你可以用一行代码实现向量加法。

def sum_of(A, B): return [ai+bi for (ai, bi) in zip(A, B)]

对于两组列表,这就能像拉链一样将数据压缩在一起。

List zipping
A = [1.0, 4.0, 3.0] B = [7.0, 3.0, 1.0]zip(A, B) = [...
Step 1
A = [1.0, 4.0, 3.0] B = [7.0, 3.0, 1.0]zip(A, B) = [(1.0, 7.0), ...
Step 2
A = [1.0, 4.0, 3.0] B = [7.0, 3.0, 1.0]zip(A, B) = [(1.0, 7.0), (4.0, 3.0), ...
Step 3
A = [1.0, 4.0, 3.0] B = [7.0, 3.0, 1.0]zip(A, B) = [(1.0, 7.0), (4.0, 3.0), (3.0, 1.0)]

3、求和函数

求和函数可以进行简单地求和操作。你可以通过累积向量元素的乘积,用一行代码实现向量点积。

def dot_of(A, B): return sum([ai*bi for (ai, bi) in zip(A, B)])

不过,有时简单的求和并不够用。比如在处理浮点数时,你可能会遇到一些恼人的小误差。为了让你更方便,Python 提供了另一个求和函数,它可以对部分求和进行操作,从而获得更精确的输出。

>>> [0.1]*10[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
>>> sum([0.1]*10)0.9999999999999999
>>> import math>>> math.fsum([0.1]*10)1.0

4、条件表达式

条件表达式是 Python 中的三元运算符。它们是依赖于条件的表达式,通常也是简单而纯粹的表达式。我们可以用条件表达式来制作一个 单位矩阵

def




    
 identity(n):    return [[1.0 if i==j else 0.0            for j in range(n)]        for i in range(n)]

这实际上是一个带有条件表达式的嵌套列表解析,结果如下:

Conditional expression: 0 == 0
j = 0[[1.0, ... i = 0
Conditional expression: 1 != 0
j = 1[[1.0, 0.0, ... i = 0
Conditional expression: 2 != 0
j = 2[[1.0, 0.0, 0.0], ... i = 0
Conditional expression: 0 != 1
j = 0[[1.0, 0.0, 0.0], [0.0, ... i = 1
Conditional expression: 1 == 1
j = 1[[1.0, 0.0, 0.0], [0.0, 1.0, ... i = 1
Conditional expression: 2 != 1
j = 2[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], ... i = 1
Conditional expression: 0 != 2
j = 0[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, ... i = 2
Conditional expression: 1 != 2
j = 1[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, ... i = 2
Conditional expression: 2 == 2
j = 2[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] i = 2

条件表达式在简洁性和表达力方面非常出色,但由于它们是表达式,所以不容易添加副作用。但也并不是说不可能,只是需要一些技巧。在 Python 中,元组计算会从左到右依次计算每个元组元素,所以当你需要在表达式中添加更多内容时,只需使用元组即可。

def identity(n):    return [[1.0 if i==j else (0.0,                               sys.stdout.write("i != j\n")                              )[0]            for j in range(n)]        for i in range(n)]

你也不能在表达式中使用 print,因为它们不应该有副作用,但可以使用 sys.stdout.write。







请到「今天看啥」查看全文