Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active May 6, 2023 15:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dacr/72375bec825a16b997f520c10fb72380 to your computer and use it in GitHub Desktop.
Save dacr/72375bec825a16b997f520c10fb72380 to your computer and use it in GitHub Desktop.
Evaluating random generators using compression algorithm : less compressed meaning better random generator ? / published by https://github.com/dacr/code-examples-manager #12596664-b528-404b-9e34-ec4e856e019d/289ffa17d0ba41679e1a3a19af05aab12c885a64
// summary : Evaluating random generators using compression algorithm : less compressed meaning better random generator ?
// keywords : scala, random, rng, @testable
// publish : gist
// authors : David Crosson
// license : Proprietary BUT Machine Learning models training is not allowed by the author
// id : 12596664-b528-404b-9e34-ec4e856e019d
// created-on : 2022-02-26T08:08:12+01:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// ---------------------
//> using scala "3.2.2"
//> using dep "org.apache.commons:commons-compress:1.23.0"
//> using dep "org.tukaani:xz:1.9"
// ---------------------
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
import java.io.{ByteArrayOutputStream, OutputStream}
import java.nio.ByteBuffer
import scala.Console.{BLUE, GREEN, YELLOW, RED, RESET}
object RandomGenerators:
trait RandomGenerator:
def nextInt(n: Int): Int
class DefaultRandom(seed: Long = System.currentTimeMillis()) extends RandomGenerator:
private val random = scala.util.Random(seed)
final def nextInt(n: Int): Int = random.nextInt(n)
class BasicFastRandom(seed: Long = System.currentTimeMillis()) extends RandomGenerator:
private var g_seed = (seed >> 16 & 0x7fffffff).toInt
final def nextInt(n: Int) =
g_seed = 214013 * g_seed + 2531011
((g_seed >> 16) & 0x7fff) % n
class XORShift32(val seed: Long = System.currentTimeMillis()) extends RandomGenerator:
private var cseed = seed
private def randomInt =
var x = cseed
x ^= (x << 13)
x ^= (x >>> 17)
x ^= (x << 5)
cseed = x
x
final def nextInt(bound: Int): Int = ((randomInt & 0x7fffff) % bound).toInt
object Compressors:
def compress(makeCompressorOutputStream: OutputStream => OutputStream)(input: Array[Byte]): Array[Byte] =
val baos = new ByteArrayOutputStream()
val outputStream = makeCompressorOutputStream(baos)
try outputStream.write(input)
finally if (outputStream != null) outputStream.close()
baos.toByteArray
def now() = System.currentTimeMillis
case class RandomCheckResult(name: String, compressedLevel: Double, randomGenerationDuration: Long, compressorDuration: Long):
override def toString = f"$BLUE$name$RESET : $compressedLevel%.2f%% in $randomGenerationDuration%sms $compressorDuration%sms"
object RandomChecks:
def randomCheck(
name: String,
nextInt: Int => Int,
compressor: Array[Byte] => Array[Byte],
bound: Int = 32
): RandomCheckResult =
val bytesCount = 5 * 1024 * 1024
val rngStarted = now()
val input = LazyList.continually(nextInt(bound)).map(_.toByte).take(bytesCount).toArray
val rngDuration = now() - rngStarted
val compressorStarted = now()
val output = compressor(input)
val compressorDuration = now() - compressorStarted
val result = 100d * output.size / input.size
RandomCheckResult(name, result, rngDuration, compressorDuration)
val compressor = Compressors.compress(out => XZCompressorOutputStream(out))
//val compressor = Compressors.compress(out => BZip2CompressorOutputStream(out))
//val compressor = Compressors.compress(out => GzipCompressorOutputStream(out))
//val seed = System.currentTimeMillis
val seed = 42
val bound = 2
println(s"${GREEN}USING COMPRESSION ALGORITHM TO EVALUATE RANDOM GENERATORS$RESET")
println(s" $YELLOW(Higher is better)$RESET")
val algos: List[(String, Int => Int)] = List(
"NOT-RANDOM" -> (_ => 42),
"DEFAULT" -> RandomGenerators.DefaultRandom(seed).nextInt,
"BASICFAST" -> RandomGenerators.BasicFastRandom(seed).nextInt,
"XORSHIFT32" -> RandomGenerators.XORShift32(seed).nextInt
)
algos
.map((name, nextInt) => RandomChecks.randomCheck(name, nextInt, compressor, bound))
.sortBy(_.compressedLevel)
.map(println)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment