7 Oct

班门弄斧:Python的代码能有多简洁?

此文很水,高手略过...

Python以它的开发效率而闻名,优秀的开发效率自然意味着它能够用更少的代码实现更多的功能。那么,对于同一个问题,Python的代码能有多简洁?而我们怎么平衡开发效率和运行效率?笔者学了几个月Python,略懂一点,在此班门弄斧一翻。

在此,我们来编程计算
$$\sum_{n=0}^{10} n^2$$
这当然是一道非常简单的习题。按照一般思路,写出来的最自然的代码就是:

s=0
for i in range(11):
    s = s + i**2

print(s)

点击阅读全文...

12 Oct

集合的划分与贝尔数

集合上的一个等价关系决定了几何的一个划分,反之亦然,这直观上是不难理解的。但是,如果我要问一个有$n$个元素的有限集合,共有多少种不同的划分呢?以前感觉这也是一个很简单的问题,就没去细想,但前天抽象代数老师提到这是一个有相当难度的题目,于是研究了一下,发现里面大有文章。这里把我的研究过程简单分享一下,读者可以从中看到如何“从零到有”的过程。

以下假设有$n$个元素的有限集合为$\{1,2,\dots,n\}$,记它的划分数为$B(n)$。

前期:暴力计算

$n=3$的情况不难列出:
$$\begin{aligned}&\{\{1,2,3\}\},\{\{1,2\},\{3\}\},\{\{1,3\},\{2\}\},\\
&\{\{2,3\},\{1\}\},\{\{1\},\{2\},\{3\}\}\end{aligned}$$

点击阅读全文...

17 Oct

两百万素数之和与“电脑病”

原则上来讲,同样的算法,如果分别在Python和C++上实现,那么Python的速度肯定比不上C++的。但是Python还被称为“胶水语言”,它允许我们把主要计算的部分用C或C++等高效的语言编写好,然后它作为“粘合剂”把两者粘合在一起。正因为如此,Python才有了各种各样的扩展库,这些库中有不少是用C语言编写的。因此,我们在编写Python程序的时候,如果可以用这些现成的库,速度会快很多。本文就是用Numpy来改进之前的《两百万前素数之和与前两百万素数之和》的计算。

算法本身是没有变的,只是用了Numpy来处理数组计算,代码如下:

点击阅读全文...

6 May

变分自编码器(五):VAE + BN = 更好的VAE

本文我们继续之前的变分自编码器系列,分析一下如何防止NLP中的VAE模型出现“KL散度消失(KL Vanishing)”现象。本文受到参考文献是ACL 2020的论文《A Batch Normalized Inference Network Keeps the KL Vanishing Away》的启发,并自行做了进一步的完善。

值得一提的是,本文最后得到的方案还是颇为简洁的——只需往编码输出加入BN(Batch Normalization),然后加个简单的scale——但确实很有效,因此值得正在研究相关问题的读者一试。同时,相关结论也适用于一般的VAE模型(包括CV的),如果按照笔者的看法,它甚至可以作为VAE模型的“标配”。

最后,要提醒读者这算是一篇VAE的进阶论文,所以请读者对VAE有一定了解后再来阅读本文。

VAE简单回顾

这里我们简单回顾一下VAE模型,并且讨论一下VAE在NLP中所遇到的困难。关于VAE的更详细介绍,请读者参考笔者的旧作《变分自编码器(一):原来是这么一回事》《变分自编码器(二):从贝叶斯观点出发》等。

VAE的训练流程

VAE的训练流程大概可以图示为

VAE训练流程图示

VAE训练流程图示

点击阅读全文...

27 Oct

算符的艺术:差分、微分与伯努利数

两年前,笔者曾写过《算子与线性常微分方程》两篇,简单介绍了把线形常微分方程算符化,然后通过对算符求逆的方法求得常微分方程的通解。而在这篇文章中,笔者打算介绍关于算符类似的内容:差分算符、微分算符以及与之相关的伯努利数(Bernoulli数)。

