我一直无法找到从C++整数向量中获取平均值的方法。
我不可能开始添加所有值,因为我可能会超过可以接受的最大整数值。
如何高效快速地计算? C++语言中是否有任何标准库可以做到这一点?
使用std::accumulate
和足够宽的整数类型进行求和是常用的方法:
double avg1(std::vector<int> const& v) {
return 1.0 * std::accumulate(v.begin(), v.end(), 0LL) / v.size();
}
如果这个求和超过了整型的范围(23百万个整数的平均值至少得是4.01x1011,也就是说,它甚至无法适应一个int32_t
...所以你完全没问题,但如果你有更多数量级的数字或者使用更大的int
类型,就需要使用常见的“在线”算法来计算平均值:
double avg2(std::vector<int> const& v) {
int n = 0;
double mean = 0.0;
for (auto x : v) {
double delta = x - mean;
mean += delta/++n;
}
return mean;
}
这不会溢出,也不太容易失精,但由于需要多次额外的除法计算,可能会更加昂贵。
1
(也可以想出需要更少条目的例子...) - Oliver Charlesworthmpz_class n; // a multi-precision integer,
n = 1; // easy initialize
size_t F = 1000;
for (size_t i=1; i<=F; ++i)
n = n * i;
// show
std::string Fstr = digiComma(n); // inserts comma's
std::cout << "\n" << F << "! = " << Fstr
<< "\n" << digitCnt(Fstr) << " bytes " << std::endl;
2568字节
实际用时0m0.013秒
用户用时0m0.004秒
系统用时0m0.000秒
那么uint64_t有多大呢?我认为可以容纳的最大斐波那契数是Fib(93)。
诀窍在于您不必存储向量的整个总和。您可以在迭代过程中进行整数除法,并将余数存储以添加到下一个值。
这使得可以创建非常内存高效的算法。我没有进行基准测试,但对于具有硬件除法模块的处理器应该是可以的。
这里提供了一种解决方案,只要每个向量元素的el + vector.size()
适合ACCU_T
,就不应该溢出。如果使用处理器溢出标志,应该可以消除此限制。
template<typename T, typename ACCU_T = uintmax_t>
T vec_average(const std::vector<T> &vec)
{
const ACCU_T size = (ACCU_T)vec.size();
T avg = 0;
ACCU_T accu = 0;
for (const T &el : vec)
{
accu += (ACCU_T)el;
avg += (T)(accu / size);
accu %= size;
}
return avg;
}
它不使用任何浮点数或大数。
accu
变量在函数结束时的值为 sum(vec) % vec.size()
。
是的,这里有一个适用于GCC和Clang的版本,不应该对任何无符号整数造成溢出。
(这里的确切限制是el + vector.size()
不能比ACCU_T能容纳的两倍还要大。)
template<typename T, typename ACCU_T = uintmax_t>
T vec_average(const std::vector<T> &vec)
{
const ACCU_T size = (ACCU_T)vec.size();
const T overflowAvg = (T)((ACCU_T(-1)) / size);
const ACCU_T overflowAccu = overflowAvg * size;
T avg = 0;
ACCU_T accu = 0;
for (const T &el : vec)
{
if (__builtin_add_overflow(accu, (ACCU_T)el, &accu))
{
avg += overflowAvg;
accu -= overflowAccu;
}
avg += (T)(accu / size);
accu %= size;
}
return avg;
}
double
进行累加。由于整数的平均值不一定是整数,因此似乎你需要浮点数。 - 5gon12ederint
中(例如使用uint64_t
)。 - Oliver Charlesworthint64_t
。如果您有64位整数,则可以使用__int128_t
或等效类型(假设您的编译器提供它)。 - Cornstalks