Victor
Victor

Reputation: 127

Many independent pseudorandom graphs each with same arbitrary y for any input x

By 'graph' I mean 'function' in the mathematical sense, where you always find one unchanging y value per x value.

Python's random.Random class's seed behaves as the x-coordinate of a random graph and each new call to random.random() gives a new random graph with all new x-y mappings.

Is there a way to directly refer to random.Random's nth graph, or in other words, the nth value in a certain seed's series without calling random.random() n times?

I am making a set of classes that I call Transformers that take any (x,y) coordinates as input and output another pair of (x,y) coordinates. Each transformer has two methods: transform and untransform. One of the transformers that I want adds a random value to the input y coordinate depending on the the input x coordinate. Say that I then want this transformer to untransform(x, y), now I need to subtract the same value I added from y if x is the same. This can be done by setting the seed to the same value it had when I added to y, so acting like the x value. Now say that I want two different instances of the transformer that adds random values to y. My question is about my options for making this new random transformer give different values than the first one.

Upvotes: 2

Views: 187

Answers (3)

David Eisenstat
David Eisenstat

Reputation: 65458

Since Python 3.4 apparently removes jumpahead, here's some code that implements a convenient pseudorandom dictionary.

from hashlib import sha256 as _sha256
from hmac import HMAC as _HMAC
from math import ldexp as _ldexp
from os import urandom as _urandom
from sys import byteorder as _byteorder

class PRF():

    def __init__(self):
        digestmod = _sha256
        self._h = _HMAC(_urandom(digestmod().block_size), digestmod=digestmod)

    def __getitem__(self, key):
        h = self._h.copy()
        h.update(repr(key).encode())
        b = h.digest()
        return _ldexp(int.from_bytes(b, _byteorder), (len(b) * (- 8)))

Example usage:

>>> import prf
>>> f = prf.PRF()
>>> f[0]
0.5414241336009658
>>> f[1]
0.5238549618249061
>>> f[1000]
0.7476468534384274
>>> f[2]
0.899810590895144
>>> f[1]
0.5238549618249061

Upvotes: 4

violet313
violet313

Reputation: 1932

Well you're probably going to need to come up with some more detailed requirements but yes, there are ways:

  1. pre-populate a dictionary with however many terms in the series you require for a given seed and then at run-time simply look the nth term up.

  2. if you're not fussed about the seed values and/or do not require some n terms for any given seed, then find a O(1) way of generating different seeds and only use the first term in each series.

Otherwise, you may want to stop using the built-in python functionality & devise your own (more predictable) algo.

EDIT wrt the new infos:

Ok. so i also looked at your profile & so you are doing something (musical?) other than any new crypto thing. if that's the case, then it's unfortunately mixed blessings, because while you don't require security, you also still won't want (audible) patterns appearing. so you unfortunately probably do still need a strong prng.

One of the transformers that I want adds a random value to the input y coordinate depending on the the input x coordinate

It's not yet clear to me if there is actually any real requirement for y to depend upon x...

Now say that I want two different instances of the transformer that adds random values to y. My question is about my options for making this new random transformer give different values than the first one.

..because here, i'm getting the impression that all you really require is for two different instances to be different in some random way.

But, assuming you have some object containing tuple (x,y) and you really do want a transform function to randomly vary y for the same x; and you want an untransform function to quickly undo any transform operations, then why not just keep a stack of the state changes throughout the lifetime of any single instance of an object; and then in the untransform implementation, you just pop the last transformation off the stack ?

Upvotes: 0

jscs
jscs

Reputation: 64002

Is there a way to directly refer to random.Random's nth graph, or in other words, the nth value in a certain seed's series without calling random.random() n times?

Yes, sort of; you use Random.jumpahead(). There aren't really separate functions/graphs, though -- there's only one sequence generated by the PRNG -- but you can get into it at any point.

You seem to be still working on the same problem as your last question, and the code I posted in a comment there should cover this:

from random import Random

class IndependentRepeatableRandom(object):

    def __init__(self):
        self.randgen = Random()
        self.origstate = self.randgen.getstate()

    def random(self, val):
        self.randgen.jumpahead(int(val))
        retval = self.randgen.random()
        self.randgen.setstate(self.origstate)
        return retval

Upvotes: 1

Related Questions