From 7d00896b46a151c232171b2f17f2e4dfa3b2636e Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 8 Mar 2025 17:12:13 +0700 Subject: [PATCH] Provide PCG XSH RR random implementation --- gradle.properties | 2 +- .../dbotthepony/kommons/random/PCG32Random.kt | 28 +++++++++++++++++++ .../random/Xoshiro256PlusPlusRandom.kt | 9 ++++-- .../random/Xoshiro256StarStarRandom.kt | 9 ++++-- 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/random/PCG32Random.kt diff --git a/gradle.properties b/gradle.properties index 2157224..b815800 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ kotlin.code.style=official specifyKotlinAsDependency=false projectGroup=ru.dbotthepony.kommons -projectVersion=3.2.1 +projectVersion=3.3.0 guavaDepVersion=33.0.0 gsonDepVersion=2.8.9 diff --git a/src/main/kotlin/ru/dbotthepony/kommons/random/PCG32Random.kt b/src/main/kotlin/ru/dbotthepony/kommons/random/PCG32Random.kt new file mode 100644 index 0000000..7231120 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/random/PCG32Random.kt @@ -0,0 +1,28 @@ +package ru.dbotthepony.kommons.random + +import java.util.random.RandomGenerator + +/** + * PCG XSH RR 32-bit (64-bit state) random generator, which has better statistical properties + * than plain [LCG64Random] generator + */ +open class PCG32Random(protected var seed: Long) : RandomGenerator { + final override fun nextInt(): Int { + var x = this.seed + this.seed = LCG64Random.MULTIPLIER * this.seed + LCG64Random.INCREMENT + + val rot = x.ushr(59) and MASK + x = x.xor(x.ushr(18)) + return x.ushr(27).toInt().rotateLeft(rot.toInt()) + } + + override fun nextLong(): Long { + val a = nextInt().toLong() + val b = nextInt().toLong() and 0xFFFFFFFFL + return a.shl(32) or b + } + + companion object { + const val MASK = (1L shl 5) - 1L + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256PlusPlusRandom.kt b/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256PlusPlusRandom.kt index 116f8a3..fcb24eb 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256PlusPlusRandom.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256PlusPlusRandom.kt @@ -2,6 +2,11 @@ package ru.dbotthepony.kommons.random import java.util.random.RandomGenerator +/** + * Excellent PRNG with expected period of 2^255 - 1, a slightly faster version than [Xoshiro256StarStarRandom]. + * + * Where [Xoshiro256PlusPlusRandom] is used consider using [Xoshiro256StarStarRandom] instead. + */ open class Xoshiro256PlusPlusRandom protected constructor( protected var s0: Long, protected var s1: Long, @@ -25,14 +30,14 @@ open class Xoshiro256PlusPlusRandom protected constructor( } constructor(seed: Long) : this(1L, 0L, 0L, 0L, null) { - val rng = LCG64Random(seed) + val rng = PCG32Random(seed) s0 = rng.nextLong() s1 = rng.nextLong() s2 = rng.nextLong() s3 = rng.nextLong() } - override fun nextLong(): Long { + final override fun nextLong(): Long { val result = (s0 + s3).rotateLeft(23) + s1 val t = s1.shl(17) s2 = s2.xor(s0) diff --git a/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256StarStarRandom.kt b/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256StarStarRandom.kt index fc1377c..e25a640 100644 --- a/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256StarStarRandom.kt +++ b/src/main/kotlin/ru/dbotthepony/kommons/random/Xoshiro256StarStarRandom.kt @@ -2,6 +2,11 @@ package ru.dbotthepony.kommons.random import java.util.random.RandomGenerator +/** + * Excellent PRNG with expected period of 2^255 - 1, which is de-facto standard PRNG + * used in many applications and programming languages (where not restricted + * by backward compatibility) + */ open class Xoshiro256StarStarRandom protected constructor( protected var s0: Long, protected var s1: Long, @@ -25,14 +30,14 @@ open class Xoshiro256StarStarRandom protected constructor( } constructor(seed: Long) : this(1L, 0L, 0L, 0L, null) { - val rng = LCG64Random(seed) + val rng = PCG32Random(seed) s0 = rng.nextLong() s1 = rng.nextLong() s2 = rng.nextLong() s3 = rng.nextLong() } - override fun nextLong(): Long { + final override fun nextLong(): Long { val result = (s1 * 5).rotateLeft(7) * 9 val t = s1.shl(17) s2 = s2.xor(s0)