【问题标题】:Is this number a power of two?这个数字是二的幂吗?
【发布时间】:2016-10-12 17:07:58
【问题描述】:

我有一个数字(以 10 为底)表示为最多 10^6 位的字符串。我想检查这个数字是否是 2 的幂。我能想到的一件事是对指数进行二进制搜索并使用 FFT 和快速取幂算法,但它的代码相当长且复杂。让 n 表示输入的长度(即输入中的小数位数)。作为 n 的函数,解决这个问题最有效的算法是什么?

【问题讨论】:

  • 您使用的是哪种语言?
  • C++,但是自己写算术没问题。
  • 如果复杂度没有问题,那么您可以简单地遍历 String 并将其除以 2,最后如果剩下 1,则 number 是 2 的幂数。
  • 有很多必要(但不充分)的条件很容易检查。例如,测试最后一位是否为偶数将消除 50% 的数字。
  • 好吧,正如您所见,我的解决方案包含快速傅里叶变换和其他一些复杂的算法,因此复杂性是一个问题。这不是一个基本的编程问题。

标签: algorithm binary


【解决方案1】:

对于任何给定大小的十进制数,都有 2 的 2 次方或 3 次方,很容易猜到它们是什么,因为十进制数的大小很好地近似于它的以 10 为底的对数,而且你只需乘以适当的常数 (log210) 即可计算以 2 为底的对数。所以二分查找是低效且不必要的。

一旦你有了一个大约 300 万的试验指数,你就可以使用平方指数算法和大约 22 个 bugnum 十进制乘法。 (最多 21 次加倍,但这些相对容易。)

根据您执行此检查的频率,您可能希望投资于快速的 bignum 代码。但如果不频繁,简单的乘法应该没问题。

如果您不希望数字是 2 的幂,您可以先进行快速计算 mod 109 以查看最后 9 位是否匹配。这将消除除一小部分随机数之外的所有随机数。或者,对于更快但稍弱的过滤器,使用 64 位算术检查最后 20 位数字是否可被 220 整除而不是 10。

【讨论】:

    【解决方案2】:

    这是一个简单的概率解决方案。

    假设您的电话号码是n,我们要查找kn = 2^k。显然,k = log2(n) = log10(n) * log2(10)。我们可以估计log10(n) ~ len(n) 并找到带有小错误的k' = len(n) * log2(10)(比如|k - k'| <= 5,我没有检查,但这应该足够了)。可能您在任何可以想到的解决方案中都需要这部分,其他答案中也提到过。

    现在让我们检查一下n = 2^k 是否有一些已知的k。选择从 2 到 k^2 的随机素数 P。如果余数不相等,则k 绝对不匹配。但是如果他们是平等的呢?我声称误报率受2 log(k)/k 的限制。

    为什么会这样?因为如果n = 2^k (mod P)P 除以D = n-2^k。数字 D 的长度约为 k(因为由于第一部分,n2^k 具有相似的大小),因此不能有超过 k 的不同素数除数。大约有k^2 / log(k^2) 质数小于k^2,因此您随机选择D 的质数除数的概率小于k / (k^2 / log(k^2)) = 2 log(k) / k

    在实践中,最高 10^9(甚至最高 log(n))的素数就足够了,但您必须进行更深入的分析才能证明概率。

    此解决方案根本不需要任何长算法,所有计算都可以在 64 位整数中进行。

    附:为了从 1 到 T 中选择一个随机素数,您可以使用以下逻辑:从 1 到 T 中选择一个随机数并将其递增 1 直到它为素数。在这种情况下,素数上的分布是不均匀的,前面的分析也不完全正确,但它也可以适应这种随机。

    【讨论】:

    • (注意添加关于 (2^p)±1 的想法?)((两个错字:smalarythmetics))
    • @greybeard 2^p ± 1 是什么意思?如果你想检查 n = 2^p ± 1,你可以用完全相同的方式来做——计算 2^p ± 1 的余数并检查它是否与 N 的余数匹配。还有什么不清楚吗?跨度>
    • 我太懒了,如果能够分析余数模梅森素数是否高于或低于平均水平,以识别使用非二的幂的基数表示的数字中的非二的幂.想一想,mod 10ⁿ+1 可能与这个问题更相关,同样易于计算,无需乘法或(显式)除法,并且无需基础转换即可适用。
    • @greybeard 我不明白为什么计算大数模 10^n+1 比模任何其他数字更容易。在线性(从 n 的长度)时间内计算 n%P 很容易:只需从最高有效位开始逐位计算余数(给定 x%P,很容易找到 (x*10+digit )%P)。而且我看不到任何更简单的方法来计算 n%(10^k+1)。
    • @greybeard 更多,我是在竞争激烈的编程比赛中长大的,总是想到最坏的情况。如果您对某个固定数字(例如 10^18)取余数,那么构建这样一个测试用例就很容易导致您的方法失败。
    【解决方案3】:

    我不确定它是否容易申请,但我会通过以下方式进行:

    1) 以二进制显示数字。现在如果这个数字是 2 的幂,它看起来像:

    1000000....

    只有一个1,其余的是0。检查这个数字很容易。现在的问题是如何存储数字。例如,它可能有前导零,这将加强对 1 的搜索:

    ...000010000....

    如果只有少量的前导零,只需从左到右搜索。如果零的数量未知,我们将不得不...

    2) 对1的二分查找:

    2a) 从中间切开。

    2b) 如果两者都为 0 或都不为 0(希望您可以在合理的时间内检查一个数字是否为零),则停止并返回 false。 (假 = 不是 2 的幂)

    否则继续非零部分。

    如果非零部分 = 1 则停止并返回 true。


    估计:如果数字是 n 位(十进制),那么它的 2^n 位二进制。 二进制搜索采用O(log t),并且由于 t = 2^n,log t = n。因此算法应该采用O(n)


    假设:

    1)您可以访问数字的二进制视图。

    2) 您可以在合理的时间内将一个数字与零进行比较。

    【讨论】:

    • 但是我说我有这个数字是十进制的,所以要让它变成二进制我应该转换我的数字,这不会比仅仅除以 2 而数字!= 1
    • 你的意思是你不使用标准计算机上的算法,已经以二进制值存储数字?因为如果你这样做,也许你不必转换,而是直接从计算机内存中读取......
    猜你喜欢
    • 1970-01-01
    • 2013-03-04
    • 2021-09-15
    • 2013-11-16
    • 1970-01-01
    • 2015-09-25
    • 1970-01-01
    • 2017-01-09
    • 1970-01-01
    相关资源
    最近更新 更多