Skip to content

batch_inverse.hpp

SECTIONMath INCLUDEnoya/math/batch_inverse.hpp

只做一次除法和线性次乘法,批量求一组非零域元素的逆元。

\[ \displaystyle y_i = x_i^{-1} \]

Complexity: Time: \(O(n)\) field operations, one division for nonempty input. Space: \(O(n)\) output and \(O(1)\) additional workspace.

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include。

/// @complexity Time: O(n) field operations, one division for nonempty input.
/// Space: O(n) output and O(1) additional workspace.

#include <array>
#include <cassert>
#include <vector>

namespace noya {

/// @brief Invert nonzero field elements with four independent product chains.
/// Each chain contains indices with the same remainder modulo four.
template <class T> std::vector<T> batch_inverse(const std::vector<T> &vs) {
  if (vs.empty()) return {};
  int n = int(vs.size());
  std::vector<T> res(n);
  std::array<T, 4> prod;
  prod.fill(T(1));
#pragma GCC unroll 4
  for (int i = 0; i < n; ++i) {
    assert(vs[i] != T{});
    res[i] = prod[i & 3];
    prod[i & 3] *= vs[i];
  }
  // Invert the four chain products together, using just one division.
  auto last = prod;
  for (int i = 1; i < 4; ++i) prod[i] *= prod[i - 1];
  T inv = T(1) / prod[3];
  for (int i = 3; i > 0; --i) {
    prod[i] = prod[i - 1] * inv;
    inv *= last[i];
  }
  prod[0] = inv;
#pragma GCC unroll 4
  for (int i = n - 1; i >= 0; --i) {
    res[i] *= prod[i & 3];
    prod[i & 3] *= vs[i];
  }
  return res;
}

} // namespace noya

 // NOYA_BATCH_INVERSE_HPP
#ifndef NOYA_BATCH_INVERSE_HPP
#define NOYA_BATCH_INVERSE_HPP 1

/// @complexity Time: O(n) field operations, one division for nonempty input.
/// Space: O(n) output and O(1) additional workspace.

#include <array>
#include <cassert>
#include <vector>

namespace noya {

/// @brief Invert nonzero field elements with four independent product chains.
/// Each chain contains indices with the same remainder modulo four.
template <class T> std::vector<T> batch_inverse(const std::vector<T> &vs) {
  if (vs.empty()) return {};
  int n = int(vs.size());
  std::vector<T> res(n);
  std::array<T, 4> prod;
  prod.fill(T(1));
#pragma GCC unroll 4
  for (int i = 0; i < n; ++i) {
    assert(vs[i] != T{});
    res[i] = prod[i & 3];
    prod[i & 3] *= vs[i];
  }
  // Invert the four chain products together, using just one division.
  auto last = prod;
  for (int i = 1; i < 4; ++i) prod[i] *= prod[i - 1];
  T inv = T(1) / prod[3];
  for (int i = 3; i > 0; --i) {
    prod[i] = prod[i - 1] * inv;
    inv *= last[i];
  }
  prod[0] = inv;
#pragma GCC unroll 4
  for (int i = n - 1; i >= 0; --i) {
    res[i] *= prod[i & 3];
    prod[i & 3] *= vs[i];
  }
  return res;
}

} // namespace noya

#endif // NOYA_BATCH_INVERSE_HPP
/// @complexity Time: O(n) field operations, one division for nonempty input.
/// Space: O(n) output and O(1) additional workspace.

#include <array>
#include <cassert>
#include <vector>

namespace noya {

/// @brief Invert nonzero field elements with four independent product chains.
/// Each chain contains indices with the same remainder modulo four.
template <class T> std::vector<T> batch_inverse(const std::vector<T> &vs) {
  if (vs.empty()) return {};
  int n = int(vs.size());
  std::vector<T> res(n);
  std::array<T, 4> prod;
  prod.fill(T(1));
#pragma GCC unroll 4
  for (int i = 0; i < n; ++i) {
    assert(vs[i] != T{});
    res[i] = prod[i & 3];
    prod[i & 3] *= vs[i];
  }
  // Invert the four chain products together, using just one division.
  auto last = prod;
  for (int i = 1; i < 4; ++i) prod[i] *= prod[i - 1];
  T inv = T(1) / prod[3];
  for (int i = 3; i > 0; --i) {
    prod[i] = prod[i - 1] * inv;
    inv *= last[i];
  }
  prod[0] = inv;
#pragma GCC unroll 4
  for (int i = n - 1; i >= 0; --i) {
    res[i] *= prod[i & 3];
    prod[i & 3] *= vs[i];
  }
  return res;
}

} // namespace noya

 // NOYA_BATCH_INVERSE_HPP