Самое быстрое вычисление суммы x^5 + x^4 + x^3...+x^0 (возможно побитовое) при x=16

для макета дерева, который использует преимущества предварительной выборки строки кэша (reading _next_ cacheline дешево), мне нужно решить расчет адреса очень быстрым способом. Я смог свести проблему к следующему:

newIndex = nowIndex + 1 + (localChildIndex*X)

x будет, например: X = 45 + 44 + 43 + 42 +40.

примечание: 4 является фактором ветвления. На самом деле это будет 16, поэтому сила 2. Это должно быть полезно для использования bitwise вещи?

было бы очень плохо, если бы для вычисления X (performancewise) и такие вещи, как деление/умножение. Это интересная проблема, которую я не смог придумать какой-то хороший способ ее вычисления.

поскольку его часть обхода дерева, возможны 2 режима: абсолютный расчет, независимый от предыдущих вычислений и инкрементный расчет, который начинается с высокого X, хранящегося в переменной, а затем некоторые минимальные вещи, сделанные с ним с каждым более глубоким уровнем дерева.

надеюсь, я смог прояснить, что должна делать математика. Не уверен, что есть способ сделать это быстро и без цикла, но, возможно, кто - то может придумать действительно умное решение. Я хотел бы поблагодарить всех за помощь-StackOverflow были отличным учителем для меня в прошлом, и я надеюсь, что смогу дать больше в будущем, поскольку мои знания растут.

1 ответов


Я отвечу на это во все возрастающей сложности и общности.

  • если x фиксируется на 16, то просто используйте постоянное значение 1118481. Ура! (Назовите его, использование магических чисел-плохая практика)

  • если у вас есть несколько случаев, известных во время компиляции использовать несколько констант или даже определяет, например:

    #define X_2 63
    #define X_4 1365
    #define X_8 37449
    #define X_16 1118481
    ...
    
  • если во время выполнения известно несколько случаев, инициализируйте и используйте индексированную таблицу подстановки с экспонентой.

    int _X[MAX_EXPONENT]; // note: give it a more meaningful name :)
    

    инициализируйте его, а затем просто получите доступ с известным показателем 2^exp во время выполнения.

    newIndex = nowIndex + 1 + (localChildIndex*_X[exp]);
    
  • как эти значения предварительно вычисляются или как их эффективно вычислять на лету: Сумма X = x^n + x^(n - 1) + ... + x^1 + x^0 это геометрическая серия и его конечная сумма составляет:

    X = x^n + x^(n - 1) + ... + x^1 + x^0 = (1-x^(n + 1))/(1-x)
    
  • о побитовых операциях, как заявил Оли Чарльзуорт, если x-степень 2 (в двоичном 0..010..Ноль) x^n также является степенью 2, а сумма разные полномочия двух эквивалентны операции или. Таким образом, мы могли бы сделать такое выражение как:

    пусть exp быть показателем так, что x = 2^exp. (Для 16, exp = 4). Затем:

    X = x^5 + ... + x^1 + x^0
    X = (2^exp)^5 + ... + (2^exp)^1 + 1
    X = 2^(exp*5) + ... + 2^(exp*1) + 1
    

    теперь с помощью побитовых 2^n = 1<<n

    X = 1<<(exp*5) | ... | 1<<exp | 1
    

    In C:

    int X;
    int exp = 4; //for x == 16
    X = 1 << (exp*5) | 1 << (exp*4) | 1 << (exp*3) | 1 << (exp*2) | 1 << (exp*1) | 1;
    
  • и, наконец, я не могу удержаться, чтобы не сказать: если бы ваше выражение было более сложным, и вы должны были оценить произвольный многочлен a_n*x^n + ... + a_1*x^1 + a_0 в x вместо реализации очевидного цикла более быстрый способ оценки многочлена использует Горнера.