大十进制整数中的位数

时间:2017-03-22 17:02:32

标签: algorithm biginteger bits logarithm

给定十进制整数,如何计算bit-length,即其二进制表示的位数?

示例: bitlength("590295810358705712624") == 70

算术表达式为:bitlength = ⌊log₂(n)⌋ + 1

对于小整数,此表达式可以转换为标准库调用。但是具有任意数字位数的大整数呢?

我们可以从一个或两个前导数字计算log₂的非常接近的估计值:

log₂(8192) ≥ log₂(8100) = log₂(81) + log₂(10) * 2 = 12.98...

将其插入到上面的算术表达式中,我们得到了比特长度的非常好的下界。但在某些情况下,为了获得准确的结果,我们必须检查更多的数字,可能是最不重要的数字:

bitlength("1125899906842623") == 50
bitlength("1125899906842624") == 51

有关如何在所有情况下准确有效地计算位长度的任何建议?

4 个答案:

答案 0 :(得分:2)

计算近似对数的下限和上限并在两者相等时接受结果,我们可以在一步中完成超过90%的所有情况的计算。在所有其他情况下,我们将输入除以2并递归重复。

给定n位数的输入并假设除以2在O(n)中,运行时的复杂性如下:

  • O(1)在最好的情况下
  • 平均O(n)
  • O(n²)在最坏的情况下

我仍然认为可以改善平均和最差情况下的运行时复杂性。

以下是所述算法的示例性实现:

// Divide n by 2:
function half(n) {
  let shrink = n[0] > 1 ? 0 : 1;
  let result = new Array(n.length - shrink);
  let carry = 0.5 * shrink;
  for (let i = 0; i < result.length; ++i) {
    let d = n[i + shrink] * 0.5 + carry * 10;
    carry = d % 1;
    result[i] = Math.floor(d);
  }
  return result;
}

// Compute bit-length of n:
function bitlength(n) {
  if (n.length < 2) return Math.floor(Math.log2(n[0]            )) + 1;
  if (n.length < 3) return Math.floor(Math.log2(n[0] * 10 + n[1])) + 1;

  let lower = Math.floor(Math.log2(n[0] * 10 + n[1]    ) + Math.log2(10) * (n.length - 2));
  let upper = Math.floor(Math.log2(n[0] * 10 + n[1] + 1) + Math.log2(10) * (n.length - 2));
  if (lower === upper) return lower + 1;

  return bitlength(half(n)) + 1;
}

// Example:
console.log(bitlength([5,9,0,2,9,5,8,1,0,3,5,8,7,0,5,7,1,2,6,2,4]));

可以优化上述实施方案,例如使用查找表。但是,为了提高运行时的复杂性,我们需要提出一个更好的解决方案,将n除以2.随意发表评论。

答案 1 :(得分:0)

我曾经写过如何将作为字符串给出的任意大小的数字转换为十六进制数字字符串的答案: https://stackoverflow.com/a/2653006/64121

您可以非常轻松地将其调整为二进制字符串并获取其长度(或直接计算长度)。一种更为懒惰的方法是保持十六进制版本并计算所有数字,但第一个数字为四个二进制数字,最后检查第一个数字的确切位长。

答案 2 :(得分:0)

这就是我在BigInteger实现中的方法:

result = (number_of_limbs - 1) * 32 + Integer.bitlength(top_limb);

你应该知道四肢的数量。在我的例子中,它们是32位无符号整数。只需要处理上肢的位,但下面的简单算法将为您做到这一点:

if (n > 65535)
{
    result += 16;
    n >>= 16;
}
if (n > 255)
{
    result += 8;
    n >>= 8;
}
etc...

你必须调整负数,但这很简单:如果上肢是2的幂,则将你找到的数字减1。

答案 3 :(得分:0)

如果给定的BigDecimal n仅需要大约位数,请使用unscaledValue()访问表示为BigInteger的数字(不带比例因子),然后访问其关联的字节数组的长度:

int bytes = n.unscaledValue().toByteArray().length;
int bits = bytes * 8;

这将产生所需位数的上限。