Generating random vectors of Euclidean norm <= 1 in Python?

Question:

More specifically, given a natural number d, how can I generate random vectors in R^d such that each vector x has Euclidean norm <= 1?

Generating random vectors via numpy.random.rand(1,d) is no problem, but the likelihood of such a random vector having norm <= 1 is predictably bad for even not-small d. For example, even for d = 10 about 0.2% percent of such random vectors have appropriately small norm. So that seems like a silly solution.

EDIT: Re: Walter’s comment, yes, I’m looking for a uniform distribution over vectors in the unit ball in R^d.

Asked By: lamb_da_calculus

||

Answers:

Based on the Wolfram Mathworld article on hypersphere point picking and Nate Eldredge’s answer to a similar question on math.stackexchange.com, you can generate such a vector by generating a vector of d independent Gaussian random variables and a random number U uniformly distributed over the closed interval [0, 1], then normalizing the vector to norm U^(1/d).

Answered By: user2357112

Based on the answer by user2357112, you need something like this:

import numpy as np
...
inv_d = 1.0 / d
for ...:
    gauss = np.random.normal(size=d)
    length = np.linalg.norm(gauss)
    if length == 0.0:
        x = gauss
    else:
        r = np.random.rand() ** inv_d
        x = np.multiply(gauss, r / length)
        # conceptually: / length followed by * r
    # do something with x

(this is my second Python program, so don’t shoot at me…)

The tricks are that

  • the combination of d independent gaussian variables with same σ is a gaussian distribution in d dimensions, which, remarkably, has spherical symmetry,
  • the gaussian distribution in d dimensions can be projected onto the unit sphere by dividing by the norm, and
  • the uniform distribution in a d-dimensional unit sphere has cumulative radial distribution rd (which is what you need to invert)
Answered By: Walter Tross

this is the Python / Numpy code I am using. Since it does not use loops, is much faster:

n_vectors=1000
d=2

rnd_vec=np.random.uniform(-1, 1, size=(n_vectors, d))                # the initial random vectors
unif=np.random.uniform(size=n_vectors)                               # a second array random numbers
scale_f=np.expand_dims(np.linalg.norm(rnd_vec, axis=1)/unif, axis=1) # the scaling factors
rnd_vec=rnd_vec/scale_f                                              # the random vectors in R^d

The second array of random numbers (unif) is needed as second scaling factor because otherwise all the vectors will have euclidean norm equal to one.

Answered By: Vincenzo Lavorini
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.