You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: api.bs
+35-17Lines changed: 35 additions & 17 deletions
Original file line number
Diff line number
Diff line change
@@ -1701,27 +1701,45 @@ To perform <dfn>common matching logic</dfn>, given
1701
1701
1702
1702
<div algorithm>
1703
1703
To <dfn>fairly allocate credit</dfn>, given a [=list=] of doubles |credit| and an integer |value|:
1704
+
1. [=Assert=]: |credit| is not [=list/is empty|empty=].
1704
1705
1. Let |sumCredit| be the sum of |credit|'s [=list/items=].
1705
-
1. Let |rawNormalizedCredit| be a new [=list=].
1706
-
1. Let |normalizedCredit| be a new [=list=].
1706
+
1. Let |roundedCredit| be a new [=list=].
1707
1707
1. [=list/iterate|For each=] |item| of |credit|:
1708
-
1. Let |rawNormalized| be |value| * |item| / |sumCredit|.
1709
-
1. [=list/Append=] |rawNormalized| to |rawNormalizedCredit|.
1710
-
1. Let |normalized| be |rawNormalized| rounded towards positive Infinity and converted to an integer.
1711
-
1. [=list/Append=] |normalized| to |normalizedCredit|.
1712
-
1. Let |shuffledFractionalIndices| be [=list/get the indices|the indices=] of |credit|, ordered randomly.
1713
-
1. [=list/remove|Remove=] all indices |i| from |shuffledFractionalIndices| where |rawNormalizedCredit|[|i|] has no fractional part.
1714
-
1. [=list/iterate|For each=] |index| of |shuffledFractionalIndices|:
1715
-
1. If the sum of |normalizedCredit|'s [=list/items=] is equal to |value|, [=iteration/break=].
1716
-
1. Decrement |normalizedCredit|[|index|] by 1.
1717
-
1. Return |normalizedCredit|.
1708
+
1. Let |normalizedCredit| be |value| * |item| / |sumCredit|.
1709
+
1. [=list/append|Append=] |normalizedCredit| to |roundedCredit|.
1710
+
1. Let |idx1| be 0.
1711
+
1. [=list/iterate|For each=] |n| of [=list/get the indices=] of |roundedCredit|, removing the first [=list/item=]:
1712
+
1. Let |idx2| be |n|.
1713
+
1. Let |frac1| be |roundedCredit|[|idx1|] − floor(|roundedCredit|[|idx1|]).
1714
+
1. Let |frac2| be |roundedCredit|[|idx2|] − floor(|roundedCredit|[|idx2|]).
1715
+
1. If |frac1| and |frac2| are both equal to zero, [=iteration/continue=].
1716
+
1. If |frac1| + |frac2| is greater than 1, let |incr1| be 1 − |frac1| and |incr2| be 1 − |frac2|.
1717
+
1. Otherwise, let |incr1| be −|frac1| and |incr2| be −|frac2|.
1718
+
<p class=note>|incr1| denotes the amount to increment |roundedCredit|[|idx1|] so that it is integral, and similar for |incr2| and |idx2|.
1719
+
Note that these values can be negative.
1720
+
1721
+
1. Let |p1| be |incr2| / (|incr1| + |incr2|).
1722
+
<p class=note>The value |p1| denotes the probability that the [=list/item=] in |idx1| is rounded to an integer.
1723
+
Divide by zero occurs when |incr1| + |incr2| is 0, which is only possible if both |frac1| and |frac2|
1724
+
are integers (either both 0 or 1 exactly). In this case |idx2| does not need to be rounded, so we just skip it.
1725
+
1726
+
1. Let |r| be a random double between 0 and 1 (inclusive).
1727
+
1. If |r| is less than |p1|, let |incr| be |incr1| and swap the values of |idx1| and |idx2|.
1728
+
1. Otherwise, let |incr| be |incr2|.
1729
+
1. Increment |roundedCredit|[|idx2|] by |incr|.
1730
+
1. Decrement |roundedCredit|[|idx1|] by |incr|.
1731
+
1. Let |integerCredit| be the result of converting [=list/iterate|every=][=list/item=] from |roundedCredit| into an integer by rounding to the nearest integer,
1732
+
and rounding halfway cases away from zero.
1733
+
1. Return |integerCredit|.
1734
+
<p class=note>This final rounding step is only here in cases where tiny floating-point addition and subtraction errors do not completely remove
1735
+
the fractional part for |roundedCredit|[|idx1|]. The rounding mode at half will never actually occur, and is chosen to match C++ <code>std::round</code>
1736
+
behavior for ease of implementation.
1737
+
1718
1738
<p class=note>The algorithm aims to 1) allocate exactly |value| total value across assignments,
1719
-
2) never bias towards any particular credit assignment in rounding, and
1739
+
2) maintain the property that the expected value of |integerCredit| is exactly equal to the normalized credit
0 commit comments