108 const std::vector<T>& branchVars, std::size_t k) {
110 "fj_quorum_moments requires transcendental arithmetic");
111 const std::size_t n = branchMeans.size();
112 if (n != branchVars.size())
113 throw InputError(
"fj_quorum_moments: branchMeans and branchVars must have the same length");
115 if (n == 0)
return {zero, zero};
116 if (k < 1 || k > n)
throw InputError(
"fj_quorum_moments: k must satisfy 1 <= k <= n");
119 "fj_quorum_moments: too many branches; evaluation is cubic in the branch count");
121 std::vector<detail::StepCdf<T>> branches(n);
122 for (std::size_t i = 0; i < n; ++i) {
123 branches[i] = detail::three_point_fit(branchMeans[i], branchVars[i]);
124 if (branches[i].t.empty()) {
125 branches[i].t.push_back(zero);
126 branches[i].A.push_back(one);
131 for (std::size_t i = 0; i < n; ++i) grid.insert(grid.end(), branches[i].t.begin(), branches[i].t.end());
132 std::sort(grid.begin(), grid.end());
133 grid.erase(std::unique(grid.begin(), grid.end()), grid.end());
134 const std::size_t ngrid = grid.size();
138 for (std::size_t i = 0; i < n; ++i) {
141 for (std::size_t g = 0; g < ngrid; ++g) {
142 while (p < branches[i].t.size() && branches[i].t[p] <= grid[g]) {
143 cur = branches[i].A[p];
150 std::vector<T> A(ngrid, zero);
151 for (std::size_t g = 0; g < ngrid; ++g) {
152 std::vector<T> q(n + 1, zero);
154 for (std::size_t i = 0; i < n; ++i) {
155 const T f = fv(i, g);
156 for (std::size_t j = std::min(i + 1, n); j >= 1; --j) q[j] = q[j - 1] * f + q[j] * (one - f);
160 for (std::size_t j = k; j <= n; ++j) tail += q[j];
164 T m = zero, prev = zero;
165 for (std::size_t g = 0; g < ngrid; ++g) {
166 m += (A[g] - prev) * grid[g];
171 for (std::size_t g = 0; g < ngrid; ++g) {
172 const T d = grid[g] - m;
173 v += (A[g] - prev) * d * d;
176 if (v < zero) v = zero;
FJQuorumMomentsResult< T > fj_quorum_moments(const std::vector< T > &branchMeans, const std::vector< T > &branchVars, std::size_t k)
Mean and variance of a k-of-n (quorum) join completion time, from the mean and variance of each branc...