Add xorwow random implementation test and fix implementation
This commit is contained in:
@@ -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]")
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user