测试一个大整数的快速方法

Fast method for testing a bit of a large int

在一个处理非常大的整数n(十万位到几百万位)的数论算法中,我需要测试jth少量。这些工作之一:

if 1<<j & n != 0 :
    # bit j of n is set

if n>>j & 1 != 0 :
    # bit j of n is set

但测试的持续时间随 n.bit_length() 线性增长(j 的一半)。否则说每 big-O notation,时间是 O(log(n)),当它可能是 O(1).

是否有 O(1) 习语来测试 Python 3(.8) 中的 int,就像我们在 GMP 中有 mpz_tstbit()

如果没有,Python 建议的下拉框在哪里?


每个 comment 的加法:n.bit_length() 最多 1<<24j < n.bit_length()j>=0

如果你的 "j" 是固定的,你可以用字面形式拼写数字而不是使用 "j" - Python 的编译器然后会将“1 << j”记录为字面量,你只有一个操作而不是两个。(也就是说,如果 "j" 不是可变的,并且总是,比如说“10204”,你应该写成 1 << 10204

就是说,我认为您将此算法 运行 想象为 "calmly pushing the 1 thousands of bits to the left, one by one" - 事实并非如此。

大整数的算法可能会优化“1 << j”整数的创建 - 虽然 & n 的结果将是 "linear",但它仍然是非常快的操作。

总而言之,如果在您 运行ning 之后并且您的应用程序配置文件显示此操作速度变慢,则有大整数库可以胜过 Python的本机整数超过一个数量级。

过去,我使用过 GMP2 库 - Python 可用作 gmpy2,并取得了不错的效果。\

至于你的问题的具体细节,关于通过为位测试编写其他表达式来尝试加快速度:这绝对是错误的方法 -

如果 Python 中的数字 太慢 并且速度更快的数字库根本不支持位测试,你可能会推出你自己的整数类型,将错误编号存储在字节数组中,每个字节 8 位,并为这些数字编写自定义 "bit compare" 方法。

相对于使用正常的按位测试 &,您将获得的加速是您的函数会提前知道它必须匹配其中一个操作数的一位,而不必搜索另一个操作数中的其他“1”位 - 因此操作将是 O(1).

但我敢打赌,您从中获得的加速太少了 - 请记住 "premature optimization is the root of all evil"。

更新:gmp 构建 1 << j number 的速度并不快:


In [22]: a = bmpy2.numer(1); b = gmpy2.numer(10_000_000)                                                                                                   

In [23]: %timeit a << b                                                                                                                
25.8 µs ± 508 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [24]: %timeit 1 << 10_000_000                                                                                                       
27.2 µs ± 239 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)


免责声明:我维护 gmpy2.

gmpy2 以几种不同的方式支持位访问。 gmpy2.bit_test(n,j) 将测试 n 的第 j 位。 n 可以是 Python 整数或 gmpy2 整数类型。

>>> gmpy2.bit_test(78,2)
True

gmpy2.mpz整数类型支持bit_test方法。还支持其他方法。

>>> a=gmpy2.mpz(123456)
>>> a.bit_test(27)
False

gmpy2.xmpz是一个可变整数类型,支持位访问,包括设置位和访问位片。

>>> a=gmpy2.xmpz(123456)
>>> a[27]
0
>>> a[27]=1
>>> a[27]
1
>>> a[27:30]
mpz(1)
>>> a[27:30] = -1
>>> a[27:30]
mpz(7)

您可以在正常的数学运算中使用 xmpz 个整数。如果您只使用即时操作(+=、*= 等),那么 xmpz 对象将就地更新。

>>> a
xmpz(939647552)
>>> b=a
>>> a+=9999
>>> a
xmpz(939657551)
>>> b
xmpz(939657551)

xmpz 整数有时有点奇怪,但对于直接位访问它们通常非常快。