对于2个数字,如何测试一个是否是另一个的整数幂?

时间:2012-12-12 18:30:22

标签: java algorithm logarithm exponent

对于整数x,y和n,(只给出x和y)测试x n = y?如果x = 8,y = 512,则n = 3,这是真的。但是如果x = 8且y = 500,则n必须大约为2.98(不是整数),因此语句的计算结果为false。使用对数是进行此测试的最佳方法吗?

Check if one integer is an integer power of another提供了一些解决方案:

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

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

和对数方法(这是我的版本):

return ((log(y, x) % 1) == 0) // log(expression, base)

log(y,x)= log x y

哪种方法评估速度更快,特别是对于大数字?

6 个答案:

答案 0 :(得分:4)

对数方法需要更加小心,因为对数函数由于使用浮点近似而具有少量不准确性。例如3 10 = 59049,但是:

log(59049, 3)
   ===> 9.999999999999998

您是否可以通过检查答案是否“足够接近”到最接近的整数来进行补偿(取决于 x y 的范围) 。如果 y 小于2 32 ,那么我认为最接近的对数可以按比例获得一个整数(没有真正的答案是整数)是:

1 - log(4294967295, 65536) / 2
   ===> 1.049693665322593e-11

所以选择一个小于此的epsilon,你可以放心地使用对数方法:

n = log(y, x);
e = round(n);
if (abs(1 - n / e) < epsilon) {
    /* y == x to the power of e */
} else {
    /* y not a power of x */
}

如果 y 的允许范围更大,那么您必须为epsilon找到合适的值。但要注意:对于足够大的 y ,在双精度浮点中可能没有合适的epsilon 。例如,如果 y 可以大到2 48 - 1那么就是这种情况,因为

log(281474976710655, 16777216)
   ===> 2.0 exactly

因此,对于足够大的 y ,您不能依赖于对数:您需要在检查后明确执行取幂。

答案 1 :(得分:1)

如果存在 b e b ^ <,则数字 n 是完美的力量em> e = n 。例如216 = 6 ^ 3 = 2 ^ 3 * 3 ^ 3是完美的功率,但是72 = 2 ^ 3 * 3 ^ 2不是。如果数字 n 是一个完美的幂,则指数 e 必须小于log2 n ,因为如果 e 大于2 ^ e 将大于 n 。此外,只需要测试素数* e * s,因为如果数字是复合指数的完美幂,它也将是复合分量的素因子的完美幂;例如,2 ^ 15 = 32768 = 32 ^ 3 = 8 ^ 5是一个完美的立方根,也是一个完美的第五根。

我的博客上有implementation

答案 2 :(得分:1)

对于大数字,日志可能会更快,但您应该对其进行基准测试。这将涉及转换为双倍和退回,这可能是一个问题。

我认为这可能是一个更好的解决方案:

long y = 512;
long x = 8;
if (x <= 1) return false;
while (y>1) {
    // Find maximum x^(2^n) <= y
    long xp = x;   // x to some maximum power
    long xp2;      // experimental value of xp
    while ((xp2 = xp*xp) <= y)
      xp = xp2;
    if (y%xp != 0) return false;  // reject
    y /= xp;
}
return y == 1;

仍有一些方法可以改进,我只测试了一些案例,但似乎有效。

答案 3 :(得分:1)

对于相当小的输入,我相当确定你的第三种方法效果最好。 (不过,你需要更加小心地检查完整性。)

有些值得深思的话:

  1. 您可以非常快速地计算x的幂sqrt(y)(即O(M(log y))时间),然后通过此幂计算(在O(M) (log y)log log y)time)得到两个大小一半的子问题。

  2. 您可以对对数使用相当差的近似值来获得n上相当紧的边界。如果我知道一个整数在log_x(y)的常数内,那么我只需要检查x的常数幂。使用Edward Falk的答案中举例说明的“方形和乘法技术”,可以最快地完成这些检查。

  3. 即使在n上有一个相当宽的界限,你也可以使用算术模数一个适度大小的随机素数来大大缩小候选n的集合。应该可以使用算术模数几个适度大小的随机素数和中国剩余定理来将可能的n缩小到零或一个非常有效。

  4. 运行第二个想法:请注意,我只需要y的最高位来获得log y的近似值,该近似值最多为常数;其他一切都是肉汁。您应该能够使用FPU取前53位的对数,使用数字的长度进行调整,然后在O(M(log y))时间内检查最接近的整数。第三个想法允许您使用随机化检查x^n == y时间O(log y)是否为最佳。

答案 4 :(得分:1)

这种方法应该可以解决问题:

void toCheckPower(){
    Scanner sc=new Scanner(System.in);

    int n=sc.nextInt();
    System.out.println("No of which power is checked="+n);

    int pow=sc.nextInt();
    int num=pow;
    System.out.println("No to check power="+pow);
    int sum=1;

    while(sum<=pow){
        sum=sum*n;
        if(sum==pow){
            System.out.println(pow+" is a Power of "+n);
            break;
        }
        else if(sum>pow){
            System.out.println(pow+" is not a Power of "+n);
            break;
        }
     }
}

答案 5 :(得分:0)

如果没有基准测试,只关注正在发生的事情,这个应该是最快的。

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

(请注意,你已经从你的描述中颠倒了x&amp; y的含义,并且n完全不同)

while (x%y == 0)  x = x / y;

这是除法和模数(基本上是第二个除法),所以它的工作量是两倍,但是它可以提前退出循环,所以它可能会赢。

return ((log(y, x) % 1) == 0)

这使用了本质上是邪恶的浮点(尽管浮点在现代CPU芯片中变得更好和更快)。然而,当你需要知道它是否完整时,你做它的模数是偶数还是奇数。