Обчислення ступенів цілих чисел


78

Чи існує інший спосіб в Java обчислити ступінь цілого числа?

Я використовую Math.pow(a, b)зараз, але він повертає a double, і це, як правило, багато роботи, і виглядає менш чистим, коли ви просто хочете використовувати ints (потужність тоді також завжди призведе до an int).

Чи є щось таке просте, a**bяк у Python?


2
Це можливо дублікат цього питання stackoverflow.com/questions/101439 / ...
bpgergo

Відповіді:


46

Цілі числа - лише 32 біти. Це означає, що його максимальне значення становить 2^31 -1. Як бачите, для дуже малих чисел ви швидко отримуєте результат, який більше не може бути представлений цілим числом. Ось чому Math.powвикористовує double.

Якщо ви хочете довільну цілу точність, використовуйте BigInteger.pow. Але це, звичайно, менш ефективно.


42
+1, це правда. Але я вважав би приємним, якби Javaархітектори додали pow(int, int). Ви знаєте, що іноді ви просто цього хочете 5^6і взагалі не дбаєте про дублі.
Петро Мінчев

4
Я лише здогадуюсь, але, думаю, вони цього не зробили, оскільки такий метод у більшості випадків призвів би до абсолютно неправильних результатів. Принцип найменшого здивування.
JB Nizet

1
Так, це хороший момент. Але коли ви невіглас і працюєте int, ніщо не заважає вам перетворити на (int)переповнене значення.
Петро Мінчев

3
Я маю в виду , якщо ви визначили «більшість випадків» , як «для більшості значення входів» , то Java, безумовно , не пропонує *оператор або для intабо longвходів , так як для більшості входів множення буде переповненням. Насправді навіть додавання переповнює приблизно половину часу!
BeeOnRope

Вибачте, якщо я заходжу пізно. Чому використовувати подвійний і не довгий?
Blueriver

38

Найкраще алгоритм базується на визначенні рекурсивної потужності a ^ b.

long pow (long a, int b)
{
    if ( b == 0)        return 1;
    if ( b == 1)        return a;
    if (isEven( b ))    return     pow ( a * a, b/2); //even a=(a^2)^b/2
    else                return a * pow ( a * a, b/2); //odd  a=a*(a^2)^b/2

}

Тривалість операції - O (logb). Довідково: Додаткова інформація


2
А що таке isEven(b)метод? Це те саме з b % 2 == 0?
HendraWD

1
Мені теж цікаво ... Хлопець пішов і залишив нас, дивуючись ... Програмісти, що проскакують ...
Апостолос,

Неіснуючий (isEven(b))' function -- meaning ((b & 1) == 0) `- і непотрібний складний алгоритм! (
Апостолос

Краще обробляти випадок, коли b <0, щоб уникнути нескінченної рекурсії
Tzafrir

5
У моїх тестах це насправді вдвічі-вдвічі повільніше, ніж алгоритм простого циклу для повного діапазону значень. Рекурсія тут накладає багато накладних витрат.
Gumby The Green

33

Ні, немає чогось такого короткого, як a**b

Ось простий цикл, якщо ви хочете уникнути подвоєння:

long result = 1;
for (int i = 1; i <= b; i++) {
   result *= a;
}

Якщо ви хочете використати powта перетворити результат на ціле число, додайте результат таким чином:

int result = (int)Math.pow(a, b);

5
@DmitryGinzburg: якщо a long- це 64 біт, тоді O (n) має 64 як верхню межу. Невже це так погано?
Томас Веллер

1
@Thomas ні, ти помиляєшся; якщо bє 2_000_000_000, то вам доведеться робити 2e9операції , а не 31в іншому рішенні
Дмитро Гінзбург

2
@DmitryGinzburg: у такому випадку ви отримаєте багато проблем із переповненням. Код OP не включає перевірку аргументів. Максимально дозволене число має бути 64, якщо а має значення лише 2 (а для а = 1 це не має сенсу).
Томас Веллер

Проблеми переповнення @Thomas - це не ваша проблема, а проблема бігуна.
Дмитро Гінзбург

1
У моїх тестах це насправді в 2–20 разів швидше, ніж рекурсивний алгоритм O (log (b)) для всього діапазону значень. Рекурсія накладає багато накладних витрат.
Gumby The Green

32

Коли це сила 2. Майте на увазі, що ви можете використовувати простий і швидкий вираз зміщення 1 << exponent

приклад:

2 2 = 1 << 2= (int) Math.pow(2, 2)
2 10 = 1 << 10=(int) Math.pow(2, 10)

Для більших показників (більше 31) використовуйте довгі замість них

2 32 = 1L << 32=(long) Math.pow(2, 32)

до речі. у Котліні у вас shlзамість <<цього

(java) 1L << 32= 1L shl 32(kotlin)



2

Математичні бібліотеки Гуави пропонують два методи, корисні при обчисленні точних цілочисельних ступенів:

pow(int b, int k) обчислює b до k-ї потужності та обертається при переповненні

checkedPow(int b, int k)ідентичний, за винятком того, що він кидає ArithmeticExceptionна переповнення

Особисто checkedPow()задовольняє більшість моїх потреб у цілочисельній степені і є чистішим та безпечнішим, ніж використання подвійних версій та округлення тощо. Майже у всіх місцях, де я хочу функцію потужності, переповнення є помилкою (або неможливою, але я хочу сказати, якщо неможливе колись стає можливим).

Якщо ви хочете отримати longрезультат, ви можете просто використовувати відповідні LongMathметоди та передавати intаргументи.


2

Ну, ви можете просто використовувати, Math.pow(a,b)як раніше, і просто перетворити його значення, використовуючи (int)перед ним. Нижче може бути використаний як приклад.

int x = (int) Math.pow(a,b);

де aі bмогло бути doubleабо intзначення, як ви хочете. Це просто перетворить його вихідні дані на ціле значення, як вам потрібно.


3
Я не згоден, якщо 3.0 ^ 1.0 = 2.999999...через помилки округлення відповідь буде 2неправильною.
Hidde

@Hidde Так, ти маєш рацію, друг, це, безумовно, має недолік, дякую за згадку. Це може дати неправильний результат, але, можливо, ваш приклад дасть правильний результат як. 3.0Просто як приклад помилки округлення для множення, 2.2*3.0 = 6.0000005а не 6.6.
Шубхам Шрівастава,

2
@Hidde Java doubleможе intsточно представляти всю Java . Див. Специфікацію мови Java, розділ 5.1.2, " Розширення примітивного перетворення ".
Девід Моул

3
@David, Дякую, однак мій аргумент вище все ще зберігається. Самі числа будуть точними поданнями, але результат потужності може не бути. Особливо для великих держав.
Hidde

1
@Hidde Якщо результат потужності відповідає int, це буде точно. Якщо воно не вміщується в int, то десяткове розширення - не ваша проблема, переповнення - ваша проблема.
Девід Моулс

2
import java.util.*;

public class Power {

    public static void main(String args[])
    {
        Scanner sc=new Scanner(System.in);
        int num = 0;
        int pow = 0;
        int power = 0;

        System.out.print("Enter number: ");
        num = sc.nextInt();

        System.out.print("Enter power: ");
        pow = sc.nextInt();

        System.out.print(power(num,pow));
    }

    public static int power(int a, int b)
    {
        int power = 1;

        for(int c = 0; c < b; c++)
            power *= a;

        return power;
    }

}

Покращення forциклу:for (; b > 0; b --)
tckmn

7
серйозне поліпшення: вирівняйте основу і вдвічі зменшіть показник ступеня на кожному кроці
кевін Клін

1

Мені вдалося змінити (межі, навіть перевірка, перевірка від’ємних чисел) відповідь Qx__. Використовуйте на власний ризик. 0 ^ -1, 0 ^ -2 тощо .. повертає 0.

private static int pow(int x, int n) {
        if (n == 0)
            return 1;
        if (n == 1)
            return x;
        if (n < 0) { // always 1^xx = 1 && 2^-1 (=0.5 --> ~ 1 )
            if (x == 1 || (x == 2 && n == -1))
                return 1;
            else
                return 0;
        }
        if ((n & 1) == 0) { //is even 
            long num = pow(x * x, n / 2);
            if (num > Integer.MAX_VALUE) //check bounds
                return Integer.MAX_VALUE; 
            return (int) num;
        } else {
            long num = x * pow(x * x, n / 2);
            if (num > Integer.MAX_VALUE) //check bounds
                return Integer.MAX_VALUE;
            return (int) num;
        }
    }

1

Проста (не перевіряє переповнення або достовірність аргументів) реалізація алгоритму повторного квадратування для обчислення потужності:

