Commits (2)
  • Sergio Gelato's avatar
    ITS#8204 Remove bias towards the first record in RFC2782 shuffle implementation. · ee7502ac
    Sergio Gelato authored and Quanah Gibson-Mount's avatar Quanah Gibson-Mount committed
    Prior to this change, given two records of weight 1 the algorithm would
    return them in the order (0,1) with 100% probability instead of the
    desired 50%. This was due to an off-by-one error in the range test.
    
    srv_rand() returns a float in the range [0.0, 1.0[, so r is an integer in the
    range [0, total[. The correct probability for record 0 to be chosen is
    a[0].weight/total, not (a[0].weight+1)/total.
    ee7502ac
  • Sergio Gelato's avatar
    ITS#8204 Improved RFC2782 shuffle when several, but not all, records have weight 0. · 8006ee58
    Sergio Gelato authored and Quanah Gibson-Mount's avatar Quanah Gibson-Mount committed
    The fallback to a straight Fisher-Yates shuffle needs to occur whenever the
    sum of the *remaining* weights is zero, or else the remaining records will
    not be reordered. Testing only once at the beginning covers the case when
    all weights are zero, and obviously no shuffling is needed when only one
    weight is zero; but other weight combinations are possible, such as (1, 0, 0).
    8006ee58
......@@ -216,36 +216,27 @@ static void srv_shuffle(srv_record *a, int n) {
for (i=0; i<n; i++)
total += a[i].weight;
/* all weights are zero, do a straight Fisher-Yates shuffle */
if (!total) {
while (n) {
srv_record t;
i = srv_rand() * n--;
t = a[n];
a[n] = a[i];
a[i] = t;
}
return;
}
/* Do a shuffle per RFC2782 Page 4 */
p = n;
for (i=0; i<n-1; i++) {
r = srv_rand() * total;
for (j=0; j<p; j++) {
r -= a[j].weight;
if (r <= 0) {
if (j) {
srv_record t = a[0];
a[0] = a[j];
a[j] = t;
for (p=n; p>1; a++, p--) {
if (!total) {
/* all remaining weights are zero,
do a straight Fisher-Yates shuffle */
j = srv_rand() * p;
} else {
r = srv_rand() * total;
for (j=0; j<p; j++) {
r -= a[j].weight;
if (r < 0) {
total -= a[j].weight;
break;
}
total -= a[0].weight;
a++;
p--;
break;
}
}
if (j && j<p) {
srv_record t = a[0];
a[0] = a[j];
a[j] = t;
}
}
}
#endif /* HAVE_RES_QUERY */
......