Well Equidistributed Long-period Linear (WELL) is a pseudorandom number generator algorithm based on the technique of Linear Feedback Shift Register to create Pseudo-Random Numbers. It belongs to the family of Linear Congruential Generators. It is similar to a Mersenne twister in terms of functioning since both of them work using XOR, shifts and an LFSR mechanism. Both require a seed value to start off the process of generation.
Pseudorandom Number Generators refers to a class of algorithms that utilize an initial set of seed values and apply a variety of logical operations or shift operations to produce a seemingly random number. A generator can be defined recursively as follows:
Xn+1 = (a*(Xn) + c) modulo-m where X is a sequence of pseudorandom values, m is the modulus (m>0), a is the multiplier (0<a<m), c is the increment ( 0<c<m), and X0 is the seed ( 0<X0<m)
The algorithm is as follows:
0. Initialize the necessary seed values 1. Utilize a state array of a defined length (here it is 32) 2. Apply the necessary xor, shift and logical and operations to arrive at the required parameters 3a. Update the current location value and output that value from an array location multiplied with a scaling constant 3b. newV0 and newV1 are additional pseudorandom numbers that can be supplied as per requirement
WELL algorithm can produce many numbers in a short time and is advantageous for applications that need many numbers. One of the major weakness that WELL suffers from is having a smaller state space. This can be changed by utilizing a larger size of state space with auxiliary space being traded off.
Python code implementation –
# W stands for size of the digest, R is the size of the state W = 32;R = 32 # Variables used for jumping to a particular state M1 = 3;M2 = 24;M3 = 10 # can be changed to scale accordingly FACT = 1.718 # value based right shift with xor operation def mat0pos(t, v): return(v^(v>>t)) # value based negative left shift with xor operation def mat0neg(t, v): return(v^(v<<(~t))) # returns the value itself (identity function) def identity(v): return v state = [None]*R state_i = 0; z0 = None;z1 = None;z2 = None; newV1 = None; newV0 = None # used to initialize the state of our digest based on argument provided def InitWellRNG(arg): global state for j in range(0, len(arg)): state[j]= arg[j] # function that utilizes seed values and logical operations to # output random numbers def WELLRNG(): global state_i global z0; global z1; global z2; global newV1; global newV0 z0 = state[(state_i + M1) & 0x001f] z1 = identity(state[state_i]) ^ mat0pos(8, state[(state_i + M1) & 0x001f]) z2 = mat0neg(-19, state[(state_i + M2) & 0x001f]) ^ mat0neg(-14, state[(state_i + M3) & 0x001f]) # newV1 and newV0 are additional random values that are produced # and can be returned as per requirement newV1 = z1 ^ z2; newV0 = mat0neg(-11, z0) ^ mat0neg(-7, z1) ^ mat0neg(-13, z2) state_i = (state_i + (W-1)) & 0x001f return (state[state_i] *FACT) # executed if this is the main file if (__name__ == "__main__"): print("WELL created\n") # replace seed array with your own array to create different output # sequence of random numbers seed = [27, 10, 14, 27, 24, 4, 11, 25, 14, 13, 2, 20, 0, 22, 9, 24, 14, 9, 20, 14, 17, 6, 21, 10, 27, 8, 16, 5, 26, 21, 18, 29] InitWellRNG(seed) for i in range(0,5): print("random number generated is {}".format(WELLRNG()))
Output :
WELL created random number generated is 49.822 random number generated is 30.924 random number generated is 36.078 random number generated is 44.668 random number generated is 8.59