-1

The following program is created to to calculate the perfect power of numbers

The end result is to have the Perfect Power complete all test cases in under .01s of elapsed execution time.

Currently the program end result time performance is at about 2+secs for 10,000 and the one for 1073741824 takes much much longer. Need assistance to help lower the time for all perfect-powers to be .01 seconds and less.

Below is the program code:

    /**
  
 *
 * A utlity class to calculate the perfect power of an integer
 */
public class PerfectPower {
    public static void main(String[] args) {
        new TimeExec(new Runnable() {
            public void run() {
                   System.out.println("Perfect Power of 17 is " + getPerfectPower(17));
            }
        }, "Get Perfect Power of 17", System.out).start();
        
        new TimeExec(new Runnable() {
            public void run() {
                   System.out.println("Perfect Power of 625 is " + getPerfectPower(625));
            }
        }, "Get Perfect Power of 625", System.out).start();
        
        new TimeExec(new Runnable() {
            public void run() {
                   System.out.println("Perfect Power of 1024 is " + getPerfectPower(1024));
            }
        }, "Get Perfect Power of 1024", System.out).start();
        
        new TimeExec(new Runnable() {
            public void run() {
                   System.out.println("Perfect Power of 10000 is " + getPerfectPower(10000));
            }
        }, "Get Perfect Power of 10000", System.out).start();
        
        new TimeExec(new Runnable() {
            public void run() {
                   System.out.println("Perfect Power of 1073741824 is " + getPerfectPower(1073741824));
            }
        }, "Get Perfect Power of 1073741824", System.out).start();  
    }

    /**
     * Get the perfect power for a number.
     * @param x number for which to calculate the perfect power.
     */
    public static int getPerfectPower(int x) {
        int largestP = 1;
           
        for (int b = 1; b < x; b++) {
        for (int p = 1; p < x; p++) {
           
                if (Math.pow(b,p) == x) {
                    largestP = p;
                }
           }           
        }
        
        return largestP;
    }

}

end code:

Perfect Power of 17 is 1 TimeExec: Get Perfect Power of 17: 0.001s

Perfect Power of 625 is 2 TimeExec: Get Perfect Power of 625: 0.026s

Perfect Power of 1024 is 2 TimeExec: Get Perfect Power of 1024: 0.052s

Perfect Power of 10000 is 2 TimeExec: Get Perfect Power of 10000: 1.9s

need this code here to print under .01s Perfect Power of 1073741824 is X TimeExec: Get Perfect Power of : XX.XXXs