我们记$D=\frac{d}{dx}$,那么$Df=\frac{df}{dx}$,同时定义$\Delta_t f(x)=f(x+t)-f(x)$,并且记$\Delta \equiv \Delta_1 =f(x+1)-f(x)$,这里我们研究的$f(x)$,都是具有良好性态的。我们知道,$f(x+t)$在$t=0$附近的泰勒展式为
$$\begin{aligned}f(x+t)&=f(x) + \frac{df(x)}{dx}t + \frac{1}{2!}\frac{d^2 f(x)}{dx^2}t^2 + \frac{1}{3!}\frac{d^3 f(x)}{dx^3}t^3 + \dots\\
&=\left(1+t\frac{d}{dx}+\frac{1}{2!}t^2\frac{d^2}{dx^2}+\dots\right)f(x)\\
&=\left(1+tD+\frac{1}{2!}t^2 D^2+\dots\right)f(x)\end{aligned}$$

点击阅读全文...

28 Oct

在Python中使用GMP(gmpy2)

之前笔者曾写过《初试在Python中使用PARI/GP》,简单介绍了一下在Python中调用PARI/GP的方法。PARI/GP是一个比较强大的数论库,“针对数论中的快速计算(大数分解,代数数论,椭圆曲线...)而设计”,它既可以被C/C++或Python之类的编程语言调用,而且它本身又是一种自成一体的脚本语言。而如果仅仅需要高精度的大数运算功能,那么GMP似乎更满足我们的需求。

了解C/C++的读者都会知道GMP(全称是GNU Multiple Precision Arithmetic Library,即GNU高精度算术运算库),它是一个开源的高精度运算库,其中不但有普通的整数、实数、浮点数的高精度运算,还有随机数生成,尤其是提供了非常完备的数论中的运算接口,比如Miller-Rabin素数测试算法、大素数生成、欧几里德算法、求域中元素的逆、Jacobi符号、legendre符号等[来源]。虽然在C/C++中调用GMP并不算复杂,但是如果能在以高开发效率著称的Python中使用GMP,那么无疑是一件快事。这正是本文要说的gmpy2

点击阅读全文...

12 Nov

特殊的通项公式:二次非线性递推

特殊的通项公式

对数学或编程感兴趣的读者,相信都已经很熟悉斐波那契数列了

0, 1, 1, 2, 3, 5, 8, 13, ...

它是由
$$a_{n+2}=a_{n+1}+a_n,\quad a_0=0,a_1=1$$
递推所得。读者或许已经见过它的通项公式
$$a_{n}=\frac{\sqrt{5}}{5} \cdot \left[\left(\frac{1 + \sqrt{5}}{2}\right)^{n} - \left(\frac{1 - \sqrt{5}}{2}\right)^{n}\right]$$
这里假设我们没有如此高的智商可以求出这个复杂的表达式出来,但是我们通过研究数列发现,这个数列越来越大时,相邻两项趋于一个常数,这个常数也就是(假设我们只发现了后面的数值,并没有前面的根式)
$$\beta=\frac{1 + \sqrt{5}}{2}=1.61803398\dots$$

点击阅读全文...

17 Nov

[转载] 做数学一定要是天才吗?

(译自 陶哲轩 博客, 译者 liuxiaochuang)
(英文原文:Does one have to be a genius to do maths?)

这个问题的回答是一个大写的:不!为了达到对数学有一个良好的,有意义的贡献的目的,人们必须要刻苦努力;学好自己的领域,掌握一些其他领域的知识和工具;多问问题;多与其他数学工作者交流;要对数学有个宏观的把握。当然,一定水平的才智,耐心的要求,以及心智上的成熟性是必须的。但是,数学工作者绝不需要什么神奇的“天才”的基因,什么天生的洞察能力;不需要什么超自然的能力使自己总有灵感去出人意料的解决难题。

大众对数学家的形象有一个错误的认识:这些人似乎都使孤单离群的(甚至有一点疯癫)天才。他们不去关注其他同行的工作,不按常规的方式思考。他们总是能够获得无法解释的灵感(或者经过痛苦的挣扎之后突然获得),然后在所有的专家都一筹莫展的时候,在某个重大的问题上取得了突破的进展。这样浪漫的形象真够吸引人的,可是至少在现代数学学科中,这样的人或事是基本没有的。在数学中,我们的确有很多惊人的结论,深刻的定理,但是那都是经过几年,几十年,甚至几个世纪的积累,在很多优秀的或者伟大的数学家的努力之下一点一点得到的。每次从一个层次到另一个层次的理解加深的确都很不平凡,有些甚至是非常的出人意料。但尽管如此,这些成就也无不例外的建立在前人工作的基础之上,并不是全新的。(例如, Wiles 解决费马最后定理的工作,或者Perelman 解决庞加莱猜想的工作。)

点击阅读全文...