/** Compute a**p, assume result fits in a 32-bit signed integer */ 
int pow(int a, int p)
{
    int res = 1;
    int i1 = 31 - Integer.numberOfLeadingZeros(p); // highest bit index
    for (int i = i1; i >= 0; --i) {
        res *= res;
        if ((p & (1<<i)) > 0)
            res *= a;
    }
    return res;
}

Складність часу логарифмічна до показника p (тобто лінійна до кількості бітів, необхідних для подання p).


1

Є деякі проблеми з методом pow:

  1. Ми можемо замінити (y & 1) == 0; з y% 2 == 0
    побітові операції завжди швидші.

Ваш код завжди зменшує y і виконує додаткове множення, включаючи випадки, коли y парне. Краще помістити цю частину в речення else.

public static long pow(long x, int y) {
        long result = 1;
        while (y > 0) {
            if ((y & 1) == 0) {
                x *= x;
                y >>>= 1;
            } else {
                result *= x;
                y--;
            }
        }
        return result;
    }

0

На відміну від Python (де потужності можна обчислити за допомогою ** b), JAVA не має такого швидкого способу досягнення результату степеня двох чисел. Java має функцію pow з класом Math, яка повертає значення Double

double pow(double base, double exponent)

Але ви також можете обчислити степені цілого числа, використовуючи ту саму функцію. У наступній програмі я зробив те саме і нарешті перетворюю результат у ціле число (typecasting). Наслідуйте приклад:

import java.util.*;
import java.lang.*; // CONTAINS THE Math library
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n= sc.nextInt(); // Accept integer n
        int m = sc.nextInt(); // Accept integer m
        int ans = (int) Math.pow(n,m); // Calculates n ^ m
        System.out.println(ans); // prints answers
    }
}

Крім того, java.math.BigInteger.pow(int exponent)функція The повертає велике ціле число, значення якого (цей ^ показник). Показником є ​​ціле число, а не велике ціле число. Приклад:

import java.math.*;
public class BigIntegerDemo {
public static void main(String[] args) {
      BigInteger bi1, bi2; // create 2 BigInteger objects          
      int exponent = 2; // create and assign value to exponent
      // assign value to bi1
      bi1 = new BigInteger("6");
      // perform pow operation on bi1 using exponent
      bi2 = bi1.pow(exponent);
      String str = "Result is " + bi1 + "^" +exponent+ " = " +bi2;
      // print bi2 value
      System.out.println( str );
   }
}

0

Використовуйте наведену нижче логіку для обчислення потужності n значення a.

Зазвичай, якщо ми хочемо обчислити n потужність a. Ми помножимо 'a' на n кількість разів. Складність часу цього підходу буде O (n) Розділити потужність n на 2, обчислити Експонентацію = помножити 'a' лише до n / 2. Подвойте значення. Тепер складність часу зменшується до O (n / 2).

public  int calculatePower1(int a, int b) {
    if (b == 0) {
        return 1;
    }

    int val = (b % 2 == 0) ? (b / 2) : (b - 1) / 2;

    int temp = 1;
    for (int i = 1; i <= val; i++) {
        temp *= a;
    }

    if (b % 2 == 0) {
        return temp * temp;
    } else {
        return a * temp * temp;
    }
}

Дякую, але я шукаю спосіб, який був би вбудований у мову і короткий. Я знаю алгоритм обчислення потужностей.
Hidde

0

base - це число, яке ви хочете підсилити, n - потужність, ми повертаємо 1, якщо n дорівнює 0, і повертаємо базу, якщо n дорівнює 1, якщо умови не виконуються, використовуємо формулу base * (powerN (база, n-1)) наприклад: 2, піднятий до використання цієї формули, це: 2 (база) * 2 (потужністьN (база, n-1)).

public int power(int base, int n){
   return n == 0 ? 1 : (n == 1 ? base : base*(power(base,n-1)));
}


0
import java.util.Scanner;

class Solution {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();

        for (int i = 0; i < t; i++) {

            try {
                long x = sc.nextLong();
                System.out.println(x + " can be fitted in:");
                if (x >= -128 && x <= 127) {
                    System.out.println("* byte");
                }
                if (x >= -32768 && x <= 32767) {
                    //Complete the code
                    System.out.println("* short");
                    System.out.println("* int");
                    System.out.println("* long");
                } else if (x >= -Math.pow(2, 31) && x <= Math.pow(2, 31) - 1) {
                    System.out.println("* int");
                    System.out.println("* long");
                } else {
                    System.out.println("* long");
                }
            } catch (Exception e) {
                System.out.println(sc.next() + " can't be fitted anywhere.");
            }

        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.