Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

For a user specified magnitude, is there a way to have Mathematica produce any 3D vector that fits that magnitude?

share|improve this question
    
Do you want a random vector, or every vector? –  kunkelwe Oct 12 '12 at 0:37
    
Just a random vector. –  user1676471 Oct 12 '12 at 0:41
2  
Relevant math.SE question. –  J. M. Oct 14 '12 at 17:02
add comment

migrated from stackoverflow.com Oct 14 '12 at 16:45

This question came from our site for professional and enthusiast programmers.

5 Answers

up vote -5 down vote accepted

This should work:

random3DVector[magnitude_] := magnitude * Normalize[{RandomReal[], RandomReal[], RandomReal[]}]

A 3-dimensional vector in Mathematica is just a 3-element list of the components, so we make a random 3-element list, normalize it to a unit vector, and then multiply it by the desired magnitude.

share|improve this answer
2  
Mr. Wizard raises a good point in another answer, that this will only produce vectors with all components positive (well, non-negative I suppose) because RandomReal[] by default produces a number between 0 and 1. So this winds up excluding 7/8 of all possible directions. –  David Z Oct 14 '12 at 5:17
add comment

If you want a random vector just because you need some arbitrary vector and you don't really care what it is, then Mr.Wizard's method of picking three random coordinates in [-1,1] will work. But if you care about the statistical properties of your vector, and in particular if you want it drawn from a uniform distribution over the surface of the sphere, then you'll have to do something a little more complicated. The reason is explained on this MathWorld page, but basically the directions of the corners of the cube have more points to pick from, so they're going to be more likely.

The same page suggests several methods of picking a vector uniformly distributed over the sphere's surface. One way would be to transform uniformly distributed random numbers $u\in[0,1],v\in[-1,1]$ into the spherical coordinate angles as

$$\begin{align}\theta &= 2\pi u & \phi &= \cos^{-1}v\end{align}$$

and then compute the components of the corresponding unit vector as

$$\begin{align} x &= \cos\theta\sin\phi & y &= \sin\theta\sin\phi & z &= \cos\phi \end{align}$$

You could wrap this up into a function as

randomVec[r_] := With[{theta=2*Pi*RandomReal[],phi=ArcCos@RandomReal[{-1,1}]},
                    r*{Cos[theta]Sin[phi],Sin[theta]Sin[phi],Cos[phi]}]

You may notice that $z = v$ with this method, so you could try to "optimize" the calculation by directly setting $z = v$ and calculating $\sin\phi = \sqrt{1 - z^2}$, but I don't think that's going to save much time in the computation - it's not like Mathematica is a particularly speed-optimized programming language anyway. (And it might actually make that optimization on its own, I'm not sure.)

A different method is to choose three Gaussian random numbers and normalize the resulting vector:

randomVec[r_] := r * Normalize@RandomVariate[NormalDistribution[], 3]
share|improve this answer
    
I thought that seemed too easy. Thanks for finally providing the correct answer. –  Mr.Wizard Oct 14 '12 at 6:39
    
This paper discusses the last method that David supplied. –  J. M. Oct 14 '12 at 17:03
    
@J.M. how about a reference that doesn't require purchase? –  Mr.Wizard Oct 14 '12 at 19:55
2  
@Mr.Wizard you can try googling the title for pdfs out there or try accessing it from your local college/university library –  rm -rf Oct 14 '12 at 20:07
add comment

David's answer has given the methods for producing random points that are uniformly distributed over the surface of the sphere. Of course, there are other probability distributions on the sphere that are of interest, as well as a number of methods for generating them. For instance, here is how to generate a random unit vector which follows the von Mises-Fisher distribution, based on the proposal given here:

vonMisesFisherRandom[μ_?VectorQ, κ_?NumericQ] := Module[{ξ = RandomReal[], w},
  w = 1 + (Log[ξ] + Log[1 + (1 - ξ) Exp[-2 κ]/ξ])/κ;
  RotationTransform[{{0, 0, 1}, Normalize[μ]}][
   Append[Sqrt[1 - w^2] Normalize[RandomVariate[NormalDistribution[], 2]], w]]]

(I had previously used this generator for this answer.)

The distribution followed by the generator given above is completely analogous to the built-in VonMisesDistribution[μ, κ]; $\mu$ here is the so-called "mean direction", and $\kappa$ is the "concentration parameter". Large values of $\kappa$ yields points that are more tightly clustered about the direction $\mu$.

This paper might also be of interest.


As a demonstration of the von Mises-Fisher distribution, here is the result of generating $10^4$ random unit vectors from this distribution, with respect to the fixed mean direction $\mu=\left(-\tfrac1{\sqrt 8}\;-\sqrt{\tfrac38}\;\frac1{\sqrt 2}\right)^\top$ and varying concentration parameter $\kappa$. Note the clustering around $\mu$ that I mentioned earlier as $\kappa$ increases.

von Mises-Fisher samples with varying concentration parameter

Code for the image:

