假设你有一个秒数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的符号,因为它更明确。