秒数和小时换算的编程实现算法优化

146 阅读2分钟

假设你有一个秒数n,你想把它转换成小时和秒。如果你用n除以3600,商就是小时数,余下的部分就是秒数。

例如,假设n= 8072022。这里是用Python进行计算的明显方法。

    def div3600(n): return (n // 3600, n % 3600)

如果我们传入872022,这将返回(2242,822),因此872022秒等于242小时822秒。

然而,正如通常的情况一样,最明显的方法并不是最快的。一般来说,除法要比乘法和加法慢得多,尽管除以2的幂是非常快的。因此,如果我们有办法把问题改成需要除以2的幂,而不是除以3600,就可以加快我们的计算速度。

下面是另一个Python程序,它以非常不同的方式计算同一个结果。

    def div3600(n):
        assert(0 <= n < 2255761) 
        t = 1193047*n 
        q = t >> 32
        r = 3600*(t & 4294967295) >> 32
        assert(q == n // 3600)
        assert(r == n % 3600)
        return (q, r)

这个算法并不是对所有的n都有效,因此在函数的顶部有一个断言语句,验证n是否在范围内。请注意,这里没有明确的除法语句。有一个位移,相当于除以232,有一个位和,相当于除以232后取余数。

Python代码是一个原型。如果我们如此关心性能而考虑使用这种算法,我们就不应该使用Python。这里有一个C语言的实现,会更现实一些。

    void div3600(long n, long* q, long* r) {
        long t = 1193047*n;
        *q = t >> 32;
        *r = 3600*(t & 4294967295) >> 32;
    }

上面介绍的算法是[1]中的一个例子。在那篇论文中,作者研究了以下形式的整数函数

(αr + β) // δ

(αr + β) % δ

以及它们在加快软件速度方面的应用[2],特别是日历算法。作者包括了几种编程语言的基准,验证了他们的算法确实比直接方法快得多。

当然,大多数应用都会很好地使用直接计算法,这显然是正确的。但是,一个大规模的、反复进行这类计算的应用程序可能会受益于一个混淆的、但更有效的算法。

[1] Cassio Neri和Lorenz Schneider.Euclidean Affine Functions and Applications to Calendar Algorithms.Arxiv2102.06959v1.

[2] 这里我使用了Python的整数除法符号// 。作者使用的是C语言符号,其中/ 是指商,因为上下文是整数参数。我更喜欢Python的符号,因为它更明确。