1- package com .lewdev .probabilitylib ;
2-
3- import java .util .LinkedHashMap ;
4- import java .util .Map ;
5- import java .util .Map .Entry ;
6- import java .util .concurrent .ThreadLocalRandom ;
7- import java .util .stream .Collectors ;
8-
9- /**
10- * ProbabilityMap to easily handle probability <br>
11- * <br>
12- *
13- * <b>Selection Algorithm Implementation</b>:
14- * <p>
15- * <ul>
16- * <li>Elements have a "box" of space, sized based on their probability share
17- * <li>"Boxes" start from index 1 and end at the total probability of elements
18- * <li>A random number is selected between 1 and the total probability
19- * <li>Which "box" the random number falls in is the element that is selected
20- * <li>Therefore "boxes" with larger probability have a greater chance of being
21- * selected than those with smaller probability.
22- * </p>
23- * </ul>
24- *
25- * @param <E> Type of elements
26- *
27- * @author Lewys Davies
28- */
29- public class ProbabilityMap < E > {
30-
31- private LinkedHashMap < E , Integer > map = new LinkedHashMap <>();
32-
33- private int totalProbability = 0 ;
34-
35- /**
36- * Construct a empty probability map
37- */
38- public ProbabilityMap () {
39- }
40-
41- /**
42- * Construct a probability map with initial elements
43- *
44- * @param elements
45- */
46- public ProbabilityMap (Map <E , Integer > elements ) {
47- this .addAll (elements );
48- }
49-
50- /**
51- * Add a element to the map
52- *
53- * @param element
54- * @param probability x > 0
55- */
56- public final boolean add (E element , int probability ) {
57- this .map .put (element , probability );
58- this .updateState ();
59- return true ;
60- }
61-
62- /**
63- * Add all elements from a different map to this one
64- *
65- * @param elements
66- */
67- public final void addAll (Map <E , Integer > elements ) {
68- this .map .putAll (elements );
69- this .updateState ();
70- }
71-
72- /**
73- * Get a random element from the map
74- *
75- * @return Random element based on probability | null if map is empty
76- */
77- public final E get () {
78- if (this .map .isEmpty ())
79- return null ;
80-
81- // Map is sorted when changed
82- // Therefore probability of elements is already in descending order: i.e. 5, 5,
83- // 4, 1, 1
84-
85- // Random int between 1 and total probability (+1 as nextInt bound is exclusive)
86- int randomProb = ThreadLocalRandom .current ().nextInt (1 , this .totalProbability + 1 );
87-
88- int cumulativeProb = 0 ;
89- E selectedElm = null ;
90-
91- for (Entry <E , Integer > entry : this .map .entrySet ()) {
92- // Calculate the size of this elements box: i.e 1-5, 6-10, 11-14, 15, 16
93- int boxStart = cumulativeProb + 1 ;
94- int boxEnd = boxStart + (entry .getValue () - 1 );
95-
96- // Check if the elements box falls within the randomly chosen index
97- if (randomProb >= boxStart && randomProb <= boxEnd ) {
98- selectedElm = entry .getKey ();
99- break ;
100- }
101-
102- // If not keep searching
103- cumulativeProb = boxEnd ;
104- }
105-
106- return selectedElm ;
107- }
108-
109- /**
110- * Remove a element from the map
111- *
112- * @param element
113- */
114- public final void remove (E element ) {
115- this .map .remove (element );
116- this .updateState ();
117- }
118-
119- /**
120- * Remove all elements from the map
121- */
122- public final void clear () {
123- this .map .clear ();
124- this .updateState ();
125- }
126-
127- /**
128- * @return Sum of all the element's probability
129- */
130- public final int getTotalProbability () {
131- return this .totalProbability ;
132- }
133-
134- private final void updateState () {
135- // Update total probability cache
136- this .totalProbability = this .map .values ().stream ().mapToInt (Integer ::intValue ).sum ();
137-
138- // Sort LinkedHashMap
139- this .map = this .map .entrySet ().stream ().sorted (Map .Entry .<E , Integer >comparingByValue ().reversed ())
140- .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue , (x , y ) -> y , LinkedHashMap ::new ));
141- }
1+ package com .lewdev .probabilitylib ;
2+
3+ import java .util .LinkedHashMap ;
4+ import java .util .Map ;
5+ import java .util .Map .Entry ;
6+ import java .util .concurrent .ThreadLocalRandom ;
7+ import java .util .stream .Collectors ;
8+
9+ /**
10+ * ProbabilityMap to easily handle probability <br>
11+ * <br>
12+ *
13+ * <b>Selection Algorithm Implementation</b>:
14+ * <p>
15+ * <ul>
16+ * <li>Elements have a "box" of space, sized based on their probability share
17+ * <li>"Boxes" start from index 1 and end at the total probability of elements
18+ * <li>A random number is selected between 1 and the total probability
19+ * <li>Which "box" the random number falls in is the element that is selected
20+ * <li>Therefore "boxes" with larger probability have a greater chance of being
21+ * selected than those with smaller probability.
22+ * </p>
23+ * </ul>
24+ *
25+ * @param <E> Type of elements
26+ * @version 0.5
27+ *
28+ * @author Lewys Davies
29+ */
30+ public class ProbabilityMap < E > {
31+
32+ private LinkedHashMap < E , Integer > map = new LinkedHashMap <>();
33+
34+ private int totalProbability = 0 ;
35+
36+ /**
37+ * Construct a empty probability map
38+ */
39+ public ProbabilityMap () { }
40+
41+ /**
42+ * Construct a probability map with initial elements
43+ *
44+ * @param elements
45+ */
46+ public ProbabilityMap (Map <E , Integer > elements ) {
47+ this .addAll (elements );
48+ }
49+
50+ /**
51+ * Add a element to the map
52+ *
53+ * @param element
54+ * @param probability x > 0
55+ */
56+ public final boolean add (E element , int probability ) {
57+ this .map .put (element , probability );
58+ this .updateState ();
59+ return true ;
60+ }
61+
62+ /**
63+ * Add all elements from a different map to this one
64+ *
65+ * @param elements
66+ */
67+ public final void addAll (Map <E , Integer > elements ) {
68+ this .map .putAll (elements );
69+ this .updateState ();
70+ }
71+
72+ /**
73+ * Get a random element from the map
74+ *
75+ * @return Random element based on probability | null if map is empty
76+ */
77+ public final E get () {
78+ if (this .map .isEmpty ())
79+ return null ;
80+
81+ // Map is sorted when changed
82+ // Therefore probability of elements is already in descending order: i.e. 5, 5,
83+ // 4, 1, 1
84+
85+ // Random int between 1 and total probability (+1 as nextInt bound is exclusive)
86+ int randomProb = ThreadLocalRandom .current ().nextInt (1 , this .totalProbability + 1 );
87+
88+ int cumulativeProb = 0 ;
89+ E selectedElm = null ;
90+
91+ for (Entry <E , Integer > entry : this .map .entrySet ()) {
92+ // Calculate the size of this elements box: i.e 1-5, 6-10, 11-14, 15, 16
93+ int boxStart = cumulativeProb + 1 ;
94+ int boxEnd = boxStart + (entry .getValue () - 1 );
95+
96+ // Check if the elements box falls within the randomly chosen index
97+ if (randomProb >= boxStart && randomProb <= boxEnd ) {
98+ selectedElm = entry .getKey ();
99+ break ;
100+ }
101+
102+ // If not keep searching
103+ cumulativeProb = boxEnd ;
104+ }
105+
106+ return selectedElm ;
107+ }
108+
109+ /**
110+ * Remove a element from the map
111+ *
112+ * @param element
113+ */
114+ public final void remove (E element ) {
115+ this .map .remove (element );
116+ this .updateState ();
117+ }
118+
119+ /**
120+ * Remove all elements from the map
121+ */
122+ public final void clear () {
123+ this .map .clear ();
124+ this .updateState ();
125+ }
126+
127+ /**
128+ * @return Sum of all the element's probability
129+ */
130+ public final int getTotalProbability () {
131+ return this .totalProbability ;
132+ }
133+
134+ private final void updateState () {
135+ // Update total probability cache
136+ this .totalProbability = this .map .values ().stream ().mapToInt (Integer ::intValue ).sum ();
137+
138+ // Sort LinkedHashMap
139+ this .map = this .map .entrySet ().stream ().sorted (Map .Entry .<E , Integer >comparingByValue ().reversed ())
140+ .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue , (x , y ) -> y , LinkedHashMap ::new ));
141+ }
142142}
0 commit comments