【问题标题】:Check if one integer is an integer power of another检查一个整数是否是另一个整数的幂
【发布时间】:2020-09-08 23:38:04
【问题描述】:

这是interview question:“给定 2 个整数 x 和 y,检查 x 是否是 y 的整数幂”(例如,对于 x = 8 和 y = 2,答案是“真”,对于 x = 10和 y = 2“假”)。

显而易见的解决方案是:

int n = y; while(n < x) n *= y; return n == x

现在我正在考虑如何改进它。

当然,我可以检查一些特殊情况:例如xy 都应该是奇数或偶数,即我们可以检查 xy 的最低有效位。但是我想知道我是否可以改进核心算法本身。

【问题讨论】:

  • 实际上,我认为显而易见的解决方案是将 x 除以 y,然后将结果除以 y 不断,直到达到一个不能被 y 整除的数字。如果该数字为 1,则 x 是 y 的幂。
  • 不幸的是,这里没有一个用户注意到发布的每段代码都在 x = ±1 时惨遭失败
  • 不,我的适用于 x = +1(并且一个微不足道的 abs 修复了负数)。但是现在 y == 0。

标签: algorithm math


【解决方案1】:

最好将 y 反复除以 x。第一次得到非零余数时,您知道 x 不是 y 的整数幂。

while (x%y == 0)  x = x / y
return x == 1

这涉及您在第一次迭代中的奇数/偶数点。

【讨论】:

  • 为什么除法比乘法好?
  • @Michael:假设 x 是 123456789,y 是 2。计算出你需要做多少次乘以 y 才能得到答案,然后计算出你需要做多少次除法.
  • 它也比乘法好,因为它不会溢出。优化后的代码可能会跳过您检测溢出的尝试。
  • 是的,但平均情况要好得多。如果 y 不是 x 的因数(在 y 次中将是真正的 y-1),您会在第一次除法时找到。
  • @Paul - 32 位整数除法将比乘法慢大约 10 倍(至少在 x86 上)。您必须将其纳入分析。
【解决方案2】:

这意味着 logy(x) 应该是一个 整数。不需要任何循环O(1) 时间

public class PowerTest {

    public static boolean isPower(int x, int y) {
        double d = Math.log(Math.abs(x)) / Math.log(Math.abs(y));

        if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
            if (d == (int) d) {
                return true;
            } else {
                return false;
            }
        } else if (x > 0 && y < 0) {
            if ((int) d % 2 == 0) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {

        System.out.println(isPower(-32, -2));
        System.out.println(isPower(2, 8));
        System.out.println(isPower(8, 12));
        System.out.println(isPower(9, 9));
        System.out.println(isPower(-16, 2));
        System.out.println(isPower(-8, -2));
        System.out.println(isPower(16, -2));
        System.out.println(isPower(8, -2));
    }

}

【讨论】:

  • 您需要小心浮点数的舍入问题。还有isPower(-8, -2)公平吗?
  • 是的,你是对的。我认为整数是正数。那么,我应该认为是abs版
  • 你应该试试 isPower(1162261467, 3) x is greater one (int the question)
  • +1 不错,但是,不是 O(1),是 O(log(x) + log(y)),另外使用小数更好地避免舍入问题。
  • 我认为您的代码在 (17, -2) 上失败。 (不过你可以更正它)。
【解决方案3】:

这会在 O(log N) 步中查找指数:

#define MAX_POWERS 100

int is_power(unsigned long x, unsigned long y) {
  int i;
  unsigned long powers[MAX_POWERS];
  unsigned long last;
  last = powers[0] = y;

  for (i = 1; last < x; i++) {
    last *= last; // note that last * last can overflow here!
    powers[i] = last;
  }
  while (x >= y) {
    unsigned long top = powers[--i];
    if (x >= top) {
      unsigned long x1 = x / top;
      if (x1 * top != x) return 0;
      x = x1;
    }
  }
  return (x == 1);
}

此代码不处理负数,但i = 1时使用一些条件代码可以轻松完成

【讨论】:

