3

I need to migrate some C# code to Python. The original code makes use of the `Random`

class. The migrated code must be cycle-accurate (namely consecutive calls to `Next()`

must produce the same results in both codes). Some questions:

- Is there a call-equivalent to C#'s
`Random`

in Python? - Alliteratively, assuming I can modify both sources, is there a pseudo-random library which works with both C# and Python?

Write your own PRNG for both languages - David Heffernan 2012-04-04 07:49

1

I don't know of any library that is available for both Python and C# and which generates the same random numbers for both. However, you may be able to take advantage of IronPython. The default `Random`

implementation differs between IronPython and CPython, but the `WichmannHill`

class does not.

You can use C# to instantiate the `WichmannHill`

class in IronPython and get the same values as CPython for the same seed. Alternatively, you can implement the Wichmann-Hill algorithm in C# relatively easily by translating the Python code in `random.py`

.

Another option is to take the CPython implementation of `Random`

's Mersenne Twister algorithm and translate that to C# to get identical results.

I second the use of Mersenne Twister. It's a much better randomiser than the built-in Random() (for c# at least, I don't know about IronPython). Don't use it (or indeed Random()) for cryptography though - Matthew Watson 2012-04-04 08:05

1

I know this is an old question, but I ended up needing a solution to this. I ended up implementing C#'s Random class in python. It works as long as you don't need random numbers larger than 2147483647, I ended up not needing that functionality so I left it unimplemented.

https://gist.github.com/BadStreff/541cf2e6953b3c666f83127a1d4f6a47

```
from ctypes import *
# implemented from:
# http://referencesource.microsoft.com/#mscorlib/system/random.cs,dec894a7e816e665
class Random(object):
def __init__(self, seed):
self.seed = c_int(seed).value
self.MBIG = 2147483647
self.MMIN = -2147483648
self.MZ = 0
self.MSEED = 161803398
self.SeedArray = [0] * 56
if seed == self.MMIN:
subtraction = self.MBIG
else:
subtraction = abs(seed)
mj = c_int(self.MSEED - subtraction).value
self.SeedArray[55] = mj
mk = 1
for i in range(1, 55):
ii = (21 * i) % 55
self.SeedArray[ii] = mk
mk = mj - mk
if mk < 0:
mk += self.MBIG
mj = self.SeedArray[ii]
for k in range(1, 5):
for i in range(1, 56):
self.SeedArray[i] -= self.SeedArray[1 + (i + 30) % 55]
if self.SeedArray[i] < 0:
self.SeedArray[i] = c_int(self.SeedArray[i] + self.MBIG).value
self.inext = 0
self.inextp = 21
self.seed = 1
def InternalSample(self):
locINext = self.inext + 1
locINextp = self.inextp + 1
if locINext >= 56:
locINext = 1
if locINextp >= 56:
locINextp = 1
retVal = c_int(self.SeedArray[locINext] - self.SeedArray[locINextp]).value
if retVal == self.MBIG:
retVal -= 1
if retVal < 0:
retVal = c_int(retVal + self.MBIG).value
self.SeedArray[locINext] = retVal
self.inext = locINext
self.inextp = locINextp
return retVal
def Next(self, minValue=None, maxValue=None):
if minValue == None:
return self.InternalSample()
valRange = maxValue - minValue
if valRange <= self.MBIG:
return int(c_float(self.Sample() * valRange).value) + minValue
else:
return self.GetSampleForLargeRange() * valRange + minValue
def GetSampleRangeForLargeRange(self):
pass
def Sample(self):
s = self.InternalSample()
ret = c_double(s * c_double(1.0/self.MBIG).value).value
# print(f'sample: {s}\nret: {ret}')
return ret
```

Post your implementation here, in case the gist gets deleted or the link goes down - Adam 2017-04-16 23:16