2

Edit: As it turns out, I typoed the seed value in my test code in the C version (It wasn't the same as I pasted in this question), so I was getting different output. Thanks everyone.

I need to generate psuedo random numbers in a C program, and then pass the seed to a Python program later and generate the same numbers.

PRNGs are something that I consider over my head, so I looked up an implemention (in C):

static unsigned long next = 1;

/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}

And a naive port:

next = 1

def myrand():
    global next
    next = next * 1103515245 + 12345
    return (next / 65536) % 32768

However, these both produce different values. I'm guessing next in the C implementation overflows, and that's why the two functions produce different values. But in Python numbers have no explicit type, they don't overflow, and they're not unsigned. How can I replicate this behavior in Python?

Thanks in advance.

Salis
  • 433
  • 1
  • 8
  • 15
  • Overwriting the name `next` is indeed very naive. – wim Mar 14 '13 at 06:13
  • I think it's because in C the decimal values are truncated and in python they aren't – Topo Mar 14 '13 at 06:13
  • Built in python numbers do have an explicit type. They just behave sanely because it's a more modern language. Could you provide the first few numbers generated from your C code/platform in your question? – wim Mar 14 '13 at 06:15
  • @wim Using a seed of 5, and 3 iterations: 16838, 5758, 10113 (on Linux, if that matters). By "they don't have an explicit type", I meant that Python doesn't have multiple explicit integer types for 8-bit, 16-bit, 32-bit, etc, it simply uses more memory as needed to store numbers of infinite* size – Salis Mar 14 '13 at 06:18
  • Beware that in newer versions of python (3+), integer division only happens when you explicitly ask for it. To get this behavior, use `//` instead of `/`. – FatalError Mar 14 '13 at 06:21
  • @Salis I'm on linux too. I tried compiling the C version with GCC and running the python one on python 2.7 and they work the same for me. On the first 3 iterations I get the same as you. Can you put an example of when they start giving different values? – Topo Mar 14 '13 at 06:24
  • 1
    @Topo: Even if it works as-is, some bounds control on `next` is needed. Otherwise `next` becomes arbitrarily large, and as a result dealing with it becomes arbitrarily slow. – FatalError Mar 14 '13 at 06:27
  • @Topo As it turns out, I'm a clown and typoed the seed. Wow. Thanks everyone – Salis Mar 14 '13 at 06:28
  • Yeah your comment has the results for seed 1 not seed 5 .. – wim Mar 14 '13 at 06:44

3 Answers3

2

Taking every computation modulo (%) 2^32 (assuming 32 bit integer width) should work.

next = 1
modulo = 2**32
def myrand():
    global next
    next = ((next * 1103515245) + 12345) % modulo
    return (next / 65536) % 32768
Paul
  • 849
  • 1
  • 7
  • 18
  • 2
    +1 but I think the modulus should be 2^32. After all, 2^32 - 1 is a valid value for such an int, and it wraps around only at 2^32. – FatalError Mar 14 '13 at 06:12
  • 1
    and make sure to do 2**32 because in python 2^32 is something else entirely – wim Mar 14 '13 at 06:20
  • -1 the modulo doesn't actually do anything. run it without and you will get the same result. also don't encourage the use of global. – wim Mar 14 '13 at 06:32
  • 1
    @wim: The modulo may not change the result but its usage is well-warranted here. Consider what happens without it -- `next` becomes arbitrarily large and slow. – FatalError Mar 14 '13 at 06:40
  • @wim You are correct, I should probably have checked that first. I assumed it was overflow too. – Paul Mar 14 '13 at 06:48
2

You don't want to use a global variable here, the problem is more suited to a generator function. Also you certainly don't want to use next as a variable name in python because it shadows a very useful builtin name (actually I'm using it below!)

def myrand(seed=1):
    n = seed
    while True:
      n = n * 1103515245 + 12345
      yield (n // 65536) % 32768


g = myrand()
print(next(g))
print(next(g))
print(next(g))

This works for me on python3, apparently no overflow handling needn't, and matches your first 3 inputs. Could you post a few more so we can see where/why they may diverge?

wim
  • 338,267
  • 99
  • 616
  • 750
  • @Topo As it turns out, I'm a clown and typoed the seed in the C version. How embarrassing. For the record, the global variable, overwriting, etc was just for a brief question, not actual code. – Salis Mar 14 '13 at 06:30
0
next = 1
def myrand():
    global next
    next = next * 1103515245 + 12345
    return (next & 0xFFFFFFFF / 65536) % 32768
pigletfly
  • 1,051
  • 1
  • 16
  • 32