以下是您可以尝试的一些想法,这些想法可能有助于快速消除非六次方。对于实际的六次方,您最终仍然需要计算六次方。
检查小箱子
如果您给出的数字有合理的小概率(例如,少于 12 位),您可以建立一个小案例表并进行检查。小于 10**12 的六次方只有 100 个。如果你的输入总是更大,那么这个测试的价值就很小,但它仍然是一个非常便宜的测试。
消除小素数
任何小的质因数都必须以 6 的倍数出现。为避免过多的试除,您可以将一些小因数捆绑在一起。
例如,2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 = 223092870,它足够小以适应 Python 中的单个 30 位肢体,因此使用该模数的单个模运算应该很快。
所以给定一个测试号n,计算g = gcd(n, 223092870),如果结果不是1,检查n 是否能被g ** 6 整除。如果不是,n 不是六次方,你就完了。如果n 是能被g**6整除,重复n // g**6。
检查模124488的值(例如)
如果您执行了上一步,那么此时您的值不能被任何小于25 的素数整除。现在您可以使用精心选择的模数进行模数测试:例如,与124488 = 8 * 9 * 7 * 13 * 19 相对质数的任何六次方都与[1, 15625, 19657, 28729, 48385, 111385] 模数124488 的六个值之一全等。可以使用更大的模数,但代价是必须检查更多可能的残基。
检查是否为正方形
任何六次方都必须是正方形。由于 Python(至少 Python >= 3.8)有一个相当快的内置整数平方根函数,因此在计算完整的六次方根之前检查该值是否为平方是有效的。 (如果它是一个平方并且你已经计算了平方根,现在你只需要提取一个立方根而不是六次根。)
使用浮点运算
如果输入不是太大,比如 90 位或更小,并且它是六次方,那么浮点运算就有合理的机会准确地找到六次方。但是,Python 不保证幂运算的准确性,因此值得进行一些额外的检查以确保结果在预期范围内。对于较大的输入,浮点运算获得正确结果的机会较小。 (2**53 + 1)**6 的第六个根不能完全表示为 Python 浮点数(合理假设 Python 的 float 类型与 IEEE 754 binary64 格式匹配),一旦 n 超过 308 位左右,它就太大而无法容纳无论如何都要变成一个浮点数。
使用整数运算
一旦你用尽了所有便宜的技巧,你别无选择,只能计算六次方的底,然后将其与原始数字的六次方进行比较。
这里有一些 Python 代码,将上面列出的所有技巧组合在一起。您应该针对您的特定用例制定自己的时间安排,并选择哪些技巧值得保留,哪些应该调整或丢弃。技巧的顺序也很重要。
from math import gcd, isqrt
# Sixth powers smaller than 10**12.
SMALL_SIXTH_POWERS = {n**6 for n in range(100)}
def is_sixth_power(n):
"""
Determine whether a positive integer n is a sixth power.
Returns True if n is a sixth power, and False otherwise.
"""
# Sanity check (redundant with the small cases check)
if n <= 0:
return n == 0
# Check small cases
if n < 10**12:
return n in SMALL_SIXTH_POWERS
# Try a floating-point check if there's a realistic chance of it working
if n < 10**90:
s = round(n ** (1/6.))
if n == s**6:
return True
elif (s - 1) ** 6 < n < (s + 1)**6:
return False
# No conclusive result; fall through to the next test.
# Eliminate small primes
while True:
g = gcd(n, 223092870)
if g == 1:
break
n, r = divmod(n, g**6)
if r:
return False
# Check modulo small primes (requires that
# n is relatively prime to 124488)
if n % 124488 not in {1, 15625, 19657, 28729, 48385, 111385}:
return False
# Find the square root using math.isqrt, throw out non-squares
s = isqrt(n)
if s**2 != n:
return False
# Compute the floor of the cube root of s
# (which is the same as the floor of the sixth root of n).
# Code stolen from https://stackoverflow.com/a/35276426/270986
a = 1 << (s.bit_length() - 1) // 3 + 1
while True:
d = s//a**2
if a <= d:
return a**3 == s
a = (2*a + d)//3