With[{μ = {-1/Sqrt[8], -Sqrt[3/8], 1/Sqrt[2]}}, 
     GraphicsGrid[Partition[
       Table[Graphics3D[{AbsolutePointSize[1], 
                         Point[Table[vonMisesFisherRandom[μ, κ], {10^4}]]}, 
                        PlotLabel -> StringForm["κ=`1`", κ]],
             {κ, {1/2, 1, 4, 8}}], 2]]]

As another example of a nontrivial spherical distribution, here are routines for generating a random unit vector which follows the Dimroth-Watson distribution, using the method proposed in this paper:

(* special case; κ = 0 is the uniform distribution *)
dimrothWatsonRandom[μ_?VectorQ, κ_ /; κ == 0] :=
                    Normalize[RandomVariate[NormalDistribution[], 3]]

dimrothWatsonRandom[μ_?VectorQ, κ_ /; NumericQ[κ] && Positive[κ]] := Module[{c, u, v, w},
  c = Exp[-κ/2] Csch[κ/2]/2;
  While[
        {u, v} = RandomReal[1, 2]; w = Log[1 + u/c]/κ;
        v > Exp[κ w (w - 1)]];
  RotationTransform[{{0, 0, 1}, Normalize[μ]}][Append[
  Sqrt[1 - w^2] Normalize[RandomVariate[NormalDistribution[], 2]], RandomChoice[{-1, 1}] w]]]

dimrothWatsonRandom[μ_?VectorQ, κ_ /; NumericQ[κ] && Negative[κ]] := Module[{c, d, u, v, w},
  c = Sqrt[-κ]; d = ArcTan[c];
  While[
        {u, v} = RandomReal[1, 2]; w = Tan[d u]/c;
        v > (1 - κ w^2) Exp[κ w^2]];
  RotationTransform[{{0, 0, 1}, Normalize[μ]}][Append[
  Sqrt[1 - w^2] Normalize[RandomVariate[NormalDistribution[], 2]], RandomChoice[{-1, 1}] w]]]

As with the von Mises-Fisher distribution, $\mu$ is a mean direction, and $\kappa$ is a concentration parameter; here, however, the clustering behavior of the variates depends on the sign of $\kappa$. For $\kappa > 0$, the Dimroth-Watson distribution is termed bipolar, with the variates clustering about the axis determined by $\mu$, while for $\kappa < 0$, the distribution is termed girdle, with the variates clustered about the great circle perpendicular to the axis determined by $\mu$.

Here is the result of generating $10^4$ random unit vectors from the Dimroth-Watson distribution, with mean direction $\mu=(0\;0\;1)^\top$ and various values of $\kappa$:

Dimroth-Watson samples with varying concentration parameter

share|improve this answer
1  
+1 For esoterica (at least to me). –  Mr.Wizard Oct 14 '12 at 19:58
add comment

A very simple way to get over the corner problem is to generate random points in the cube and throw away any that are outside the sphere.

ranvec := Module[{r},
  While[Norm[r = RandomReal[{-1, 1}, 3]] > 1];
  Normalize[r]];

ListPointPlot3D[Table[ranvec, {30000}], PlotStyle -> PointSize[0.001],
  BoxRatios -> 1, Boxed -> False, Axes -> False, ViewPoint -> Top, 
 ImageSize -> 250]

enter image description here

share|improve this answer
    
This is essentially Method 1 in Marsaglia's paper. –  J. M. Nov 2 '12 at 1:41
1  
@J.M., hopefully one day I'll have a great idea that isn't 40 years too late :-) –  Simon Woods Nov 2 '12 at 12:41
    
I used this method just the other day to generate uniformly distributed random points within an ellipsoid. This method requires the least thought, but you may end up throwing away many random numbers, particularly since you have to fill the cube rather than a rectangle. –  Timothy Wofford Oct 11 '13 at 17:06
add comment

I think kunkelwe's answer is on the right track but not quite there. This seems better to me:

randomVec = # * Normalize @ RandomReal[{-1, 1}, 3] &;

Test:

ListPointPlot3D[
  Table[randomVec @ 5, {1500}],
  BoxRatios -> 1
]

Mathematica graphics


David Zaslavsky shows that this too is incomplete. I shall graphically compare my incorrect result with his:

randomVecDZ = # * Normalize @ RandomReal[NormalDistribution[], 3] &;

ListPointPlot3D[Table[#@5, {30000}], PlotStyle -> PointSize[0.001], 
    BoxRatios -> 1, Boxed -> False, Axes -> False, ViewPoint -> Top, 
    ImageSize -> 250] & /@ {randomVec, randomVecDZ} // Row

Mathematica graphics

share|improve this answer
    
Nice visualization :-) I tried to do something similar but I didn't think of shrinking the point size to make it clear. –  David Z Oct 14 '12 at 7:01
3  
Note the clumping of points on the left point-cloud; this shows that the distribution followed by Normalize[RandomReal[{-1, 1}, 3]] is not uniform over the surface of the sphere. –  J. M. Oct 14 '12 at 17:05
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.