Skip to content

Commit 91ff56b

Browse files
committed
merge
2 parents d4b61b0 + b2ef21b commit 91ff56b

File tree

8 files changed

+702
-1
lines changed

8 files changed

+702
-1
lines changed

.github/workflows/maven.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# This workflow will build a Java project with Maven
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3+
4+
name: Build
5+
6+
on:
7+
push:
8+
branches: [ master ]
9+
pull_request:
10+
branches: [ master ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v2
19+
- name: Set up JDK 1.8
20+
uses: actions/setup-java@v1
21+
with:
22+
java-version: 1.8
23+
- name: Build with Maven
24+
run: mvn -B package --file probability-lib/pom.xml

README.md

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

probability-lib/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/target/
2+
.classpath
3+
.project
4+
*.settings
5+
.editorconfig

probability-lib/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+
}

0 commit comments

Comments
 (0)