Re: Random number with custom distribution
- To: mathgroup at smc.vnet.net
- Subject: [mg107461] Re: [mg107445] Random number with custom distribution
- From: DrMajorBob <btreat1 at austin.rr.com>
- Date: Sat, 13 Feb 2010 05:23:19 -0500 (EST)
- References: <201002121008.FAA15765@smc.vnet.net>
- Reply-to: drmajorbob at yahoo.com
NOTE: there's a BUG indicated below, in RandomReal or Interpolation, probably the latter. Here's an example PDF, first just a raw non-negative function: fRaw[x_] = Abs@Piecewise[{{Cos@x, 0 <= x < Pi}, {Sin@x, Pi <= x <= 2 Pi}}]; Integrate[fRaw@x, {x, 0, 2 Pi}] 4 (Use NIntegrate, if Integrate doesn't work.) To make it a PDF, scale it by dividing by the total integral (4 in this case): pdf[x_] = Abs@Piecewise[{{Cos@x, 0 <= x < Pi}, {Sin@x, Pi <= x <= 2 Pi}}]/4; pdfPlot = Plot[pdf@x, {x, 0, 2 Pi}, PlotStyle -> Directive[Red, Thickness[.01]]] Next, we need the CDF: Clear[cdf] cdf = cdf /. First@NDSolve[{cdf'[x] == pdf@x, cdf@0 == 0}, cdf, {x, 0, 2 Pi}]; cdfPlot = Plot[cdf@x, {x, 0, 2 Pi}] In order to sample, we need the inverse CDF. First, retrieve data points from the plot, reversing {x,y} in each datapoint: data = First@Cases[cdfPlot, Line[x_] :> Reverse /@ x, Infinity]; ListPlot@data That's a mirror image of the CDF plot. Interpolating gives an inverse to the CDF: invCDF = Interpolation@data InterpolatingFunction[{{3.20571*10^-8,1.}},<>] That doesn't cover the whole range, unfortunately, so we should have made sure that "data" includes the points {0,0} and {2Pi,1}. The current first and last points are data[[{1, -1}]] {{3.20571*10^-8, 1.28228*10^-7}, {1., 6.28319}} We'll simply replace them with the end-points we want: data[[{1, -1}]] = {{0, 0}, {1, 2 Pi}} {{0, 0}, {1, 2 \[Pi]}} and recompute the interpolation: invCDF=Interpolation@data InterpolatingFunction[{{0.,1.}},<>] And now, random sampling is easy: random[] := invCDF@RandomReal[] Table[random[], {100}] {0.173608, 0.135088, 6.11428, 4.90872, 4.30839, 5.06734, 2.80241, \ 0.187845, 3.12592, 4.07651} But there's a BUG lurking, since random[] is sometimes BADLY negative: Select[Table[random[], {100000}], ! 0 <= # <= 2 Pi &] {-0.0385181, -9.55779, -6.85895, -10.0228, -8.54523, -9.86493, \ -10.2442, -3.56191, -9.99411, -7.73028, -8.6684, -8.18295, -8.29155, \ -7.13628, -0.458468, -5.05335, -1.66142, -9.86214, -7.29216, \ -2.61625, -4.49397, -9.74907, -8.68753, -8.00137, -9.98978} To fix this (and eliminate outputs above 2Pi, if any), redefine Clear[random] random[] := Module[{try = invCDF@RandomReal[]}, If[0 <= try <= 2 Pi, try, random[]]] n = 10000; Select[samples = Table[random[], {n}], ! 0 <= # <= 2 Pi &] hist = Histogram[samples, {.01}, "ProbabilityDensity", ChartStyle -> Directive[Blue, EdgeForm[]]]; {} Show[hist, pdfPlot] A million samples makes the two plots match far better... but if your computer is slow, don't try it. Bobby On Fri, 12 Feb 2010 04:08:16 -0600, wiso <giurrero at gmail.com> wrote: > Dear experts, is there a simple way to generate a random sample of a > custom distribution? With Random I can generate random numbers > according to the predefined distribution (Uniform, Normal, ...) but > what about custom distribution (in my case a polynomial function)? > -- DrMajorBob at yahoo.com
- References:
- Random number with custom distribution
- From: wiso <giurrero@gmail.com>
- Random number with custom distribution