  • 感谢预先计算 y 幂的想法!
  • 这个解决方案应该得到更多的支持。顺便说一句,如果 x > 1,powers[99] 将无法放入无符号长整数中。例如,如果 x == 2,则 powers 99 大约是一个 1,然后是 1000 亿零。
【解决方案4】:

这对于正数来说看起来相当快,因为​​它会找到所需功率的下限和上限,然后应用二进制搜索。

#include <iostream>
#include <cmath>
using namespace std;

//x is the dividend, y the divisor.
bool isIntegerPower(int x, int y)
{
    int low = 0, high;
    int exp = 1;
    int val = y;
    //Loop by changing exponent in the powers of 2 and
    //Find out low and high exponents between which the required exponent lies.
    while(1)
    {
        val = pow((double)y, exp);
        if(val == x)
            return true;
        else if(val > x)
            break;
        low = exp;
        exp = exp * 2;
        high = exp;
    }
    //Use binary search to find out the actual integer exponent if exists
    //Otherwise, return false as no integer power.
    int mid = (low + high)/2;
    while(low < high)
    {
        val = pow((double)y, mid);
        if(val > x)
        {
            high = mid-1;
        }
        else if(val == x)
        {
            return true;
        }
        else if(val < x)
        {
            low = mid+1;
        }
        mid = (low + high)/2;
    }
    return false;
}

int main()
{
    cout<<isIntegerPower(1024,2);
}

【讨论】:

    【解决方案5】:
        double a=8;
        double b=64;
    
        double n = Math.log(b)/Math.log(a);
        double e = Math.ceil(n);
    
        if((n/e) == 1){
            System.out.println("true");
        } else{
           System.out.println("false");
        }
    

    【讨论】:

      【解决方案6】:

      我会像这样实现这个功能:

      bool IsWholeNumberPower(int x, int y)
      {
          double power = log(x)/log(y);
          return floor(power) == power;
      }
      

      这不应该像浮点比较一样需要在增量内进行检查,因为我们正在检查整数。

      【讨论】:

      • 现在的问题是“日志”是如何实现的,为什么它比循环更好。
      • log(x) 其中 x 是整数不是整数。所以,不,你不是在处理整数。也可以考虑IsWholeNumberPower(-8, -2)。答案应该是真的。
      • 日志方法比循环更好,IMO,因为它清楚地表明了你的意图。只要您知道一个数字的对数除以另一个数字的对数就可以使您获得第二个数字得到第一个数字的权力,我想不出更清晰的方法(我假设大多数人在学校都学过,但我可能离基地很远)。如果你正在寻找更快的代码,那么我不能告诉你,因为我没有用任何语言测试过。
      • @JeremyP:我不是说 log(x) 会给出一个整数,我的意思是你只想检查 log(x)/log(y) 是一个整数,所以检查答案是否在floor 的增量是不必要的。但是你是对的,这不适用于负数。
      • @Matt Ellen:是的,但是因为 log(x) 是一个超越数,所以它会被四舍五入,类似地 log(y),log(x)/log(y) 可能不会作为整数。
      【解决方案7】:

      再三考虑,不要这样做。它不适用于否定的x 和/或y请注意,现在提供的所有其他基于log 的答案也以完全相同的方式被破坏。

      下面是一个快速通用的解决方案(Java):

      static boolean isPow(int x, int y) {
          int logyx = (int)(Math.log(x) / Math.log(y));
          return pow(y, logyx) == x || pow(y, logyx + 1) == x;
      }
      

      其中pow() 是整数取幂函数,例如Java 中的以下内容:

      static int pow(int a, int b) {
          return (int)Math.pow(a, b);
      }
      

      (由于Math.pow 提供了以下保证,这有效:“如果两个参数都是整数,则结果完全等于将第一个参数提升到第二个参数的幂的数学结果......” )

      使用对数而不是重复除法的原因是性能:虽然log is slower than division,它会慢一个小的固定倍数。同时它确实消除了对循环的需要,因此为您提供了一个恒定时间算法。

      【讨论】:

        【解决方案8】:

        y 为2 的情况下,有一种快速方法可以避免循环。这种方法可以扩展到y 是 2 的较大幂的情况。

        如果 x 是 2 的幂,则 x 的二进制表示具有单个设置位。有一个相当简单的位摆弄算法用于在 O(log n) 时间内计算整数中的位,其中 n 是整数的位宽。许多处理器还具有专门的指令,可以将其作为单个操作处理,速度与(例如)整数求反一样快。

        不过,要扩展该方法,首先要采用一种稍微不同的方法来检查单个位。首先确定最低有效位的位置。同样,还有一种简单的位摆弄算法,而且许多处理器都有快速的专用指令。

        如果这个位是唯一的位,那么(1 &lt;&lt; pos) == x。这里的优点是,如果您要测试 4 的幂,则可以测试 pos % 2 == 0(单个位在偶数位置)。测试任意二的幂,你可以测试pos % (y &gt;&gt; 1) == 0

