如何快速求二进制对数? (最多 O(1))
有没有非常快速的方法来找到整数的二进制对数?例如,给定一个数字 x=52656145834278593348959013841835216159447547700274555627155488768这样的算法必须找到y=log(x,2),即215。x总是2的幂。
问题似乎很简单。所需要的只是找到最高有效 1 位的位置。有一个著名的方法FloorLog,但它不是很快,特别是对于很长的多字整数。
最快的方法是什么?
Is there any very fast method to find a binary logarithm of an integer number? For example, given a number
x=52656145834278593348959013841835216159447547700274555627155488768 such algorithm must find y=log(x,2) which is 215. x is always a power of 2.
The problem seems to be really simple. All what is required is to find the position of the most significant 1 bit. There is a well-known method FloorLog, but it is not very fast especially for the very long multi-words integers.
What is the fastest method?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
答案取决于实现或语言。任何实现都可以将有效位数与数据一起存储,因为它通常很有用。如果必须计算,则找到最高有效字/分支以及该字中的最高有效位。
The answer is implementation or language dependent. Any implementation can store the number of significant bits along with the data, as it is often useful. If it must be calculated, then find the most significant word/limb and the most significant bit in that word.
如果您使用固定宽度整数,那么其他答案已经很好地涵盖了您。
如果您使用任意大的整数,例如 Python 中的
int
或 Java 中的BigInteger
,那么您可以利用它们的可变大小表示使用底层数组的事实,因此可以使用底层数组的长度在 O(1) 时间内轻松快速地计算以 2 为底的对数。 2 的幂的以 2 为底的对数只是比表示该数字所需的位数少 1。因此,当
n
是 2 的整数幂时:n.bit_length() - 1
(文档)。n.bitLength() - 1
(文档)。If you're using fixed-width integers then the other answers already have you pretty-well covered.
If you're using arbitrarily large integers, like
int
in Python orBigInteger
in Java, then you can take advantage of the fact that their variable-size representation uses an underlying array, so the base-2 logarithm can be computed easily and quickly in O(1) time using the length of the underlying array. The base-2 logarithm of a power of 2 is simply one less than the number of bits required to represent the number.So when
n
is an integer power of 2:n.bit_length() - 1
(docs).n.bitLength() - 1
(docs).我想到的最佳选择是使用二分搜索的 O(log(logn)) 方法。以下是 64 位 (
<= 2^63 - 1
) 数字(在 C++ 中)的示例:该算法基本上会向我提供最高数字 res,例如
(2 ^res - 1 & num) == 0
。当然,对于任何数字,您都可以用类似的方法来计算:请注意,此方法依赖于“位移”操作或多或少为 O(1) 的事实。如果不是这种情况,则必须预先计算 2 的所有幂或
2^2^i
形式的数字 (2^1, 2^2, 2^4, 2 ^8 等)并进行一些乘法(在本例中不是 O(1))。The best option on top of my head would be a O(log(logn)) approach, by using binary search. Here is an example for a 64-bit (
<= 2^63 - 1
) number (in C++):This algorithm will basically profide me with the highest number res such as
(2^res - 1 & num) == 0
. Of course, for any number, you can work it out in a similar matter:Note that this method relies on the fact that the "bitshift" operation is more or less O(1). If this is not the case, you would have to precompute either all the powers of 2, or the numbers of form
2^2^i
(2^1, 2^2, 2^4, 2^8, etc.) and do some multiplications(which in this case aren't O(1)) anymore.您可以预先创建一个对数数组。这将找到最大 log(N) 的对数值:
数组 naj 是您的对数值。其中 naj[k] = log(k)。
日志基于两个。
You can create an array of logarithms beforehand. This will find logarithmic values up to log(N):
The array naj is your logarithmic values. Where naj[k] = log(k).
Log is based on two.
这使用二分搜索来查找最接近的 2 的幂。
This uses binary search for finding the closest power of 2.
OP中的示例是一个65个字符的整数字符串,它不能用INT64甚至INT128表示。通过将其转换为双精度数,仍然可以很容易地从该字符串中获取 Log(2,x)。这至少可以让您轻松访问 2^1023 以内的整数。
下面您可以找到某种形式的伪代码
注意:
快速示例代码:如果您有
awk
,您可以快速测试此算法。以下代码创建前 300 个 2 的幂:
下面读取输入并执行上述算法:
因此,以下 bash 命令是创建从 0 到 299 的连续数字列表的复杂方法:
The example in the OP is an integer string of 65 characters, which is not representable by a INT64 or even INT128. It is still very easy to get the Log(2,x) from this string by converting it to a double-precision number. This at least gives you easy access to integers upto 2^1023.
Below you find some form of pseudocode
Note:
x=to_float(string)
Quick example code: If you have
awk
you can quickly test this algorithm.The following code creates the first 300 powers of two:
The following reads the input and does the above algorithm:
So the following bash-command is a convoluted way to create a consecutive list of numbers from 0 to 299:
快速破解:大多数浮点数表示形式会自动标准化值,这意味着它们可以有效地执行循环Christoffer Hammarström 在硬件中提到。因此,只要数字在 FP 表示的指数范围内,只需从整数转换为 FP 并提取指数就可以解决问题! (在您的情况下,您的整数输入需要多个机器字,因此在转换中需要执行多个“移位”。)
A quick hack: Most floating-point number representations automatically normalise values, meaning that they effectively perform the loop Christoffer Hammarström mentioned in hardware. So simply converting from an integer to FP and extracting the exponent should do the trick, provided the numbers are within the FP representation's exponent range! (In your case, your integer input requires multiple machine words, so multiple "shifts" will need to be performed in the conversion.)
如果整数存储在
uint32_t a[]
中,那么我明显的解决方案如下:对
a[]
运行线性搜索以找到最高的a[]
中的非零uint32_t
值a[i]
(使用uint64_t
进行该搜索测试如果您的计算机具有本机uint64_t
支持)应用位旋转黑客来查找二进制日志您在步骤 1 中找到的
uint32_t
值a[i]
的b
。评估
32*i+b
。If the integers are stored in a
uint32_t a[]
, then my obvious solution would be as follows:Run a linear search over
a[]
to find the highest-valued non-zerouint32_t
valuea[i]
ina[]
(test usinguint64_t
for that search if your machine has nativeuint64_t
support)Apply the bit twiddling hacks to find the binary log
b
of theuint32_t
valuea[i]
you found in step 1.Evaluate
32*i+b
.