Add xorwow random implementation test and fix implementation

This commit is contained in:
Ilya Gorbunov
2018-07-05 05:32:45 +03:00
parent 042a8ff6a2
commit ed1f869354
5 changed files with 62 additions and 29 deletions
@@ -5,10 +5,8 @@
package kotlin.random
import kotlin.math.pow
internal actual fun defaultPlatformRandom(): Random =
XorWowRandom((js("Math").random().unsafeCast<Double>() * (2.0.pow(32.0))).toInt())
Random(js("(Math.random() * Math.pow(2, 32)) | 0").unsafeCast<Int>())
internal actual fun fastLog2(value: Int): Int {
@@ -28,7 +28,7 @@ class Randoms {
val randomValues1 = getRandomList(Random(42))
// prints the same sequence every time
assertPrints(randomValues1, "[44, 34, 69, 67, 22, 16, 67, 45, 95, 10]")
assertPrints(randomValues1, "[33, 40, 41, 2, 41, 32, 21, 40, 69, 87]")
val randomValues2 = getRandomList(Random(42))
// random with the same seed produce the same sequence
@@ -36,6 +36,6 @@ class Randoms {
val randomValues3 = getRandomList(Random(0))
// random with another seed produce another sequence
assertPrints(randomValues3, "[20, 63, 41, 28, 0, 99, 35, 42, 72, 13]")
assertPrints(randomValues3, "[14, 48, 57, 67, 82, 7, 61, 27, 14, 59]")
}
}
+2 -2
View File
@@ -299,7 +299,7 @@ public abstract class Random {
* @sample samples.random.Randoms.seededRandom
*/
@SinceKotlin("1.3")
public fun Random(seed: Int): Random = XorWowRandom(seed)
public fun Random(seed: Int): Random = XorWowRandom(seed, seed.shr(31))
/**
* Returns a repeatable random number generator seeded with the given [seed] `Long` value.
@@ -309,7 +309,7 @@ public fun Random(seed: Int): Random = XorWowRandom(seed)
* @sample samples.random.Randoms.seededRandom
*/
@SinceKotlin("1.3")
public fun Random(seed: Long): Random = XorWowRandom(seed)
public fun Random(seed: Long): Random = XorWowRandom(seed.toInt(), seed.shr(32).toInt())
internal expect fun defaultPlatformRandom(): Random
@@ -6,36 +6,44 @@
package kotlin.random
/**
* Random number generator, algorithm "xorwow" from p. 5 of Marsaglia, "Xorshift RNGs"
* Random number generator, algorithm "xorwow" from p. 5 of Marsaglia, "Xorshift RNGs".
*
* Cycles after 2^160 * (2^32-1) repetitions.
*
* See http://www.jstatsoft.org/v08/i14/paper for details.
*/
internal class XorWowRandom internal constructor(
private var s0: Int,
private var s1: Int,
private var s2: Int,
private var s3: Int,
private var addent: Int
internal class XorWowRandom
internal constructor(
private var x: Int,
private var y: Int,
private var z: Int,
private var w: Int,
private var v: Int,
private var addend: Int
) : Random() {
public constructor(seed: Long) : this(seed.toInt(), (seed ushr 32).toInt(), 0, if (seed == 0L) 1 else 0, 1)
public constructor(seed: Int) : this(seed, 0, 0, if (seed == 0) 1 else 0, 1)
internal constructor(seed1: Int, seed2: Int) :
this(seed1, seed2, 0, 0, seed1.inv(), (seed1 shl 10) xor (seed2 ushr 4))
init {
require((s0 or s1 or s2 or s3) != 0) { "Initial state must have at least one non-zero element" }
require((x or y or z or w or v) != 0) { "Initial state must have at least one non-zero element." }
// some trivial seeds can produce several values with zeroes in upper bits, so we discard first 64
repeat(64) { nextInt() }
}
override fun nextInt(): Int {
var t = s3
t = t xor (t shr 2)
t = t xor (t shl 1)
s3 = s2
s2 = s1
val s = s0
s1 = s
t = t xor s
t = t xor (s shl 4)
s0 = t
addent += 362437
return t + addent
var t = x
t = t xor (t ushr 2)
x = y
y = z
z = w
val v0 = v
w = v0
t = (t xor (t shl 1)) xor v0 xor (v0 shl 4)
v = t
addend += 362437
return t + addend
}
override fun nextBits(bitCount: Int): Int = nextInt() ushr (32 - bitCount)
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package test.random
import kotlin.math.pow
import kotlin.random.*
import kotlin.test.*
class XorWowRandomImplTest {
@Test
fun predefinedSequence() {
val seed = 1
val addend = (seed shl 10) xor (seed ushr 4)
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
val random: Random = XorWowRandom(seed, 0, 0, 0, 0, addend)
// differs from reference 0.8178000247146859 because of different double mixing algorithm
assertEquals(0.817799582443095, random.nextDouble())
assertEquals(0.8407576507888734, random.nextBits(31) / (2.0.pow(31)))
assertEquals(533150816, random.nextInt())
}
}