[Tex/LaTex] Gaussian random numbers

pgfmathrandom numbers

The Tikz manual has all sorts of different options regarding math, but the following is the first version which actually worked. Is there a better way to do this?

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{calc}

\newcount\gaussF
\edef\gaussR{0}
\edef\gaussA{0}

\def\gauss{
 \advance\gaussF by 1\relax
 \ifodd\gaussF
  \pgfmathrnd
  \edef\temp{\pgfmathresult}
  \pgfmathln{\temp}
  \edef\temp{\pgfmathresult}
  \pgfmathmultiply{-2}{\temp}
  \edef\temp{\pgfmathresult}
  \pgfmathsqrt{\temp}
  \edef\gaussR{\pgfmathresult}%radius = $sqrt(-2*ln(rnd))$
  \pgfmathrnd
  \edef\temp{\pgfmathresult}
  \pgfmathmultiply{360}{\temp}
  \edef\gaussA{\pgfmathresult}%angle = $360*rnd$
  \pgfmathcos{\gaussA}
  \edef\temp{\pgfmathresult}
  \pgfmathmultiply{\gaussR}{\temp}
 \else
  \pgfmathsin{\gaussA}
  \edef\temp{\pgfmathresult}
  \pgfmathmultiply{\gaussR}{\temp}
 \fi
 \pgfmathresult
}

\begin{document}

\noindent
\gauss\\
\gauss\\
\gauss\\
\gauss

\end{document}

Best Answer

I'm not sure that the algorithm in the question is correct, nevertheless it is certainly implemented in a sub-optimal manner. Although Jake's (now deleted) answer is readable it also has a huge overhead in calling the parser inside a function.

It is quite simple to use the lower level pgfmath macros (although I probably would be expected to say that).

Either way, in both cases, rnd produces pseudo-random numbers on the interval [0,1] so the possibility of ln(0) must be dealt with.

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}

\newcount\gaussF
\edef\gaussR{0}
\edef\gaussA{0}

\makeatletter
\pgfmathdeclarefunction{gaussR}{0}{%
 \global\advance\gaussF by 1\relax
 \ifodd\gaussF
  \pgfmathrnd@%
  \ifdim\pgfmathresult pt=0.0pt\relax%
    \def\pgfmathresult{0.00001}%
  \fi
  \pgfmathln@{\pgfmathresult}%
  \pgfmathmultiply@{-2}{\pgfmathresult}%
  \pgfmathsqrt@{\pgfmathresult}%
  \global\let\gaussR=\pgfmathresult%radius
  \pgfmathrnd@%
  \pgfmathmultiply@{360}{\pgfmathresult}%
  \global\let\gaussA=\pgfmathresult%angle
  \pgfmathcos@{\pgfmathresult}%
  \pgfmathmultiply@{\pgfmathresult}{\gaussR}%
 \else
  \pgfmathsin@{\gaussA}%
  \pgfmathmultiply@{\gaussR}{\pgfmathresult}%
 \fi
}

\pgfmathdeclarefunction{invgauss}{2}{%
  \pgfmathln{#1}% <- might need parsing
  \pgfmathmultiply@{\pgfmathresult}{-2}%
  \pgfmathsqrt@{\pgfmathresult}%
  \let\@radius=\pgfmathresult%
  \pgfmathmultiply{6.28318531}{#2}% <- might need parsing
  \pgfmathdeg@{\pgfmathresult}%
  \pgfmathcos@{\pgfmathresult}%
  \pgfmathmultiply@{\pgfmathresult}{\@radius}%
}

\pgfmathdeclarefunction{randnormal}{0}{%
  \pgfmathrnd@
  \ifdim\pgfmathresult pt=0.0pt\relax%
    \def\pgfmathresult{0.00001}%
  \fi%
  \let\@tmp=\pgfmathresult%
  \pgfmathrnd@%
  \ifdim\pgfmathresult pt=0.0pt\relax%
    \def\pgfmathresult{0.00001}%
  \fi
  \pgfmathinvgauss@{\pgfmathresult}{\@tmp}%
}

\begin{document}

\begin{tikzpicture}[x=10pt,y=10pt]
\foreach \i in {0,...,2000}
  \fill [opacity=1/10] (randnormal, randnormal) circle [radius=1/10];
\tikzset{shift=(0:10)}
\foreach \i  in {0,...,2000}
  \fill [blue, opacity=1/10] (gaussR, gaussR) circle [radius=1/10];
\end{tikzpicture}

\end{document}

enter image description here

Related Question