模組:沙盒/a2569875/PythonScript/Librarys/Random
外观
-- Module:PythonScript/Librarys/Random
local RandomMod = {}
local PyClass = require("Module:沙盒/a2569875/PythonScript/Class")
local Runtime = require("Module:沙盒/a2569875/PythonScript/Runtime")
-- 內部輔助函式:將 Python 物件轉為 Lua 數值
local function unwrap_num(obj)
if type(obj) == "table" and obj.__type == "instance" and obj.__builtin_value ~= nil then
obj = obj.__builtin_value
end
if type(obj) == "boolean" then return obj and 1 or 0 end
if type(obj) == "table" and obj.__type == "int" and obj.is_big then
local MathCore = require("Module:沙盒/a2569875/PythonScript/Runtime/Cmath")
return tonumber(MathCore.BigInt.to_string(obj.sign, obj.limbs))
end
return obj
end
-- 內部輔助函式:提取 Python 序列元素
local function extract_elements(seq)
if type(seq) == "table" and (seq.__type == "list" or seq.__type == "tuple") then return seq.elements end
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
local it = Runtime.builtins.iter.__call({seq})
local next_fn = PyClass.get_attr(it, "__next__")
local res = {}
while true do
local ok, val = pcall(Evaluator.invoke, next_fn.__func, {}, {}, next_fn.__self)
if not ok then break end
table.insert(res, val)
end
return res
end
-- LCG (線性同餘生成器) 核心參數,保證不超出 Lua 53-bit 安全整數範圍
local LCG_A = 1664525
local LCG_C = 1013904223
local LCG_M = 4294967296
-- 共用核心方法字典
local Random_dict = {}
-- 取得 [0.0, 1.0) 亂數的快捷閉包
local function get_rand(self)
return self.__dict._rand_func()
end
Random_dict.__init__ = {
__type = "builtin_method",
__call = function(args, kwargs)
local self, x = args[1], args[2]
self.__dict._is_global = false
self.__dict._state = 0
self.__dict._gauss_next = nil
self.__dict._rand_func = function()
self.__dict._state = (self.__dict._state * LCG_A + LCG_C) % LCG_M
return self.__dict._state / LCG_M
end
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
Evaluator.invoke(PyClass.get_attr(self, "seed"), {x}, {})
return nil
end
}
Random_dict.seed = {
__type = "builtin_method",
__call = function(args)
local self, a = args[1], args[2]
local seed_val = 0
if a ~= nil and a ~= Runtime.builtins.None then
if type(a) == "number" then seed_val = math.floor(a)
elseif type(a) == "string" then
for i = 1, #a do seed_val = (seed_val * 31 + string.byte(a, i)) % LCG_M end
else
seed_val = unwrap_num(a)
end
else
seed_val = math.floor(os.time() + os.clock() * 1000000)
end
if self.__dict._is_global then
math.randomseed(seed_val)
else
self.__dict._state = seed_val % LCG_M
end
self.__dict._gauss_next = nil
return Runtime.builtins.None
end
}
Random_dict.random = {
__type = "builtin_method",
__call = function(args) return get_rand(args[1]) end
}
Random_dict.uniform = {
__type = "builtin_method",
__call = function(args)
local self, a, b = args[1], unwrap_num(args[2]), unwrap_num(args[3])
return a + (b - a) * get_rand(self)
end
}
Random_dict.randint = {
__type = "builtin_method",
__call = function(args)
local self, a, b = args[1], unwrap_num(args[2]), unwrap_num(args[3])
if b < a then Runtime.raise("ValueError", "empty range for randint") end
local val = a + math.floor(get_rand(self) * (b - a + 1))
return Runtime.builtins.int.__call({val})
end
}
Random_dict.randrange = {
__type = "builtin_method",
__call = function(args)
local self = args[1]
local start, stop, step = 0, nil, 1
if #args == 2 then stop = unwrap_num(args[2])
elseif #args == 3 then start = unwrap_num(args[2]); stop = unwrap_num(args[3])
elseif #args == 4 then start = unwrap_num(args[2]); stop = unwrap_num(args[3]); step = unwrap_num(args[4])
else Runtime.raise("TypeError", "randrange expected 1 to 3 arguments") end
if step == 0 then Runtime.raise("ValueError", "zero step for randrange()") end
local width = stop - start
if (step > 0 and width <= 0) or (step < 0 and width >= 0) then Runtime.raise("ValueError", "empty range for randrange()") end
local n = math.floor((width + step - (step > 0 and 1 or -1)) / step)
local val = start + step * math.floor(get_rand(self) * n)
return Runtime.builtins.int.__call({val})
end
}
Random_dict.choice = {
__type = "builtin_method",
__call = function(args)
local self, seq = args[1], args[2]
local elts = extract_elements(seq)
if #elts == 0 then Runtime.raise("IndexError", "Cannot choose from an empty sequence") end
local idx = math.floor(get_rand(self) * #elts) + 1
return elts[idx]
end
}
Random_dict.choices = {
__type = "builtin_method",
__call = function(args, kwargs)
local self, pop = args[1], args[2]
local kw = kwargs or {}
local weights = kw.weights or (args[3] and args[3] ~= Runtime.builtins.None and args[3] or nil)
local cum_weights = kw.cum_weights
local k_val = kw.k or 1
local pop_elts = extract_elements(pop)
local n = #pop_elts
if n == 0 then Runtime.raise("IndexError", "Cannot choose from an empty sequence") end
local cw = {}
if cum_weights ~= nil and cum_weights ~= Runtime.builtins.None then
if weights ~= nil then Runtime.raise("TypeError", "Cannot specify both weights and cumulative weights") end
local cw_elts = extract_elements(cum_weights)
if #cw_elts ~= n then Runtime.raise("ValueError", "The number of weights does not match the population") end
for i = 1, n do cw[i] = unwrap_num(cw_elts[i]) end
elseif weights ~= nil and weights ~= Runtime.builtins.None then
local w_elts = extract_elements(weights)
if #w_elts ~= n then Runtime.raise("ValueError", "The number of weights does not match the population") end
local sum = 0
for i = 1, n do sum = sum + unwrap_num(w_elts[i]); cw[i] = sum end
end
local res = {}
if #cw > 0 then
local total = cw[n]
if total <= 0 then Runtime.raise("ValueError", "Total of weights must be greater than zero") end
for _ = 1, k_val do
local r = get_rand(self) * total
local lo, hi = 1, n
while lo < hi do
local mid = math.floor((lo + hi) / 2)
if cw[mid] < r then lo = mid + 1 else hi = mid end
end
table.insert(res, pop_elts[lo])
end
else
for _ = 1, k_val do
local idx = math.floor(get_rand(self) * n) + 1
table.insert(res, pop_elts[idx])
end
end
return { __type = "list", elements = res }
end
}
Random_dict.sample = {
__type = "builtin_method",
__call = function(args)
local self, pop, k = args[1], args[2], unwrap_num(args[3])
local pop_elts = extract_elements(pop)
local n = #pop_elts
if k < 0 or k > n then Runtime.raise("ValueError", "Sample larger than population or is negative") end
local res = {}
local pool = {}
for i = 1, n do pool[i] = pop_elts[i] end
for i = 1, k do
local j = math.floor(get_rand(self) * (n - i + 1)) + i
res[i] = pool[j]
pool[j] = pool[i]
end
return { __type = "list", elements = res }
end
}
Random_dict.shuffle = {
__type = "builtin_method",
__call = function(args)
local self, seq = args[1], args[2]
if type(seq) ~= "table" or seq.__type ~= "list" then Runtime.raise("TypeError", "shuffle() only accepts mutable sequences like list") end
local elts = seq.elements
for i = #elts, 2, -1 do
local j = math.floor(get_rand(self) * i) + 1
elts[i], elts[j] = elts[j], elts[i]
end
return Runtime.builtins.None
end
}
Random_dict.triangular = {
__type = "builtin_method",
__call = function(args)
local self, low, high = args[1], unwrap_num(args[2] or 0), unwrap_num(args[3] or 1)
local mode = args[4] ~= nil and unwrap_num(args[4]) or (low + high) / 2.0
local u = get_rand(self)
local c = (mode - low) / (high - low)
if u > c then
u = 1.0 - u; c = 1.0 - c
low, high = high, low
end
return low + (high - low) * math.sqrt(u * c)
end
}
Random_dict.gauss = {
__type = "builtin_method",
__call = function(args)
local self, mu, sigma = args[1], unwrap_num(args[2]), unwrap_num(args[3])
local z = self.__dict._gauss_next
self.__dict._gauss_next = nil
if not z then
local x2pi = get_rand(self) * math.pi * 2
local g2rad = math.sqrt(-2.0 * math.log(1.0 - get_rand(self)))
z = math.cos(x2pi) * g2rad
self.__dict._gauss_next = math.sin(x2pi) * g2rad
end
return mu + z * sigma
end
}
Random_dict.normalvariate = Random_dict.gauss
Random_dict.expovariate = {
__type = "builtin_method",
__call = function(args)
local self, lambd = args[1], unwrap_num(args[2])
return -math.log(1.0 - get_rand(self)) / lambd
end
}
Random_dict.gammavariate = {
__type = "builtin_method",
__call = function(args)
local self, alpha, beta = args[1], unwrap_num(args[2]), unwrap_num(args[3])
if alpha <= 0.0 or beta <= 0.0 then Runtime.raise("ValueError", "gammavariate: alpha and beta must be > 0.0") end
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
if alpha > 1.0 then
local d = alpha - 1.0 / 3.0
local c = 1.0 / math.sqrt(9.0 * d)
while true do
local x = Evaluator.invoke(Random_dict.gauss.__call, {self, 0.0, 1.0}, {})
local v = 1.0 + c * x
if v > 0.0 then
v = v * v * v
local u = get_rand(self)
local x2 = x * x
if u < 1.0 - 0.0331 * x2 * x2 or math.log(u) < 0.5 * x2 + d * (1.0 - v + math.log(v)) then
return d * v * beta
end
end
end
else
local u = get_rand(self)
local x = Evaluator.invoke(Random_dict.gammavariate.__call, {self, 1.0 + alpha, 1.0}, {})
return x * math.pow(u, 1.0 / alpha) * beta
end
end
}
Random_dict.betavariate = {
__type = "builtin_method",
__call = function(args)
local self, alpha, beta = args[1], unwrap_num(args[2]), unwrap_num(args[3])
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
local y = Evaluator.invoke(Random_dict.gammavariate.__call, {self, alpha, 1.0}, {})
if y == 0.0 then return 0.0 end
return y / (y + Evaluator.invoke(Random_dict.gammavariate.__call, {self, beta, 1.0}, {}))
end
}
Random_dict.paretovariate = {
__type = "builtin_method",
__call = function(args)
local self, alpha = args[1], unwrap_num(args[2])
return 1.0 / math.pow(1.0 - get_rand(self), 1.0 / alpha)
end
}
Random_dict.weibullvariate = {
__type = "builtin_method",
__call = function(args)
local self, alpha, beta = args[1], unwrap_num(args[2]), unwrap_num(args[3])
return alpha * math.pow(-math.log(1.0 - get_rand(self)), 1.0 / beta)
end
}
Random_dict.lognormvariate = {
__type = "builtin_method",
__call = function(args)
local self = args[1]
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
return math.exp(Evaluator.invoke(Random_dict.gauss.__call, args, {}))
end
}
Random_dict.vonmisesvariate = {
__type = "builtin_method",
__call = function(args)
local self, mu, kappa = args[1], unwrap_num(args[2]), unwrap_num(args[3])
if kappa <= 1e-6 then return math.pi * 2.0 * get_rand(self) end
local a = 1.0 + math.sqrt(1.0 + 4.0 * kappa * kappa)
local b = (a - math.sqrt(2.0 * a)) / (2.0 * kappa)
local r = (1.0 + b * b) / (2.0 * b)
while true do
local u1 = get_rand(self)
local z = math.cos(math.pi * u1)
local f = (1.0 + r * z) / (r + z)
local c = kappa * (r - f)
local u2 = get_rand(self)
if u2 < c * (2.0 - c) or u2 <= c * math.exp(1.0 - c) then
local u3 = get_rand(self)
local res = mu + ((u3 > 0.5) and math.acos(f) or -math.acos(f))
return res % (2.0 * math.pi)
end
end
end
}
Random_dict.getrandbits = {
__type = "builtin_method",
__call = function(args)
local self, k = args[1], unwrap_num(args[2])
if k <= 0 or math.floor(k) ~= k then Runtime.raise("ValueError", "number of bits must be greater than zero") end
if k > 100000 then Runtime.raise("ValueError", "too many bits") end
local bin_parts = {}
for _ = 1, k do table.insert(bin_parts, get_rand(self) < 0.5 and "0" or "1") end
return Runtime.builtins.int.__call({ "0b" .. table.concat(bin_parts), 2 })
end
}
Random_dict.randbytes = {
__type = "builtin_method",
__call = function(args)
local self, n = args[1], unwrap_num(args[2])
if n < 0 or math.floor(n) ~= n then Runtime.raise("ValueError", "number of bytes must be non-negative integer") end
if n > 1000000 then Runtime.raise("ValueError", "too many bytes") end
local bytes = {}
for _ = 1, n do table.insert(bytes, string.char(math.floor(get_rand(self) * 256))) end
return { __type = "bytes", _data = table.concat(bytes) }
end
}
local RandomClass = PyClass.make_class("Random", {}, Random_dict)
function RandomMod.get_module()
-- 建立全域的虛擬 Random 實例,其背後直接對接 Lua 的 `math.random` 來發揮最大效能
local global_inst = PyClass.make_instance(RandomClass)
global_inst.__dict._is_global = true
global_inst.__dict._gauss_next = nil
global_inst.__dict._rand_func = function() return math.random() end
local Evaluator = require("Module:沙盒/a2569875/PythonScript/Evaluator")
Evaluator.invoke(PyClass.get_attr(global_inst, "seed"), {}, {})
-- 將 Random_dict 的所有方法直接對應綁定至模組全域
local mod_dict = {
Random = RandomClass,
SystemRandom = {
__type = "builtin_method",
__call = function() Runtime.raise("NotImplementedError", "SystemRandom is not available in MediaWiki environment") end
}
}
for k, v in pairs(Random_dict) do
if k ~= "__init__" and k ~= "__new__" then
mod_dict[k] = { __type = "bound_method", __self = global_inst, __func = v }
end
end
local mod = PyClass.make_instance(PyClass.make_class("module", {}, mod_dict))
mod.__dict["__name__"] = "random"
return mod
end
return RandomMod