Kale_Kyle
  • 1
  • 4
  • 2
    Your computer takes 2+ seconds for 10000? – Spectric Sep 19 '20 at 15:04
  • well like 1.9-2.0secs so yeah. i fixed the post so it shows the output code. – Kale_Kyle Sep 19 '20 at 15:10
  • 1
    https://stackoverflow.com/questions/39190815/how-to-make-perfect-power-algorithm-more-efficient – Prashant Sep 19 '20 at 15:11
  • that doesn't match the code I'm looking to use but thanks anyway. – Kale_Kyle Sep 19 '20 at 15:14
  • Simply adding an if statement in getPerfectPower that checks if `Math.pow(b,p) > x` and if so - breaks the inner loop. Will improve your run time dramatically. If that's not enough than notice that `b` needs to run up until `sqrt(x)` and then you can be sure that the output is 1. – Yonlif Sep 19 '20 at 15:20
  • thank you but i tried that and it still didn't make a difference. – Kale_Kyle Sep 19 '20 at 15:32
  • 8 = 8^1 = 4^2 = 2^3, so yes, 8 is a *perfect power*, not 1, 2, and 3, which are just the *exponents* leading to the perfect power, together with the *bases* 8, 4, and 2, respectively. Actually, since you include x^1, all positive numbers are perfect powers. Your method is not making sense, unless you rename it to `getMaxExponentOfPerfect Power()` – Andreas Sep 19 '20 at 15:36
  • @Andreas : 4^2=16, perhaps you were thinking of 64=8^2=4^3=2^6? – Lutz Lehmann Sep 19 '20 at 15:41
  • thanks you aswell but that wont really make a difference. I'm just looking for the code that will make it more time efficient. – Kale_Kyle Sep 19 '20 at 15:43
  • @LutzLehmann DOH!!! Yes I was. Now I just feel stupid. Thanks for giving the correct example. – Andreas Sep 19 '20 at 15:44
  • Does this answer your question? [Get powers of a perfect power number](https://stackoverflow.com/questions/61193162/get-powers-of-a-perfect-power-number) – Sneftel Sep 19 '20 at 15:55
  • @lutzLehmann in my code I need to have the end result to complete all test cases in under .01s of elapsed execution time. You're "answer" didn't help with that. At the bottom of my code, I just need to edit that section only. – Kale_Kyle Sep 19 '20 at 15:56
  • @Sneftel i just checked out that link but it didn't quite help at all but thank you. – Kale_Kyle Sep 19 '20 at 15:58
  • What, a reduction from O(x^2) to O(log2(x)) is not noticeable? If correctly coded, it should have a clear effect. – Lutz Lehmann Sep 19 '20 at 16:12

2 Answers2

1

First, we're looking for the maximum exponent for the perfect power number given in the parameter. The best candidate for the maximum exponent is a base of 2, so we'll start the loop at 2, not 1.

Second, the smallest exponent is 2 (other than the fallback 1), so the maximum base where base2 = x, is maxBase = sqrt(x), so we'll end the loop at that base value.

We're targeting the formula bp = x, and we have x from parameter and b from the loop, so we can calculate p using p = log(x) / log(b), then check if that's an integer.

Best way to do that, avoiding rounding errors, is to round to nearest integer, then checking if bp == x.

The code for that is:

public static int getPerfectPower(int x) {
    double maxBase = Math.sqrt(x);
    for (int b = 2; b <= maxBase; b++) {
        long p = Math.round(Math.log(x) / Math.log(b));
        if (Math.pow(b, p) == x)
            return (int) p;
    }
    return 1;
}

Tests

public static void main(String[] args) {
    test(17);
    test(625);
    test(1024);
    test(10000);
    test(1073741824);
}
static void test(int x) {
    long start = System.nanoTime();
    int exponent = getPerfectPower(x);
    long end = System.nanoTime();
    System.out.printf("Perfect Power of %d is %d (%.9fs)%n",
                      x, exponent, (end - start) / 1e9);
}

Output

Perfect Power of 17 is 1 (0.000022500s)
Perfect Power of 625 is 4 (0.000003700s)
Perfect Power of 1024 is 10 (0.000003700s)
Perfect Power of 10000 is 4 (0.000003500s)
Perfect Power of 1073741824 is 30 (0.000001700s)

Alternatively, we can loop the exponent instead of the base, changing time complexity from O(sqrt(x)) to O(log(x)), which is technically faster, but the values here are too small to notice any performance difference.

Without further explanation, here's the code:

public static int getPerfectPower(int x) {
    // x = b ^ p   <==>   p = log(x) / log(b)   <==>   b = exp(log(x) / p)
    double logX = Math.log(x);
    int maxExp = (int) Math.round(logX / Math.log(2));
    for (int p = maxExp; p > 1; p--) {
        long b = Math.round(Math.exp(logX / p));
        if (Math.pow(b, p) == x)
            return p;
    }
    return 1;
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
0

You are testing too many cases with the nested two loops from 1 to x.

For an exponent p you can compute the most likely base as b0=floor(pow(x,1.0/p)). If you want to be cautious then test the powers of b=b0-1, b0, b0+1 for being equal to x, but the case b=b0-1 should never be valid.

You can then stop increasing the exponents when b0=1 has been reached.

Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51