2010-05-28 3 views
6

Я сделал несколько тестов по методу pow (exponent). К сожалению, мои математические навыки недостаточно сильны для решения следующей проблемы.java.math.BigInteger pow (экспоненциальный) вопрос

Я использую этот код:

BigInteger.valueOf(2).pow(var); 

Результаты:

  • уаг | время в мс
  • 2000000 |
  • 2500000 |
  • 3000000 | 22379
  • 3500000 | 32147
  • 4000000 |
  • 4500000 |
  • 5000000 | 49922

См.? Показатель 2,500,000 рассчитывается почти так же быстро, как 2 000 000. 4,500,000 рассчитывается намного быстрее, чем 4 000 000.

Почему?

Чтобы дать вам некоторую помощь, вот оригинальная реализация BigInteger.pow (экспоненты):

public BigInteger pow(int exponent) { 
    if (exponent < 0) 
     throw new ArithmeticException("Negative exponent"); 
    if (signum==0) 
     return (exponent==0 ? ONE : this); 

    // Perform exponentiation using repeated squaring trick 
     int newSign = (signum<0 && (exponent&1)==1 ? -1 : 1); 
    int[] baseToPow2 = this.mag; 
     int[] result = {1}; 

    while (exponent != 0) { 
     if ((exponent & 1)==1) { 
     result = multiplyToLen(result, result.length, 
             baseToPow2, baseToPow2.length, null); 
     result = trustedStripLeadingZeroInts(result); 
     } 
     if ((exponent >>>= 1) != 0) { 
       baseToPow2 = squareToLen(baseToPow2, baseToPow2.length, null); 
     baseToPow2 = trustedStripLeadingZeroInts(baseToPow2); 
     } 
    } 
    return new BigInteger(result, newSign); 
    } 
+3

вы сделали миллион прогонов каждого из этих вызовов и усреднить результаты, чтобы получить таблицу, которую вы предоставили? – vicatcu

+1

Сколько пробегов вы усредняете время? –

+2

@vicatcu: Я думаю, можно с уверенностью предположить, что он не ждал 3 года, чтобы получить результаты. –

ответ

9

Алгоритм использует повторное квадратирование (squareToLen) и умножение (multiplyToLen).Время выполнения этих операций зависит от размера задействованных чисел. Умножения больших чисел ближе к концу вычисления намного дороже, чем в начале.

Умножение выполняется только при условии, что это условие верно: ((exponent & 1)==1). Количество квадратных операций зависит от количества бит в числе (за исключением начальных нулей), но умножение требуется только для битов, которые установлены в 1. Легче видеть операции, которые требуются, глядя на двоичный представление числа:

 
2000000: 0000111101000010010000000 
2500000: 0001001100010010110100000 
3000000: 0001011011100011011000000 
3500000: 0001101010110011111100000 
4000000: 0001111010000100100000000 
4500000: 0010001001010101000100000 
5000000: 0010011000100101101000000 

Обратите внимание, что 2.5M и 4.5M повезло в том, что они имеют меньше старшие биты, установленные, чем цифры, окружающих их. В следующий раз это происходит на 8.ом:

 
8000000: 0011110100001001000000000 
8500000: 0100000011011001100100000 
9000000: 0100010010101010001000000 

Сладких пятнами являются точными полномочиями 2.

 
1048575: 0001111111111111111111111 // 16408 ms 
1048576: 0010000000000000000000000 // 6209 ms 
1

Только предположение:

показатель обрабатывается по крупицам, и если наименее значимый бит - 1 дополнительная работа.

Если L представляет собой количество бит в показателе и А число бит, которые являются 1 и t1 времени, чтобы обработать общую часть и t2 дополнительное время обработки, когда LSbit 1

то время работы будет

L t1 + А t2

или время зависит от количества 1-й в двоичном представлении.

сейчас пишу небольшую программу, чтобы проверить мою теорию ...

1

Я не знаю, сколько раз вы запускать тайминги. Как отмечали некоторые из комментаторов, вам нужно много раз выполнять операции во много раз, чтобы получить хорошие результаты (и они все еще могут ошибаться).

Предполагая, что вы хорошо определили время, помните, что есть много ярлыков, которые можно использовать в математике. Вам не нужно делать операции 5 * 5 * 5 * 5 * 5 * 5 для расчета 5^6.

Вот один из способов сделать это гораздо быстрее. http://en.wikipedia.org/wiki/Exponentiation_by_squaring