IOS Swift: looking for a cross compatible method to Java's Random() PRNG that has identical output

Here's my delimna: I am writing an application that needs to exactly reproduce the PRNG output from a game that was written in Java that uses the Java random() with a given seed to create all it's initial game 'world' data.

The problem I am facing is that Java's random() and ios Swift native PRNG do not generate the same values when given the exact same seeds.

Here are my test cases: In all cases the same 'seed' is used, and the formula is for a random Integer between 0 and 9.


In Java:

import java.util.Random;
long seed = 987234904; 
Random rnd = new Random(seed); 
int result = rnd.nextInt(10);

The Java random() 'result' = 0


Swift - using srand48() / drand48():

import UIKit 
var seed: Int = 987234904 
srand48(seed) 
var result = Int(drand48()*10)

The ios Swift drand48() 'result' = 7


In Swift - using rand_r():

import UIKit 
var seed: UInt32 = 987234904 
var result = Int(Float(rand_r(&seed))/Float(INT32_MAX)*10)

The ios Swift rand_r() result = 4


In Swift - using the new GKLinearCongruentialRandomSource - which uses a simular Linear Congruential method that Java.util.random() was based on - suposedly:

import UIKit 
import GameplayKit 
if #available (iOS 9,*) { 
   let seed: UInt64 = 987234904 
   let random = GKLinearCongruentialRandomSource(seed: seed) 
   let result = random.nextIntWithUpperBound(10) // should be a random Int from 0 to 9 (total 10 possibilities }

result = 6


With that in mind - is there a ios Swift|Objective-C|C++ code snippit|library available that is the exact same in functionality and output as the Java's version of random()?

Accepted Reply

At github.com/ahltorp/swiftjavarandom I just uploaded the Java random compatible C functions that I have used before. They work as is in Swift:


var inseed: UInt64 = 987234904
var seed: UInt64 = 0
java_random_setseed(&seed, seed)
var result = java_random_nextint_n(&seed, 10)
print(result)

Replies

Just additional information - this should be possible - as defined in Java's own documentation. :-)


From http://docs.oracle.com/javase/7/docs/api/java/util/Random.html

An instance of this class is used to generate a stream of pseudorandom numbers.
The class uses a 48-bit seed, which is modified using a linear congruential formula. 
(See Donald Knuth, The Art of Computer Programming, Volume 2, Section 3.2.1.)

At github.com/ahltorp/swiftjavarandom I just uploaded the Java random compatible C functions that I have used before. They work as is in Swift:


var inseed: UInt64 = 987234904
var seed: UInt64 = 0
java_random_setseed(&seed, seed)
var result = java_random_nextint_n(&seed, 10)
print(result)

That is awesome! I ran my simulation and it's a flawless fit!


Thank you very much! :-)

Actually, I believe that what java.util.Random does, that is different from the GKLinearCongruentialRandomSource, is that they 'scramble' every seed on input, whereas the seed value is directly accessible in GKLinearCongruentialRandomSource.


See http://developer.classpath.org/doc/java/util/Random-source.html#line.151 as to how Java scrambles the seed before using it.


The same scrambling could be used on a seed value before supplying GKLinearCongruentialRandomSource with it. This would mean an identical random number generator.


I did some code that should give you a proper GKRandomSource:


import GameplayKit

let multiplier : UInt64 = 0x5DEECE66D
let mask : UInt64 = (1 << 48) - 1

func scramble( seed : UInt64 ) -> UInt64 {
          return (seed ^ multiplier) & mask;
}

func alikeJavaRandom( seed : Int) -> GKRandomSource {
  let random = GKLinearCongruentialRandomSource()
  random.seed = scramble(UInt64(seed))
  random.nextInt()
  return random
}

Sorry to revive 2015 thread, but I am curious if there is any development on moving the base entirely to Swift 4 and above?


I am working with a project that is pre-built on frameworks entirely, so no bridging header possible (unless missing something)


Appreciate if there are any instructions to make it integrable via modulemap of github.com/ahltorp/swiftjavarandom


I have the same requirement to have a java.util.random equivalent in swift.


many thanks in anticipation.

My apologies to revive 2015 thread.


Has this code been tested/confirmed to generate same sequence as Java.Util.Random?


And if I were to add NextInt function as in java, I need to translate and add this to your class, would that be the right way to go?


: int next(int bits) {

seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);

return (int) (seed >>> (48 - bits));

}


Many thanks in anticipation.


Best Regards

My Java is more than a little rusty but I think you want this:

var seed: UInt64 = 0

func nextInt(_ bits: Int32) -> Int32 {
    precondition(bits >= 0)
    precondition(bits <= 48)
    seed = (seed &* 0x5_DEEC_E66D + 0x0b) & ((1 << 48) - 1)
    return Int32(truncatingIfNeeded: seed >> (48 - bits))
}

I’m assuming that:

  • Java

    Int
    is 32-bits, and hence imported into Swift as
    Int32
  • The 64-bit maths on

    seed
    is expected to wrap (hence the
    &*
    operator)
  • The final cast from

    UInt64
    to
    UInt32
    is truncating

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Eskimo.


Probably my followup question below is little broader


Java seed is "long" and allows -ve values

long8 bytes-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807


https://docs.oracle.com/javase/7/docs/api/java/util/Random.html#setSeed(long)


where as

GKLinearCongruentialRandomSource


https://developer.apple.com/documentation/gameplaykit/gklinearcongruentialrandomsource


uses UInt64 which takes only positive values


so obviously when I compute a seed to pass on to random generator, do I need to remap?


Appreciate if anyone can give me some pointers.


I guess I am for looking two answers


1. Does +ve value of seed means --> losing half of the 9 trillion possible values? (lower priority)


2. Whats the best way (hope I dont miss any obvious) to map a computed seed if the seed happens to be -ve (high priority)

a) use modulus(seed) and then assign?

b) if seed < 0 seed = 9,223,372,036,854,775,807 + seed+1

anything else? would like to see any tradeoff here on the usable space and randomness


In this case I am going make changes to Java code ( I dont like it, but I am forced to)


thanks in advance

Looking at your Java code it seems to be relying on the fact that integer arithmetic wraps in Java. Assuming a 2’s complement representation (which I believe Java requires but, again, I’m hardly a Java expert), the difference between signed and unsigned falls away. Thus if you get an

Int64
seed from Java and want to apply it to an API that takes a
UInt64
, you can convert using the
bitPattern
initialiser. For example:
let s: Int64 = -1
let u = UInt64(bitPattern: s)
print("0x\(String(u, radix: 16))")  // -> 0xffffffffffffffff

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"