测试一个大整数是否为2的幂

时间:2017-03-23 00:02:21

标签: algorithm biginteger

给定一个整数(以二进制形式存储),如何快速测试它是否为2的幂,即对于整数指数k等于2?

一种简单但相当慢的方法是连续除以2直到数字变为2或者存在非零余数。不幸的是,我们需要执行尽可能多的分区,因为我们的号码中有数字。

对于小整数,有许多解决方案,包括位计数等。我对任意位数的整数的快速解决方案感兴趣。例如。我们可以通过一些快速整数除以2或其他技巧加速上述方法吗?

4 个答案:

答案 0 :(得分:10)

我认为最快的方法仍然是检查这些位。假设你的数字是x,在java中你可以x & (x - 1) == 0如果是真的那么它就是2的幂

对于BigInteger,您可以执行x.and(x.subtract(BigInteger.ONE)).equals(BigInteger.ZERO)

答案 1 :(得分:2)

因为您只需要验证整个大整数的位数是否为1,我想最快的方法可能是将大整数存储为uint32_t或更好{{1}的数组所以你可以使用popcnt x86指令。

还有一些天真的方法,如使用SSE3指令uint64_t所描述的here,甚至other approaches仍然依赖于PSHUFB,但手写程序集。

当然这可能是矫枉过正,因为你不需要人口数量,但是你需要知道它是否正好是1但是它可能值得尝试。

与此类优化问题一样,猜测只是无关紧要,唯一的选择是测试不同的方法,看看哪种方法更合适。

答案 2 :(得分:2)

从大数字中减去<html> <head> </head> <body> <?php $con = mysql_connect('localhost', 'root', ''); if (!$con){ die("Can not connect: " . mysql_error()); } mysql_select_db("form_process", $con); $sql = "SELECT * FROM `form_submissions`"; $myData = mysql_query($sql,$con); echo "<table border=1> <tr> <th>First Name</th> <th>Last Name</th> <th>Phone Number</th> <th>Class interested in</th> </tr>"; while($row= mysql_fetch_array($result)){ echo "<tr>"; echo "<td>" . $record['First'] . "</td>"; echo "<td>" . $record['Last'] . "</td>"; echo "<td>" . $record['Phone'] . "</td>"; echo "<td>" . $record['Class'] . "</td>"; echo "</tr>"; } echo "</table>"; mysqli_close(); ?> </body> </html> 是不理想的,因为在CPU级别会有多个操作,包括大数字的进位,因为计算不适合累加器。

对于CPU上的计算,最快的方法是:

  1. 对于每个累加器大小的字节集(64位数字上的8个字节,1 1指令)
  2. 检查数字是否为零(1 LOAD指令)
  3. 如果不为零,请将号码复制到另一个寄存器(1 CMP指令)
  4. 从新注册表中的数字中减去JNZ(1 1指令)
  5. 和两个寄存器(1 SUB指令)
  6. 如果该值为零,则继续检查其余集合是否为纯零。 (1 AND指令)
  7. 要进行优化,您需要将数字拆分为这些集合并进行比较。

    上述方法的关键优势在于:通过步骤(2),我们摆脱了大的减法和携带。步骤(4)中的减法将只接受一条指令,并且与零的比较也将采用一条指令。所以它应该加快操作。

答案 3 :(得分:1)

对于非常大的数字,这个测试基本上等于将所有字节比较为零,除了其中一个必须保持2的幂。我们可以专注于此测试为零,而有效地检查非零则不那么重要。

最简单的形式是测试32或64位整数(即4或8字节)为零(取决于寄存器大小)。

通过SIMD介绍可以更快地实现。在SSE2或AVX下,您可以获取16或32个字节,与零(_mm256_cmpeq_epi8 / _mm256_cmpeq_epi8与预清除寄存器进行比较)并使用_mm_movemask_epi8或{打包到16或32位{1}}。然后,将得到的标量(short或int)与所有标量进行比较。

[不幸的是,在AVX512中似乎没有相应的_mm256_movemask_epi8,这样一次就可以处理64个字节。]

如果您发现一组字节保存非零值,您可以使用256个条目的查找表逐个测试它们。

使用SIMD方法,您甚至可以通过另一个256个条目的查找表来提高速度,这些条目告诉一个字节中非零位的索引,或者当有几个非零位时保留值。使用此查找表两次或四次,您将知道16或32位中的哪一位对应于非零字节(并且还检测到几个非零字节)。

利用公式_mm512_movemask_epi8的解决方案也是可能的。

正如已经说过的,这些微观优化可能毫无价值,除非你的大部分数字不是2的幂。