        原则上,您可以执行类似的操作来测试 3 的幂和 3 的幂。问题是您需要一台可以在 3 为底下工作的机器,这不太可能。您当然可以测试任何值 x 以查看其在基数 y 中的表示是否有一个非零数字,但您会做更多您已经在做的工作。以上利用了计算机以二进制工作的事实。

        不过,在现实世界中可能不值得这样做。

        【讨论】:

        • 同样的想法可以应用于任何基地。这并不像移位那样容易,但可以只使用基本的算术运算(+*/)来完成。有关 C 中的实现,请参阅我自己对 OP 的回复。
        • 2 的幂检查速度更快。 (x &amp; (x-1)) == 0
        • @Axn - 我以前见过这个技巧,但我很容易忘记它。当然它并不比内在函数快,但它是可移植的。
        【解决方案9】:

        这是一个 Python 版本,它汇集了 @salva 和 @Axn 的想法,并被修改为不生成任何大于给定数字的数字,并且通过反复削减兴趣数量:

        def perfect_base(b, n):
            """Returns True if integer n can be expressed as b**e where
            n is a positive integer, else False."""
        
            assert b > 1 and n >= b and int(n) == n and int(b) == b
        
            # parity check
            if not b % 2:
                if n % 2:
                    return False  # b,n is even,odd
                if b == 2:
                    return n & (n - 1) == 0
                if not b & (b - 1) and n & (n - 1):
                    return False  # b == 2**m but n != 2**M
            elif not n % 2:
                return False  # b,n is odd,even
        
            while n >= b:
                d = b
                while d <= n:
                    n, r = divmod(n, d)
                    if r:
                        return False
                    d *= d
            return n == 1
        

        【讨论】:

          【解决方案10】:

          以前的答案是正确的,我最喜欢 Paul 的答案。它简单而干净。 这是他建议的Java实现:

          public static boolean isPowerOfaNumber(int baseOrg, int powerOrg) {
              double base = baseOrg;
              double power = powerOrg;
          
              while (base % power == 0)
                  base = base / power;
              // return true if base is equal 1
              return base == 1;
          }
          

          【讨论】:

            【解决方案11】:

            在数量太大的情况下...使用log函数降低时间复杂度:

            import math
            base = int(input("Enter the base number: "))
            for i in range(base,int(input("Enter the end of range: "))+1):
                if(math.log(i) / math.log(base) % 1 == 0 ):
                    print(i)
            

            【讨论】:

              【解决方案12】:

              如果您可以访问y 的最大幂,可以将其放入所需的数据类型中,这是解决此问题的一种非常巧妙的方法。

              让我们说,对于我们来说,y == 3。因此,我们需要检查 x 是否是 3 的幂。

              鉴于我们需要检查整数 x 是否是 3 的幂,让我们从手头已有的信息开始考虑这个问题。

              1162261467 是可以放入 Java int 的 3 的最大幂。
              1162261467 = 3^19 + 0

              给定的 x 可以表示为[(a power of 3) + (some n)]。我认为能够证明如果 n 为 0(发生 iff x 是 3 的幂)1162261467 % x = 0.

              是相当基本的。

              因此,要检查给定整数 x 是否为 3 的幂,请检查 x &gt; 0 &amp;&amp; 1162261467 % x == 0 是否。

              概括。要检查给定整数 x 是否是给定整数 y 的幂,请检查 x &gt; 0 &amp;&amp; Y % x == 0 是否:Yy 可以适合整数数据类型的最大幂。

              一般的想法是,如果AY 的某个幂,则A 可以表示为B/Ya,其中a 是某个整数,A &lt; B。对于A &gt; B,它遵循完全相同的原则。 A = B 案例是基本的。

              【讨论】:

              • 可能存在一些边缘情况,但我认为可以解决。
              • 一个极端情况是高幂的指数不是素数。
              【解决方案13】:

              我找到了这个解决方案 //检查A是否可以表示为两个整数的幂

                  int isPower(int A)
                  {
                   int i,a;
                   double p;
                   if(A==1)
                   return 1;
                   for(int a=1; a<=sqrt(A);++a )
                   {
                       p=log(A)/log(a);
                       if(p-int(p)<0.000000001)
                       return 1;
                   }
                  return 0;
                 }
              

              binarycoder.org

              【讨论】:

                猜你喜欢
                • 2015-01-26
                • 1970-01-01
                • 2017-01-09
                • 1970-01-01
                • 1970-01-01
                • 2022-01-17
                • 1970-01-01
                • 1970-01-01
                • 2021-08-31
                相关资源
                最近更新 更多