【问题标题】:Check if a number is a perfect power of another number检查一个数字是否是另一个数字的完美幂
【发布时间】:2017-01-09 22:25:31
【问题描述】:

例如,243 是 3 的完美幂,因为 243=3^5。

我以前一直在使用(math.log(a) / math.log(b)).is_integer(),我认为它工作得很好,但后来我用上面的例子试了一下,由于浮点运算,它实际上返回 4.999999999999999。所以它只对非常小的数字是可靠的,我发现不到 100 个。

我想我可以使用循环来进行重复乘法...即将 i 设置为 3,然后是 9,然后是 27,然后是 81,然后是 243,这等于目标,所以我们知道这是一个完美的幂。如果它达到大于 243 的点,那么我们知道它不是一个完美的力量。但是我在一个循环中运行这个检查,所以这看起来效率很低。

那么有没有其他方法可以可靠地检查一个数字是否是另一个数字的完美幂?

【问题讨论】:

  • 关于 Y 在你的 XY 问题中:不要测试浮动是否相等,测试 closeness(如检查 math.log(a) / math.log(b) 是否 close到它的整数部分)
  • 请注意,我发布的链接涵盖了您想要检测一个数字是否是 any 其他数字的非平凡完美幂的情况,例如你不知道 b.
  • 您是否已经知道b=3,或者您是否也想找到它?

标签: python math


【解决方案1】:

试试:

b ** int(round(math.log(a, b))) == a

也就是说,只使用log()(注意有一个2-参数形式!)猜测整数幂,然后验证是否“有效”。

请注意,math.log() 会返回合理的结果,即使整数参数太大而无法表示为浮点数。另请注意,Python 中的整数 ** 是精确的,并且在内部使用了一种高效的算法(执行与指数中的位数成比例的乘法运算)。

这比重复除法简单且高效(通常)。

但是我正在回答您提出的问题 ;-) 如果您有其他问题,其他一些答案可能更合适。

【讨论】:

  • 可能值得注意的是,虽然这个解决方案对于大值来说并非完全可靠,但它取决于浮点精度,a 必须非常大才能成为问题。例如,使用b = 2a = 2**(2**53 + 1) 和 IEEE 754 浮点数,这不可能工作,但您需要大约 1.2 PB 的 RAM 和地址空间大于 2**48 的机器才能表示首先是a 的值。
  • 是的,没有任何计算机有足够的内存来保存足够大的整数,从而使这看似失败。但我没有提到这一点,因为如果 OP 对 log() 可能并非在所有情况下都准确感到惊讶,那么他们还没有准备好接受关于理论可能性的广泛限制的争论;-)跨度>
  • 谢谢!知道您也可以将基数作为第二个参数传递是有帮助的。 :) 当round() 返回一个整数时,int() 是必要的吗?
  • 必要的,是的!需要int(),以便求幂将整数提高到整数幂,这是精确计算的。如果没有int(),我们会将整数提升到浮点幂,这只是一个近似值。顺便说一句,1-argument round() 的行为在 Python 2 和 3 之间有所不同。int() 在 Python 3 中不是必需的,但不会造成伤害,在 Python 2 中是必需的。
  • 啊,好吧,我只使用过 Python 3,所以不知道 round() 实际上在 Python 2 中返回了一个浮点数。
【解决方案2】:

可能是这样的:

def is_perfect_power(a, b):
  while a % b == 0:
    a = a / b
  if a == 1:
    return True
  return False

print is_perfect_power(8,2)

