Libraryless. Click here for Pure Java version (10590L/59K).
1 | // As long as you don't access the by-numeral-index functions, |
2 | // you can add and remove elements quickly. |
3 | // Upon accessing the list, it is updated once to fit the new data. |
4 | |
5 | // Note: When adding the same element twice, it will be stored only |
6 | // once with the higher probabilities of the two. |
7 | // |
8 | // class is synchronized |
9 | sclass ProbabilisticList<A> extends AbstractRandomAccessList<WithProbability<A>> { |
10 | // these are the 2 main data structures we update |
11 | Map<A, Double> probabilities = hashMap(); |
12 | MultiSetMap<Double, A> byProbability |
13 | = multiSetMap_outerDescTreeMap_innerCompactLinkedHashSet(); |
14 | // = multiSetMap_outerDescTreeMap_innerBetterLinkedHashSet(); |
15 | |
16 | L<WithProbability<A>> list; // this is only updated when requested |
17 | |
18 | // CUSTOMIZATION |
19 | |
20 | // must be >= 0. probability 0 is never added |
21 | double cutoffProbabilityOnAdd = 0; |
22 | |
23 | Cl<ProbabilisticListListener<A>> listeners; |
24 | |
25 | *() {} |
26 | *(Iterable<WithProbability<A>> l) { |
27 | main addAll(this, l); |
28 | } |
29 | |
30 | public synchronized WithProbability<A> get(int idx) { |
31 | if (idx < 0 || idx >= size()) null; |
32 | makeListUpToIndex(idx); |
33 | ret list.get(idx); |
34 | } |
35 | |
36 | public synchronized int size() { ret l(probabilities); } |
37 | |
38 | // assuming the index is within range |
39 | void makeListUpToIndex(int idx) { |
40 | if (idx < l(list)) ret; // index already there |
41 | |
42 | if (list == null) list = new L; |
43 | |
44 | WithProbability<A> lastEntry = main last(list); |
45 | A a = lastEntry?!; |
46 | double probability = lastEntry == null ? 0 : lastEntry.probability(); |
47 | |
48 | //print("Making list from " + l(list) + " to " + idx); |
49 | |
50 | while (idx >= l(list)) { |
51 | //printVars_str(+idx, +a, +probability); |
52 | if (a == null) { |
53 | probability = byProbability.firstKey(); |
54 | a = first(byProbability.get(probability)); |
55 | } else { |
56 | CompactLinkedHashSet<A> setForProbability = cast byProbability.get(probability); |
57 | a = setForProbability.nextElement(a); |
58 | //printVars_str(+probability, +setForProbability, +a); |
59 | if (a == null) { |
60 | probability = byProbability.higherKey(probability); |
61 | a = first(byProbability.get(probability)); |
62 | } |
63 | } |
64 | list.add(WithProbability(probability, a)); |
65 | } |
66 | } |
67 | |
68 | synchronized bool containsElement(A a) { |
69 | ret probabilities.containsKey(a); |
70 | } |
71 | |
72 | public bool add aka put(A a, double probability) { |
73 | ret add(WithProbability(probability, a)); |
74 | } |
75 | |
76 | public bool add(WithProbability<A> wp) { |
77 | A a; |
78 | double newP; |
79 | |
80 | synchronized { |
81 | if (wp == null || wp.probability() <= cutoffProbabilityOnAdd) false; |
82 | a = wp!; |
83 | newP = wp.probability(); |
84 | Double oldP = probabilities.get(a); |
85 | if (oldP != null) { |
86 | if (oldP.doubleValue() >= newP) |
87 | false; |
88 | else |
89 | remove(WithProbability<A>(oldP, a)); |
90 | } |
91 | |
92 | clearListFrom(newP); |
93 | probabilities.put(a, newP); |
94 | byProbability.add(newP, a); |
95 | } |
96 | |
97 | fireElementAdded(a, newP); |
98 | true; |
99 | } |
100 | |
101 | // will also delete if given probability doesn't match |
102 | @Override |
103 | public bool remove(O wp) { |
104 | if (wp == null) false; |
105 | cast wp to WithProbability<A>; |
106 | ret removeElement(wp!); |
107 | } |
108 | |
109 | public synchronized bool removeElement(A a) { |
110 | if (a == null) false; |
111 | Double oldP = probabilities.get(a); |
112 | if (oldP == null) false; |
113 | clearListFrom(oldP); |
114 | probabilities.remove(a); |
115 | byProbability.remove(oldP, a); |
116 | true; |
117 | } |
118 | |
119 | @Override |
120 | public synchronized WithProbability<A> remove(int idx) { |
121 | var a = get(idx); |
122 | remove(a); |
123 | ret a; |
124 | } |
125 | |
126 | // internal |
127 | void clearListFrom(double p) { |
128 | int idx = generalizedBinarySearch2(list, a -> a.probability() <= p ? 1 : -1); |
129 | idx = -idx-1; |
130 | //printVars clearListFrom(p, idx, before := takeFirst(idx, list), after := main subList(idx, list)); |
131 | truncateList(list, idx); |
132 | } |
133 | |
134 | void at(double p, A a) { |
135 | add(WithProbability(p, a)); |
136 | } |
137 | |
138 | // faster version of truncateList |
139 | void truncate(int size) { |
140 | if (size < 0) fail(); |
141 | while (size() > size) |
142 | removeLast(); |
143 | } |
144 | |
145 | double getProbability(A a) { |
146 | ret orZero(probabilities.get(a)); |
147 | } |
148 | |
149 | void truncateBelow(double thresholdProbability) { |
150 | A a; |
151 | while ((a = last()) != null && getProbability(a) < thresholdProbability) |
152 | removeElement(a); |
153 | } |
154 | |
155 | A last() { |
156 | Double last = byProbability.lastKey(); |
157 | if (last == null) null; |
158 | CompactLinkedHashSet<A> set = cast byProbability.getOpt(last); |
159 | ret set.last(); |
160 | } |
161 | |
162 | void removeLast { |
163 | removeElement(last()); |
164 | } |
165 | |
166 | int indexOfElement(A a) { |
167 | // quickly bail if element is not contained |
168 | double p = getProbability(a); |
169 | if (p == 0) ret -1; |
170 | |
171 | // TODO: binary search and stuff |
172 | int n = size(); |
173 | for i to n: |
174 | if (eq(a, get(i)!)) |
175 | ret i; |
176 | fail("Internal error"); |
177 | } |
178 | |
179 | // elements without probabilities |
180 | L<A> rawList aka elementsOnly() { |
181 | ret lazyMap_noSave(this, a -> a!); |
182 | } |
183 | |
184 | // just the probabilities in the list |
185 | L<Double> probabilities() { |
186 | ret lazyMap_noSave(this, a -> a.probability()); |
187 | } |
188 | |
189 | L<ProbabilisticListListener<A>> listenersList() { |
190 | if (listeners == null) null; |
191 | synchronized { ret cloneList(listeners); } |
192 | } |
193 | |
194 | void fireElementAdded(A a, double probability) { |
195 | main forEach(listenersList(), l -> l.elementAdded(a, probability)); |
196 | } |
197 | |
198 | synchronized void addListener(ProbabilisticListListener<A> l) { |
199 | listeners = addOrCreate(listeners, l); |
200 | } |
201 | |
202 | synchronized void removeListener(ProbabilisticListListener<A> l) { |
203 | listeners?.remove(l); |
204 | } |
205 | |
206 | void onElementAdded(IVF2<A, Double> f) { |
207 | if (f != null) |
208 | addListener(new ProbabilisticListAdapter<A> { |
209 | @Override |
210 | public void elementAdded(A a, double probability) { |
211 | f.get(a, probability); |
212 | } |
213 | }); |
214 | } |
215 | |
216 | } |
Began life as a copy of #1028471
download show line numbers debug dex old transpilations
Travelled to 4 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt
No comments. add comment
Snippet ID: | #1031504 |
Snippet name: | ProbabilisticList - list ordered by probability [OK!] |
Eternal ID of this version: | #1031504/56 |
Text MD5: | e80072181788c6849cd110b70f422091 |
Transpilation MD5: | 4ec8bbcefbe8fbbebb8d855dc21e23d1 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-09-02 20:28:57 |
Source code size: | 6028 bytes / 216 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 366 / 834 |
Version history: | 55 change(s) |
Referenced in: | [show references] |