Random utilities¶
Extensions on kotlin.random.Random and on collections for generating random text, drawing from distributions, and sampling. Each snippet is copy-pasteable and uses only real APIs from io.github.yuroyami.kitecore.random.
Random strings¶
Generate a token from a built-in alphabet¶
import io.github.yuroyami.kitecore.random.nextAlphanumeric
import kotlin.random.Random
val token = Random.nextAlphanumeric(12)
// e.g. "kX3Tz09aQm2b": exactly 12 characters from a-z, A-Z, 0-9
nextAlphabetic, nextDigits, and nextHexString follow the same contract: exactly length characters, each chosen independently and uniformly. A length of zero yields the empty string; a negative length throws IllegalArgumentException.
import io.github.yuroyami.kitecore.random.nextAlphabetic
import io.github.yuroyami.kitecore.random.nextDigits
import io.github.yuroyami.kitecore.random.nextHexString
val letters = Random.nextAlphabetic(8) // e.g. "QwErTyUi": a-zA-Z only
val pin = Random.nextDigits(6) // e.g. "042917": leading zeros are possible
val color = Random.nextHexString(6) // e.g. "3fa2c9": lowercase 0-9a-f
Use a custom alphabet¶
import io.github.yuroyami.kitecore.random.nextChar
import io.github.yuroyami.kitecore.random.nextString
// Characters that are hard to confuse when read aloud (no 0/O, 1/I/l)
val code = Random.nextString(6, "ABCDEFGHJKMNPQRSTUVWXYZ23456789")
// One character at a time
val suit = Random.nextChar("SHDC")
Each character is drawn uniformly from the positions of the alphabet, so a character that occurs twice in the alphabet is twice as likely to appear. The alphabet must not be empty.
The predefined alphabets¶
RandomAlphabets holds the strings behind the shorthand functions. Pass them anywhere an alphabet is accepted.
| Constant | Contents | Size |
|---|---|---|
RandomAlphabets.ALPHANUMERIC |
a-z, A-Z, 0-9 | 62 |
RandomAlphabets.ALPHABETIC |
a-z, A-Z | 52 |
RandomAlphabets.DIGITS |
0-9 | 10 |
RandomAlphabets.HEX_LOWER |
0-9, a-f | 16 |
import io.github.yuroyami.kitecore.random.RandomAlphabets
// These two calls are equivalent
Random.nextAlphanumeric(16)
Random.nextString(16, RandomAlphabets.ALPHANUMERIC)
Reproducible output with a seed¶
Every function on this page is an extension on kotlin.random.Random or accepts a random parameter, so a seeded instance makes the output deterministic. Useful in tests and anywhere a run must be replayable.
import io.github.yuroyami.kitecore.random.nextAlphanumeric
import io.github.yuroyami.kitecore.random.sample
import kotlin.random.Random
val rng = Random(42)
val id = rng.nextAlphanumeric(10)
// The same 10-character string on every run
val subset = listOf("a", "b", "c", "d", "e").sample(2, random = Random(42))
// The same 2 elements on every run
Two Random(42) instances produce identical streams, so a test and the code under test can be kept in lockstep by sharing the seed.
Distributions¶
Biased coin flip¶
import io.github.yuroyami.kitecore.random.nextBoolean
if (Random.nextBoolean(probability = 0.05)) {
// Runs for roughly 1 in 20 calls
}
A probability of 0.0 always yields false and 1.0 always yields true. Values outside 0.0..1.0, including NaN, throw IllegalArgumentException.
Normally distributed values¶
import io.github.yuroyami.kitecore.random.nextGaussian
val latency = Random.nextGaussian(mean = 200.0, standardDeviation = 30.0)
// About 68% of values land within 170.0..230.0
Values are produced with the Box-Muller transform. The defaults are mean = 0.0 and standardDeviation = 1.0, the standard normal distribution. A standard deviation of 0.0 always yields the mean; a negative one throws.
A stream of Gaussian values¶
import io.github.yuroyami.kitecore.random.gaussianSequence
val noise = Random.gaussianSequence(mean = 0.0, standardDeviation = 0.1)
.take(100)
.toList()
The sequence is infinite and lazy. Bound it with take (or another terminating operator) before collecting, or iteration never ends.
Random sign¶
import io.github.yuroyami.kitecore.random.nextSign
val jitter = Random.nextSign() * Random.nextDouble(5.0)
// nextSign() is +1 or -1 with equal probability
Collections¶
Sample without replacement¶
import io.github.yuroyami.kitecore.random.sample
val participants = listOf("Ana", "Bo", "Cid", "Dee", "Eli")
val winners = participants.sample(3)
// 3 elements, no position picked twice, order randomized
Every subset of size n is equally likely. n must be within 0..size; anything else throws IllegalArgumentException. Duplicate values in the source can still appear in the result, since sampling is by position.
sampleOrNull returns null instead of throwing when n is out of range:
import io.github.yuroyami.kitecore.random.sampleOrNull
val winners = participants.sampleOrNull(10) ?: emptyList()
// null: cannot draw 10 distinct positions from 5 elements
Sample with replacement¶
import io.github.yuroyami.kitecore.random.sampleWithReplacement
val measurements = listOf(9.8, 10.1, 10.0, 9.9)
val bootstrap = measurements.sampleWithReplacement(1000)
// 1000 elements; positions repeat, and n may exceed the collection size
An n of zero yields an empty list even for an empty collection. A positive n on an empty collection throws.
Weighted choice¶
import io.github.yuroyami.kitecore.random.weightedRandom
import io.github.yuroyami.kitecore.random.weightedRandomIndex
val tiers = listOf("common", "rare", "epic")
val weights = listOf(80.0, 15.0, 5.0)
val drop = tiers.weightedRandom(weights)
// "common" 80% of the time, "rare" 15%, "epic" 5%
val index = tiers.weightedRandomIndex(weights)
// The index instead of the element: 0, 1, or 2
The probability of element i is weights[i] / weights.sum(), so weights do not need to add up to 1.0. An element with weight 0.0 is never chosen. The weights list must match the list size, every weight must be non-negative and finite, and the total must be strictly positive; violations throw IllegalArgumentException.
weightedRandomOrNull returns null for any of those violations instead of throwing:
import io.github.yuroyami.kitecore.random.weightedRandomOrNull
val safe = tiers.weightedRandomOrNull(listOf(0.0, 0.0, 0.0))
// null: the total weight is not strictly positive
Pick one of a few literals¶
import io.github.yuroyami.kitecore.random.randomOf
import kotlin.random.Random
val greeting = randomOf("hi", "hey", "hello")
// Each option is equally likely
val seeded = randomOf("red", "green", "blue", random = Random(42))
// The random parameter follows the vararg, so pass it by name
At least one option is required; calling randomOf() with no options throws IllegalArgumentException.