【讨论】:

    【解决方案3】:

    如果您要处理大量数字,您可能需要查看gmpy2gmpy2 提供对 GMP 多精度库的访问。提供的功能之一是is_power()。如果参数是某个基数的完美幂,它将返回 True。它不会提供幂或基数,但它会快速过滤掉不能成为完美幂的数字。

    >>> import gmpy2
    >>> [n for n in range(1,1000) if gmpy2.is_power(n)]
    [1, 4, 8, 9, 16, 25, 27, 32, 36, 49, 64, 81, 100, 121, 125, 128, 144, 169, 196, 216, 225, 243, 256, 289, 324, 343, 361, 400, 441, 484, 512, 529, 576, 625, 676, 729, 784, 841, 900, 961]
    

    一旦您确定了可能的幂,您就可以使用iroot_rem(x,n) 找到 x 的第 n 个根和余数。然后一旦你找到一个有效的指数,你就可以找到底数。下面是一个在一个范围内搜索所有可能完美幂的示例。

    >>> for x in range(1,1000):
    ...   if gmpy2.is_power(x):
    ...     for e in range(x.bit_length(), 1, -1):
    ...       temp_root, temp_rem = gmpy2.iroot_rem(x, e)
    ...       if not temp_rem:
    ...         print x, temp_root, e
    ... 
    4 2 2
    8 2 3
    9 3 2
    16 2 4
    16 4 2
    25 5 2
    27 3 3
    32 2 5
    36 6 2
    49 7 2
    64 2 6
    64 4 3
    64 8 2
    81 3 4
    81 9 2
    << remainder clipped>>
    

    免责声明:我维护gmpy2

    【讨论】:

      【解决方案4】:

      如果您想加快对大数的重复除法,您可以列出所有底数的指数,其中指数是 2 的幂,并仅测试这些指数。

      例如,取 39。 9 是二进制的 101,即 23 + 21 = 8 + 1。

      所以 39 = 38+1 = 323+21 = 323 x 321

      您只需要试除两次,而不是 9 次。

      def is_power(a, b):
        if b == 0 or b == 1:
          return a == b
        if a < b:
          return False
        c = []
        # make a list of the power of 2 exponents less than or equal to a:
        while b * b <= a:
          c.append(b) 
          b = b * b
        # test against each of them where b <= remaining a:
        while True: 
          if a % b != 0:
             return False
          a //= b
          while b > a:
            if len(c) == 0:
              return a == 1
            b = c.pop()
        return True
      

      适用于正整数,例如:

      print is_power(3**554756,3)
      print is_power((3**554756)-1,3)
      

      输出:

      True
      False
      

      【讨论】:

        【解决方案5】:

        你也可以使用sympy的perfect_power

        >>> from sympy import perfect_power
        >>> perfect_power(243)
        (3, 5)
        >>> perfect_power(244)
        False
        

        如果您只想检查给定数字是否是某个给定基数的完美幂,请查看下面@smichr 的评论。

        【讨论】:

        • 如果你不知道基地,你会使用这个。在这种情况下,用户似乎知道,例如,基数是 3。另请参阅sympy.core.power.integer_log(243, 3) -&gt; (5, True),它使用了@samgak 建议的方法。
        • @smichr:很公平。我修改了我的答案以指向您的评论。
        【解决方案6】:

        如果这里的基数和指数都是通配符,并且您只从“大数字”结果开始,那么您将面临速度与内存之间的权衡。由于您似乎暗示嵌套循环将是“低效的”,因此我认为 CPU 周期效率对您很重要。在这种情况下,我可以建议预先计算一堆基指数对:如果你愿意的话,一种彩虹表。这将消耗更多的内存,但更少的 CPU 周期。然后您可以简单地遍历您的表并查看是否找到匹配项。或者,如果您非常关心速度,您甚至可以对表进行排序并进行二分搜索。如果您愿意,您甚至可以将预先计算的表存储在一个文件中。

        此外,您没有具体说明您是否只是想要一个是/否的答案,或者您是否正在尝试找到相应的幂的基数和指数。所以我假设你想找到 a 基指数对(一些完美的幂会有多个基指数解)。

        试试这个尺寸(我使用的是 Python 3):

        #Setup the table as a list
        min_base = 2 #Smallest possible base
        max_base = 100 #Largest possible base
        min_exp = 2 #Smallest possible exponent
        max_exp = 10 #Largest possible exponent
        powers = []
        
        #Pre-compute the table - this takes time, but only needs to be done once
        for i in range(min_base, max_base+1):
            for j in range(min_exp, max_exp+1):
                powers.append([i, j, i ** j])
        
        #Now sort the table by the 3'rd element - again this is done only once
        powers.sort(key=lambda x: x[2])
        
        
        #Binary search the table to check if a number is a perfect power
        def is_perfect_power(a, powers_arr):
            lower = 0
            upper = len(powers_arr)
            while lower < upper:
                x = lower + (upper - lower) // 2
                val = powers_arr[x][2] #[2] for the pre-computed power
                if a == val:
                    return powers_arr[x][0:2]
                elif a > val:
                    if lower == x:
                        break
                    lower = x
                elif a < val:
                    upper = x
            return False #Number supplied is not a perfect power
        
        #A simple demonstration
        print(is_perfect_power(243, powers)) #Output is [3, 5]
        print(is_perfect_power(105413504, powers)) #Output is [14, 7]
        print(is_perfect_power(468209, powers)) #Output is False - not a perfect power
        

        更倾向于数学的人可能有更有效的答案,但这应该可以帮助您入门。在我的机器上,它运行得非常快。

        【讨论】:

        • 我认为 OP 正在寻找一个已知的基地。
        • 也许吧,但鉴于他说他已经在循环中检查这些,我认为他正在循环碱基。我退后一步,看着更大的问题。也许不会帮助他,但希望能帮助某人。
        【解决方案7】:
        def checkifanumberisapowerofanothernumber(x,y):
            if x==1:
                return y==1
            p=1
            while p<y:
                p=p*x
            return p==y
        

        这会重复计算 x 的幂,直到数字接近 y。然后检查数字是否等于 y。

        【讨论】:

          猜你喜欢
          • 2011-09-27
          • 2015-11-06
          • 1970-01-01
          • 2010-10-10
          • 2022-01-17
          相关资源
          最近更新 更多