1
0
mirror of https://codeberg.org/tenplus1/farming.git synced 2024-11-14 06:10:44 +01:00
farming/statistics.lua

164 lines
3.2 KiB
Lua
Raw Normal View History

2015-05-20 11:22:04 +02:00
-- Approximations for erf(x) and erfInv(x) from
-- https://en.wikipedia.org/wiki/Error_function
local statistics = {}
2024-09-12 12:21:32 +02:00
local random, floor, ceil = math.random, math.floor, math.ceil
local exp, log, sqrt = math.exp, math.log, math.sqrt
local ROOT_2 = sqrt(2.0)
local A = 8 * (math.pi - 3.0) / (3.0 * math.pi * (4.0 - math.pi))
2015-05-20 11:22:04 +02:00
local B = 4.0 / math.pi
local C = 2.0 / (math.pi * A)
2015-05-20 11:22:04 +02:00
local D = 1.0 / A
2020-07-02 15:31:12 +02:00
2024-09-12 12:21:32 +02:00
local function erf(x)
2024-09-12 12:21:32 +02:00
if x == 0 then return 0 end
2015-05-20 11:22:04 +02:00
local xSq = x * x
local aXSq = A * xSq
2024-09-12 12:21:32 +02:00
local v = sqrt(1.0 - exp(-xSq * (B + aXSq) / (1.0 + aXSq)))
2015-05-20 11:22:04 +02:00
return (x > 0 and v) or -v
end
2020-07-02 15:31:12 +02:00
2024-09-12 12:21:32 +02:00
local function erf_inv(x)
2024-09-12 12:21:32 +02:00
if x == 0 then return 0 end
2024-09-12 12:21:32 +02:00
if x <= -1 or x >= 1 then return nil end
2024-09-12 12:21:32 +02:00
local y = log(1 - x * x)
2015-05-20 11:22:04 +02:00
local u = C + 0.5 * y
2024-09-12 12:21:32 +02:00
local v = sqrt(sqrt(u * u - D * y) - u)
2015-05-20 11:22:04 +02:00
return (x > 0 and v) or -v
end
2020-07-02 15:31:12 +02:00
2015-05-20 11:22:04 +02:00
local function std_normal(u)
return ROOT_2 * erf_inv(2.0 * u - 1.0)
end
2020-07-02 15:31:12 +02:00
2015-05-20 11:22:04 +02:00
local function generate_cdf(lambda_index, lambda)
2024-09-12 12:21:32 +02:00
local max = ceil(4 * lambda)
local pdf = exp(-lambda)
2015-05-20 11:22:04 +02:00
local cdf = pdf
local t = { [0] = pdf }
for i = 1, max - 1 do
pdf = pdf * lambda / i
cdf = cdf + pdf
t[i] = cdf
end
return t
end
2020-07-02 15:31:12 +02:00
2024-09-12 12:21:32 +02:00
local cdf_table = {}
2015-05-20 11:22:04 +02:00
for li = 1, 100 do
cdf_table[li] = generate_cdf(li, 0.25 * li)
end
2020-07-02 15:31:12 +02:00
2024-09-12 12:21:32 +02:00
local function poisson(lambda, max)
2015-05-20 11:22:04 +02:00
if max < 2 then
2024-09-12 12:21:32 +02:00
return (random() < exp(-lambda) and 0) or 1
2015-05-20 11:22:04 +02:00
elseif lambda >= 2 * max then
return max
end
2024-09-12 12:21:32 +02:00
local u = random()
local lambda_index = floor(4 * lambda + 0.5)
2015-05-20 11:22:04 +02:00
local cdfs = cdf_table[lambda_index]
if cdfs then
2015-05-20 11:22:04 +02:00
lambda = 0.25 * lambda_index
2024-09-12 12:21:32 +02:00
if u < cdfs[0] then return 0 end
if max > #cdfs then max = #cdfs + 1 else max = floor(max) end
if u >= cdfs[max - 1] then return max end
2015-05-20 11:22:04 +02:00
if max > 4 then -- Binary search
2015-05-20 11:22:04 +02:00
local s = 0
2015-05-20 11:22:04 +02:00
while s + 1 < max do
2024-09-12 12:21:32 +02:00
local m = floor(0.5 * (s + max))
2024-09-12 12:21:32 +02:00
if u < cdfs[m] then max = m else s = m end
2015-05-20 11:22:04 +02:00
end
else
for i = 1, max - 1 do
2024-09-12 12:21:32 +02:00
if u < cdfs[i] then return i end
2015-05-20 11:22:04 +02:00
end
end
return max
else
2024-09-12 12:21:32 +02:00
local x = lambda + sqrt(lambda) * std_normal(u)
2024-09-12 12:21:32 +02:00
return (x < 0.5 and 0) or (x >= max - 0.5 and max) or floor(x + 0.5)
2015-05-20 11:22:04 +02:00
end
end
2024-09-12 12:21:32 +02:00
-- Error and Inverse error functions
2015-05-20 11:22:04 +02:00
statistics.erf = erf
statistics.erf_inv = erf_inv
--- Standard normal distribution function (mean 0, standard deviation 1).
-- @return - Any real number (actually between -3.0 and 3.0).
2015-05-20 11:22:04 +02:00
statistics.std_normal = function()
2024-09-12 12:21:32 +02:00
local u = random()
2024-09-12 12:21:32 +02:00
if u < 0.001 then return -3.0 elseif u > 0.999 then return 3.0 end
2015-05-20 11:22:04 +02:00
return std_normal(u)
end
--- Standard normal distribution function (mean 0, standard deviation 1).
-- @param mu - The distribution mean.
-- @param sigma - The distribution standard deviation.
-- @return - Any real number (actually between -3*sigma and 3*sigma).
2015-05-20 11:22:04 +02:00
statistics.normal = function(mu, sigma)
2024-09-12 12:21:32 +02:00
local u = random()
2015-05-20 11:22:04 +02:00
if u < 0.001 then
return mu - 3.0 * sigma
elseif u > 0.999 then
return mu + 3.0 * sigma
end
2015-05-20 11:22:04 +02:00
return mu + sigma * std_normal(u)
end
--- Poisson distribution function.
-- @param lambda - The distribution mean and variance.
-- @param max - The distribution maximum.
-- @return - An integer between 0 and max (both inclusive).
2015-05-20 11:22:04 +02:00
statistics.poisson = function(lambda, max)
2015-05-20 11:22:04 +02:00
lambda, max = tonumber(lambda), tonumber(max)
2024-09-12 12:21:32 +02:00
if not lambda or not max or lambda <= 0 or max < 1 then return 0 end
2015-05-20 11:22:04 +02:00
return poisson(lambda, max)
end
return statistics