{
  "aliases": null,
  "backlinks": null,
  "body": "\n## Statement\n\nLet $f \\colon [0, \\infty) \\to (0, \\infty)$ be continuous, non-increasing and integrable.\nLet $n \\ge 2$, let $0 = x_0 \u003c x_1 \u003c \\cdots \u003c x_{n-1} = r$, and let $v \u003e 0$ be such that:\n\n1. $x_i \\bigl(f(x_{i-1}) - f(x_i)\\bigr) = v$ for $1 \\le i \\le n-1$;\n2. $r f(r) + \\int_r^\\infty f(x)\\, dx = v$.\n\nDefine the layers $L_i = [0, x_i] \\times [f(x_i), f(x_{i-1})]$ for $1 \\le i \\le n-1$ and the base $L_n = \\set{(x, y) : 0 \\le x \\le r,\\ 0 \\le y \\le f(r)} \\cup \\set{(x, y) : x \u003e r,\\ 0 \\le y \\le f(x)}$.\nThe *ziggurat algorithm* repeats the following trial until it returns a value.\nIt chooses $i \\in \\set{1, \\ldots, n}$ uniformly at random, draws a point $(X, Y)$ uniformly from $L_i$, and returns $X$ if $Y \\le f(X)$.\nThen the returned value has [[probability density|0ZY9DX8FBF]] $f / \\int_0^\\infty f$, and each trial returns a value with probability $\\int_0^\\infty f / (n v)$.\n\n## Remarks\n\nEach layer has area $v$ by clauses 1 and 2, and the layers overlap only in boundary segments of measure zero.\nSo a uniform choice of layer followed by a uniform point in it gives a point uniform on the union $Z = L_1 \\cup \\cdots \\cup L_n$.\nSince $f$ is non-increasing, $Z$ contains the region $\\set{(x, y) : x \\ge 0,\\ 0 \\le y \\le f(x)}$ under the graph.\nA uniform point of $Z$ conditioned to lie under the graph is uniform there, and its first coordinate has density proportional to $f$.\nThis is [[rejection sampling|R1QBNFTHFP]] with a uniform envelope on $Z$.\nThe speed comes from a shortcut: in layer $i \\le n-1$, if $X \u003c x_{i-1}$ then $Y \\le f(x_{i-1}) \\le f(X)$, so the point is accepted without evaluating $f$.\nFor large $n$, such as $n = 128$ or $256$, almost all trials end this way, after one random integer, one multiplication and one comparison.\nA uniform point of the base $L_n$ is drawn as follows.\nWith probability $r f(r)/v$ it is a uniform point of the rectangle $[0, r] \\times [0, f(r)]$.\nOtherwise $X$ is drawn from the tail density proportional to $f$ on $(r, \\infty)$, which needs a separate tail sampler, and $Y$ is uniform on $[0, f(X)]$.\nFor the standard [[normal distribution|05AQE0QR0D]], one samples $\\abs{Z}$ with $f(x) = e^{-x^2/2}$ and attaches a random sign.\nThe numbers $x_i$, $r$ and $v$ are computed once, by solving the equations of clauses 1 and 2 numerically.\n\n## Explanations\n\n### L1 human\n\nPicture the area under a decreasing density curve covered by a stack of horizontal slabs of equal area, like a stepped pyramid, the ziggurat.\nPick a slab at random and a random point in it.\nMost of each slab lies completely under the curve, and a point there is accepted at once.\nOnly near the right edge of a slab, or in the bottom slab with its infinite tail, is extra work needed.\nThe result is an exact sampler that, most of the time, costs almost nothing.\n\n### L2 human\n\nThe algorithm, due to Marsaglia and Tsang, is a form of rejection sampling with a piecewise constant envelope that fits the density tightly.\nIt is used for normal and exponential random numbers in many numerical libraries.\nFor symmetric densities such as the normal, it samples the right half and attaches a random sign; often the sign comes from a spare bit of the same random integer.\nIts exactness depends on good uniform variates and an exact tail sampler; the tail of the normal is usually handled by a method of Marsaglia based on exponential variates.\n",
  "commit": "ff8a40749dc16545e3d2fc637f6b0196e81d0bfa",
  "front": {
    "about": [
      "R1QBNFTHFP"
    ],
    "kind": "statement",
    "msc": [
      "65C10"
    ],
    "name": "Ziggurat algorithm",
    "role": "theorem",
    "same_as": {
      "wikidata": "Q2894386",
      "wikipedia": "Ziggurat algorithm"
    },
    "sources": [
      {
        "url": "https://en.wikipedia.org/wiki/Ziggurat_algorithm"
      }
    ],
    "tag": "2PSDHH34BJ",
    "uses": [
      "R1QBNFTHFP",
      "0ZY9DX8FBF",
      "0AREFQ9Z65",
      "05AQE0QR0D"
    ]
  },
  "kind": "statement",
  "link": "https://tamnd.github.io/math2/node/2PSDHH34BJ/",
  "links": [
    {
      "field": "about",
      "target": "R1QBNFTHFP"
    },
    {
      "field": "uses",
      "target": "R1QBNFTHFP"
    },
    {
      "field": "uses",
      "target": "0ZY9DX8FBF"
    },
    {
      "field": "uses",
      "target": "0AREFQ9Z65"
    },
    {
      "field": "uses",
      "target": "05AQE0QR0D"
    }
  ],
  "markdown": "https://tamnd.github.io/math2/node/2PSDHH34BJ.md",
  "msc": [
    "65C10"
  ],
  "name": "Ziggurat algorithm",
  "prov": null,
  "role": "theorem",
  "source": "https://github.com/tamnd/math2/blob/main/nodes/2PS/2PSDHH34BJ.md",
  "status": null,
  "tag": "2PSDHH34BJ",
  "uri": "m2://2PSDHH34BJ",
  "url": "https://tamnd.github.io/math2/wiki/ziggurat-algorithm/",
  "writing": {
    "tag": "2PSDHH34BJ",
    "kind": "statement",
    "level": "W0",
    "next": [
      {
        "what": "an inventory row",
        "note": "the row of the inventory that the node accounts for",
        "have": 0,
        "want": 1
      },
      {
        "what": "a source with a locator",
        "have": 0,
        "want": 1
      }
    ]
  }
}
