Skip to content

Commit d4b61b0

Browse files
committed
innit commit
0 parents  commit d4b61b0

File tree

7 files changed

+695
-0
lines changed

7 files changed

+695
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Lewys Davies
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Java-Probability-Collection
2+
Generic and Highly Optimised Java Data-Structure for Retrieving Random Elements With Probability

pom.xml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.lewdev</groupId>
7+
<artifactId>probability-lib</artifactId>
8+
<version>0.5</version>
9+
<packaging>jar</packaging>
10+
11+
<name>probability-lib</name>
12+
<url>http://example.com</url>
13+
14+
<properties>
15+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
16+
<java.version>1.8</java.version>
17+
<jmh.version>1.23</jmh.version>
18+
</properties>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>org.junit.jupiter</groupId>
23+
<artifactId>junit-jupiter-engine</artifactId>
24+
<version>5.1.0</version>
25+
<scope>test</scope>
26+
</dependency>
27+
28+
<dependency>
29+
<groupId>org.hamcrest</groupId>
30+
<artifactId>hamcrest-core</artifactId>
31+
<version>1.3</version>
32+
<scope>test</scope>
33+
</dependency>
34+
35+
<dependency>
36+
<groupId>org.openjdk.jmh</groupId>
37+
<artifactId>jmh-core</artifactId>
38+
<version>${jmh.version}</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.openjdk.jmh</groupId>
42+
<artifactId>jmh-generator-annprocess</artifactId>
43+
<version>${jmh.version}</version>
44+
</dependency>
45+
</dependencies>
46+
47+
<build>
48+
<plugins>
49+
<plugin>
50+
<groupId>org.apache.maven.plugins</groupId>
51+
<artifactId>maven-compiler-plugin</artifactId>
52+
<version>3.1</version>
53+
<configuration>
54+
<source>${java.version}</source>
55+
<target>${java.version}</target>
56+
</configuration>
57+
</plugin>
58+
59+
<plugin>
60+
<groupId>org.apache.maven.plugins</groupId>
61+
<artifactId>maven-shade-plugin</artifactId>
62+
<version>3.2.0</version>
63+
<executions>
64+
<execution>
65+
<phase>package</phase>
66+
<goals>
67+
<goal>shade</goal>
68+
</goals>
69+
<configuration>
70+
<finalName>benchmarks</finalName>
71+
<transformers>
72+
<transformer
73+
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
74+
<mainClass>org.openjdk.jmh.Main</mainClass>
75+
</transformer>
76+
</transformers>
77+
</configuration>
78+
</execution>
79+
</executions>
80+
</plugin>
81+
</plugins>
82+
</build>
83+
</project>
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package com.lewdev.probabilitylib;
2+
3+
import java.util.Comparator;
4+
import java.util.Iterator;
5+
import java.util.TreeSet;
6+
import java.util.concurrent.ThreadLocalRandom;
7+
8+
/**
9+
* ProbabilityCollection for retrieving random elements based on probability.
10+
* <br>
11+
* <br>
12+
* <b>Selection Algorithm Implementation</b>:
13+
* <p>
14+
* <ul>
15+
* <li>Elements have a "block" of space, sized based on their probability share
16+
* <li>"Blocks" start from index 1 and end at the total probability of all elements
17+
* <li>A random number is selected between 1 and the total probability
18+
* <li>Which "block" the random number falls in is the element that is selected
19+
* <li>Therefore "block"s with larger probability have a greater chance of being
20+
* selected than those with smaller probability.
21+
* </p>
22+
* </ul>
23+
*
24+
* @author Lewys Davies
25+
*
26+
* @param <E> Type of elements
27+
*/
28+
public class ProbabilityCollection<E> {
29+
30+
protected final Comparator<ProbabilitySetElement<E>> comparator =
31+
(o1, o2)-> Integer.compare(o1.getIndex(), o2.getIndex());
32+
33+
private final TreeSet<ProbabilitySetElement<E>> collection;
34+
35+
private int totalProbability;
36+
37+
/**
38+
* Construct a new Probability Collection
39+
*/
40+
public ProbabilityCollection() {
41+
this.collection = new TreeSet<>(this.comparator);
42+
this.totalProbability = 0;
43+
}
44+
45+
/**
46+
* @return Number of objects inside the collection
47+
*/
48+
public int size() {
49+
return this.collection.size();
50+
}
51+
52+
/**
53+
* @return Collection contains no elements
54+
*/
55+
public boolean isEmpty() {
56+
return this.collection.isEmpty();
57+
}
58+
59+
/**
60+
* @param object
61+
* @return True if the collection contains the object, else False
62+
*/
63+
public boolean contains(E object) {
64+
return this.collection.stream()
65+
.filter(entry -> entry.getObject().equals(object))
66+
.findFirst()
67+
.isPresent();
68+
}
69+
70+
/**
71+
* @return Iterator over collection
72+
*/
73+
public Iterator<ProbabilitySetElement<E>> iterator() {
74+
return this.collection.iterator();
75+
}
76+
77+
/**
78+
* Add an object to this collection
79+
*
80+
* @param object
81+
* @param probability share
82+
*/
83+
public void add(E object, int probability) {
84+
this.collection.add(new ProbabilitySetElement<E>(object, probability));
85+
this.totalProbability += probability;
86+
this.updateIndexes();
87+
}
88+
89+
/**
90+
* Remove a object from this collection
91+
*
92+
* @param object
93+
* @return True if object was removed, else False.
94+
*/
95+
public boolean remove(E object) {
96+
Iterator<ProbabilitySetElement<E>> it = this.iterator();
97+
boolean removed = false;
98+
99+
while(it.hasNext()) {
100+
ProbabilitySetElement<E> element = it.next();
101+
if(element.getObject().equals(object)) {
102+
removed = true;
103+
this.totalProbability -= element.getProbability();
104+
it.remove();
105+
}
106+
}
107+
108+
this.updateIndexes();
109+
return removed;
110+
}
111+
112+
/**
113+
* @return Random object based on probability
114+
*/
115+
public E get() {
116+
ProbabilitySetElement<E> toFind = new ProbabilitySetElement<>(null, 0);
117+
toFind.setIndex(ThreadLocalRandom.current().nextInt(1, this.totalProbability + 1));
118+
119+
return this.collection.floor(toFind).getObject();
120+
}
121+
122+
/**
123+
* @return Sum of all element's probability
124+
*/
125+
public final int getTotalProbability() {
126+
return this.totalProbability;
127+
}
128+
129+
/*
130+
* Calculate the size of all element's "block" of space:
131+
* i.e 1-5, 6-10, 11-14, 15, 16
132+
*
133+
* We then only need to store the start index of each element
134+
*/
135+
private void updateIndexes() {
136+
int previousIndex = 0;
137+
138+
for(ProbabilitySetElement<E> entry : this.collection) {
139+
previousIndex = entry.setIndex(previousIndex + 1) + (entry.getProbability() - 1);
140+
}
141+
}
142+
143+
/**
144+
* Used internally to store information about a object's
145+
* state in a collection. Specifically, the probability
146+
* and index within the collection.
147+
*
148+
* Indexes refer to the start position of this element's "block" of space.
149+
* The space between element "block"s represents their probability of being selected
150+
*
151+
* @author Lewys Davies
152+
*
153+
* @param <T> Type of element
154+
*/
155+
final static class ProbabilitySetElement<T> {
156+
private final T object;
157+
private final int probability;
158+
private int index;
159+
160+
/**
161+
* @param object
162+
* @param probability
163+
*/
164+
protected ProbabilitySetElement(T object, int probability) {
165+
this.object = object;
166+
this.probability = probability;
167+
}
168+
169+
/**
170+
* @return The actual object
171+
*/
172+
public final T getObject() {
173+
return this.object;
174+
}
175+
176+
/**
177+
* @return Probability share in this collection
178+
*/
179+
public final int getProbability() {
180+
return this.probability;
181+
}
182+
183+
// Used internally, see this class's documentation
184+
protected final int getIndex() {
185+
return this.index;
186+
}
187+
188+
// Used Internally, see this class's documentation
189+
protected final int setIndex(int index) {
190+
this.index = index;
191+
return this.index;
192+
}
193+
}
194+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.lewdev.probabilitylib;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.concurrent.TimeUnit;
6+
7+
import org.openjdk.jmh.annotations.Benchmark;
8+
import org.openjdk.jmh.annotations.BenchmarkMode;
9+
import org.openjdk.jmh.annotations.Measurement;
10+
import org.openjdk.jmh.annotations.Mode;
11+
import org.openjdk.jmh.annotations.Scope;
12+
import org.openjdk.jmh.annotations.Setup;
13+
import org.openjdk.jmh.annotations.State;
14+
import org.openjdk.jmh.annotations.Timeout;
15+
import org.openjdk.jmh.annotations.Warmup;
16+
import org.openjdk.jmh.infra.Blackhole;
17+
import org.openjdk.jmh.runner.Runner;
18+
import org.openjdk.jmh.runner.RunnerException;
19+
import org.openjdk.jmh.runner.options.Options;
20+
import org.openjdk.jmh.runner.options.OptionsBuilder;
21+
22+
@BenchmarkMode(Mode.AverageTime)
23+
@State(Scope.Benchmark)
24+
@Warmup(iterations = 5, time = 5)
25+
@Timeout(time = 25, timeUnit = TimeUnit.SECONDS)
26+
@Measurement(iterations = 10, time = 2)
27+
public class BenchmarkProbability {
28+
29+
public static void main(String[] args) throws RunnerException {
30+
Options opt = new OptionsBuilder()
31+
.include(BenchmarkProbability.class.getSimpleName())
32+
.forks(1)
33+
.build();
34+
35+
new Runner(opt).run();
36+
}
37+
38+
private final int elements = 10_000;
39+
40+
ProbabilityMap<Integer> map = new ProbabilityMap<>();
41+
ProbabilityCollection<Integer> collection = new ProbabilityCollection<>();
42+
43+
private Map<Integer, Integer> addAllTest = new HashMap<>();
44+
45+
@Setup
46+
public void setup() {
47+
for(int i = 0; i < elements; i++) {
48+
map.add(i, 1);
49+
collection.add(i, 1);
50+
}
51+
52+
for(int i = elements; i < elements * 2; i++) {
53+
addAllTest.put(i, 1);
54+
}
55+
}
56+
57+
@Benchmark
58+
public void mapAddSingle(Blackhole bh) {
59+
boolean added = this.map.add(25000, 1);
60+
bh.consume(added);
61+
}
62+
63+
@Benchmark
64+
public void mapAddAll() {
65+
map.addAll(addAllTest);
66+
}
67+
68+
@Benchmark
69+
public void collectionAddSingle() {
70+
this.collection.add(25000, 1);
71+
}
72+
73+
@Benchmark
74+
public void mapGet(Blackhole bh) {
75+
for(int i = 0; i < elements * 2; i++) {
76+
bh.consume(map.get());
77+
}
78+
}
79+
80+
@Benchmark
81+
public void collectionGet(Blackhole bh) {
82+
for(int i = 0; i < elements * 2; i++) {
83+
bh.consume(collection.get());
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)