9

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}
7
  • 3
    A better way to do what? What are "Gaussian random numbers"? What is wrong with the random numbers of PGFmath? What does not work without your code? Why are you loading TikZ and its calc library? Is this question about PGFmath? Dec 2 '13 at 23:42
  • I went through a lot of versions using different constructs before this one, generating a host of obtuse error messages and/or no numbers. Gaussian (normally distributed) random numbers can take on any value from -\infty to +\infty (unlike rnd which is uniformly distributed). Pgfmath is part of Tikz, and I originally intended to graph the results. And yes, this is a question about pgfmath. Dec 3 '13 at 4:56
  • I don't know why do you need this. But in this kind of problems, I thinck it's MUCH better to calculate the numbers only ONCE with another program, and input into the document.
    – Manuel
    Dec 3 '13 at 8:54
  • 2
    @JohnKormylo: You don't need the calc library for using pgfmath. calc is for coordinate calculations (like ($(0,0)+(2,2)$).
    – Jake
    Dec 3 '13 at 9:13
  • 2
    @Jake As I said, I don't know the context. But in the end you will have one document. Why ask latex to calculate random numbers every compilation instead of calculate them once and then input them. I think it's more “clean” and “logical” that way.
    – Manuel
    Dec 3 '13 at 9:31
14

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

6
  • It would be a lot easier if they were in the manual. And the algorithm is valid (you can find it in Numerical Recipes in C). Two Gaussian distributions in rectangular coordinates when converted to polar coordinates give an exponential in r^2 and uniform in angle. Dec 3 '13 at 15:32
  • I see that randnormal uses the same algorithm, except that it only uses the cosine component. Dec 3 '13 at 16:04
  • What exactly does \pgfmathdeg@ do? You also might be interested in my essay elfsoft2000.com/reports/sines.pdf Dec 3 '13 at 16:23
  • @JohnKormylo The manual describes how every function (e.g., myfunc) declared with \pgfmathdeclarefunction defines a macro (e.g., \pgfmathmyfunc) which takes the required number of arguments, which are then parsed and passed onto the internal macro (i.e., \pgfmathmyfunc@) which takes the same number of arguments but they are expected to be completely parsed numbers without units (which is what the parser provides). The code provided as the last argument to \pgfmathdeclarefunction works with these parsed arguments to perform the calculation. Dec 3 '13 at 17:58
  • @JohnKormylo in fact the deg function is unnecessary. I've updated the answer. Dec 3 '13 at 18:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.