模組:Complex Number/Dual Number
外观

本模組為基於Module:Complex Number的二元數運算系統,可提供其他模組呼叫使用,而若要直接在模板或條目中使用可透過Module:Complex Number/Calculate或{{複變運算}}來完成。
模組內容
本模組有2套數學資料結構的定義以及對應的數學運算庫:
使用方法
LUA
- 初始化數學庫
local 自訂函數庫名稱 = require("Module:Complex Number/Dual Number").函數庫名稱.init()
- 例如初始化二元數數學庫:
local dumath = require("Module:Complex Number/Dual Number").dumath.init()
- 例如初始化二元數數學庫:
- 初始化指定數學結構的數字
local 變數名稱 = 自訂函數庫名稱.constructor("描述數字的字串")
- 例如:
local num1 = dumath.constructor("2+3ε")
- 例如:
- 執行運算
- 例如:
local num1 = dumath.constructor("2+3ε") local num2 = dumath.constructor("4+5ε") print(num1 * num2)
- 輸出:8+22ε
- 或者使用函數庫內容:
local num1 = dumath.constructor("1+ε") print(dumath.sqrt(num1))
- 輸出:1+0.5ε
- 例如:
模板
使用{{複變運算}}
- 語法:
{{複變運算|運算式|number class=Module:Complex Number/Dual Number.函數庫名稱}}
使用{{計算結果}}
參見
p={}
local sollib = require("Module:Complex_Number/Solver")
p.dumath={
abs=function(z)
local real, epsilon = p.dumath.readPart(z)
if math.abs(epsilon) < 1e-12 then return math.abs(real) end
return math.sqrt(real * real)
end,
floor=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.floor(real), math.floor(epsilon))
end,
ceil=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.ceil(real), math.ceil(epsilon))
end,
round=function(op1,op2,op3)
local number = p.dumath.getDualNumber(tonumber(op1) or op1.real or 0, (tonumber(op1) and 0) or op1.epsilon or 0)
local digs = p.dumath.getDualNumber(tonumber(op2) or (op2 or {}).real or 0, (tonumber(op2) and 0) or (op2 or {}).epsilon or 0)
local base = p.dumath.getDualNumber(tonumber(op3) or (op3 or {}).real or 10, (tonumber(op3) and 0) or (op3 or {}).epsilon or 0)
local round_rad = p.dumath.pow(base,digs)
local check_number = number * round_rad
check_number.real = check_number.real + 0.5; check_number.epsilon = check_number.epsilon + 0.5;
return p.dumath.floor( check_number ) / round_rad
end,
div=function(op1,op2)return op1 / op2 end,
re=function(z)return tonumber(z) or z.real end,
im=function(z) return (tonumber(z) and 0) or z.epsilon end,
nonRealPart=function(z) return p.dumath.getDualNumber(0, (tonumber(z) and 0) or z.epsilon) end,
conjugate=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(real, -epsilon)
end,
inverse=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(1/real, -epsilon/(real*real))
end,
tovector=function(z)
return {p.dumath.readPart(z)}
end,
trunc=function(z,digs)
local real, epsilon = p.dumath.readPart(z)
local n = tonumber(digs) or digs.real or 0
return p.dumath.getDualNumber(sollib._trunc(real,n), sollib._trunc(epsilon,n))
end,
digits=function(z)
local real, epsilon = p.dumath.readPart(z)
real, epsilon = math.floor(math.abs(real)), math.floor(math.abs(epsilon))
return math.max(tostring(real):len(),tostring(epsilon):len())
end,
sqrt=function(z)
local real, epsilon = p.dumath.readPart(z)
if epsilon == 0 then return p.dumath.getDualNumber(math.sqrt(real),0):clean()end
return p.dumath.pow(z,0.5)
end,
sin=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.sin(real), epsilon * math.cos(real))
end,
cos=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.cos(real), -epsilon * math.sin(real))
end,
tan=function(z)
local real, epsilon = p.dumath.readPart(z)
local sec = 1 / math.cos(real)
return p.dumath.getDualNumber(math.tan(real), epsilon * sec * sec)
end,
cot=function(z)
local real, epsilon = p.dumath.readPart(z)
local csc = 1 / math.sin(real)
return p.dumath.getDualNumber(1 / math.tan(real), -epsilon * csc * csc)
end,
asin=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.asin(a), b / math.sqrt(1-a*a))
end,
acos=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.acos(a), -b / math.sqrt(1-a*a))
end,
atan=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.atan(a), b / (1+a*a))
end,
acot=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.atan(1/a), -b / (1+a*a))
end,
sinh=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.sinh(a), b * math.cosh(real))
end,
cosh=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.cosh(a), b * math.sinh(real))
end,
tanh=function(z)
local a, b = p.dumath.readPart(z)
local sech = 1 / math.cosh(a)
return p.dumath.getDualNumber(math.tanh(a), b * sech * sech)
end,
coth=function(z)
local a, b = p.dumath.readPart(z)
local csch = 1 / math.sinh(a)
return p.dumath.getDualNumber(1 / math.tanh(a), -b * csch * csch)
end,
asinh=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.asinh(a), b / math.sqrt(1+a*a))
end,
acosh=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.acosh(a), b / math.sqrt(a*a-1))
end,
atanh=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.atanh(a), b / (1-a*a))
end,
acoth=function(z)
local a, b = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.atanh(1/a), b / (1-a*a))
end,
dot=function (op1, op2)
local real1, epsilon1 = p.dumath.readPart(op1)
local real2, epsilon2 = p.dumath.readPart(op2)
return real1 * real2 + epsilon1 * epsilon2
end,
sgn=function(z)
local real, epsilon = p.dumath.readPart(z)
if real == 0 and epsilon == 0 then return p.dumath.getDualNumber(0, 0) end
local length = math.sqrt( real * real )
return p.dumath.getDualNumber(real/length, epsilon/length)
end,
arg=function(z)return epsilon / real end,
exp=function(z)
local real, epsilon = p.dumath.readPart(z)
local exp_r = math.exp(real)
return p.dumath.getDualNumber(exp_r, epsilon*exp_r)
end,
elog=function(z)
local real, epsilon = p.dumath.readPart(z)
return p.dumath.getDualNumber(math.log(real), epsilon/real)
end,
log=function(z,basez)
if basez~=nil then return p.dumath.elog(basez) * p.dumath.inverse(p.dumath.elog(z)) end
return p.dumath.elog(z)
end,
pow=function(op1,op2)
local check_op1, check_op2 = tonumber(tostring(op1)) or -1, tonumber(tostring(op2)) or -1
if check_op1 == 1 then return p.dumath.getDualNumber(1,0) end -- 1^z === 1
if check_op2 == 1 then return op1 end -- z^1 === z
if check_op2 == 0 then -- z^0
if check_op1 ~= 0 then return p.dumath.getDualNumber(1,0) -- z^0 === 1, z ≠ 0
else return p.dumath.getDualNumber(tonumber('nan'),0) end -- 0^0 Indeterminate
elseif check_op1 == 0 then return p.dumath.getDualNumber(0,0) end -- 0^z === 0, z ≠ 0
--a ^ z
local a = p.dumath.getDualNumber( tonumber(op1) or op1.real, (tonumber(op1) and 0) or op1.epsilon )
local z = p.dumath.getDualNumber( tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.epsilon )
return p.dumath.exp(z * p.dumath.log(a)):clean()
end,
DualNumberMeta = {
__add = function (op1, op2)
local real1, real2 = tonumber(op1) or op1.real, tonumber(op2) or op2.real
local epsilon1, epsilon2 = (tonumber(op1) and 0) or op1.epsilon, (tonumber(op2) and 0) or op2.epsilon
return p.dumath.getDualNumber(real1 + real2, epsilon1 + epsilon2)
end,
__sub = function (op1, op2)
local real1, real2 = tonumber(op1) or op1.real, tonumber(op2) or op2.real
local epsilon1, epsilon2 = (tonumber(op1) and 0) or op1.epsilon, (tonumber(op2) and 0) or op2.epsilon
return p.dumath.getDualNumber(real1 - real2, epsilon1 - epsilon2)
end,
__mul = function (op1, op2)
local a, c = tonumber(op1) or op1.real, tonumber(op2) or op2.real
local b, d = (tonumber(op1) and 0) or op1.epsilon, (tonumber(op2) and 0) or op2.epsilon
return p.dumath.getDualNumber(a * c, a * d + b * c)
end,
__div = function (op1, op2)
local a, c = tonumber(op1) or op1.real, tonumber(op2) or op2.real
local b, d = (tonumber(op1) and 0) or op1.epsilon, (tonumber(op2) and 0) or op2.epsilon
return p.dumath.getDualNumber(a/c, (b * c - a * d) / (c*c))
end,
__mod = function (op1, op2)
local x = p.dumath.getDualNumber(tonumber(op1) or op1.real, (tonumber(op1) and 0) or op1.epsilon)
local y = p.dumath.getDualNumber(tonumber(op2) or op2.real, (tonumber(op2) and 0) or op2.epsilon)
return x - y * p.dumath.floor(x / y)
end,
__tostring = function (this)
local body = ''
if this.real ~= 0 then body = tostring(this.real) end
if this.epsilon ~= 0 then
if body ~= '' and this.epsilon > 0 then body = body .. '+' end
if this.epsilon == -1 then body = body .. '-' end
if math.abs(this.epsilon) ~= 1 then body = body .. tostring(this.epsilon) end
body = body .. 'ε'
end
if sollib._isNaN(this.real) or sollib._isNaN(this.epsilon) then body = 'nan' end
if body == '' then body = '0' end
return body
end,
__unm = function (this)
return p.dumath.getDualNumber(-this.real, -this.epsilon)
end,
__eq = function (op1, op2)
local diff_real = math.abs( (tonumber(op1) or op1.real) - (tonumber(op2) or op2.real) )
local diff_epsilon1 = math.abs( ( (tonumber(op1) and 0) or op1.epsilon) - ( (tonumber(op2) and 0) or op2.epsilon) )
return diff_real < 1e-12 and diff_epsilon1 < 1e-12
end,
},
readDualNumber = function(z)
if type(z) == type({}) then --if already be complex number, don't run string find.
if z.numberType == "dualnumber" then
return z
elseif z.numberType == "complex" then
return p.dumath.getDualNumber(z.real, 0)
elseif z.numberType == "quaternion" then
return p.dumath.getDualNumber(z.real, 0)
end
elseif type(z) == type(0) then
return p.dumath.getDualNumber(z, 0)
end
return p.dumath.getDualNumber(tonumber(z) or z.real, (tonumber(z) and 0) or z.epsilon)
end,
readPart = function(z)
if type(z) == type({}) and (z.numberType == "dualnumber") then --if already be dual number, don't run string find.
return z.real, z.epsilon
elseif type(z) == type({}) and (z.numberType == "complex" or z.numberType == "quaternion") then --if already be complex number, don't run string find.
return z.real, 0
elseif type(z) == type(0) then
return z, 0
end
return tonumber(z) or z.real, (tonumber(z) and 0) or z.epsilon or 0
end,
ele=function(id)
local _zero = p.dumath.getDualNumber(0, 0)
local eles = (p.dumath.elements or {})
local id_msg = tonumber(tostring(id)) or 0
return eles[id_msg+1]or _zero
end,
getDualNumber = function(real,epsilon)
DualNumber = {}
setmetatable(DualNumber,p.dumath.DualNumberMeta)
function DualNumber:update()
self.argument = 0
self.length = math.sqrt( self.real * self.real )
if self.epsilon ~= 0 then
self.argument = self.epsilon / self.real
else
if self.real > 0 then self.argument = 0.0
else self.argument = math.pi end
end
end
function DualNumber:clean()
if math.abs(self.real) <= 1e-12 then self.real = 0 end
if math.abs(self.epsilon) <= 1e-12 then self.epsilon = 0 end
if math.abs(self.real - math.floor(self.real)) <= 1e-12 then self.real = math.floor(self.real) end
if math.abs(self.epsilon - math.floor(self.epsilon)) <= 1e-12 then self.epsilon = math.floor(self.epsilon) end
return self
end
DualNumber.real, DualNumber.epsilon = real, epsilon
DualNumber.numberType = "dualnumber"
return DualNumber
end,
toDualNumber = function(num_str)
if type(num_str) == type({}) then --if already be dual number, don't run string find.
if num_str.numberType == "dualnumber" then
return num_str
elseif num_str.numberType == "complex" then
return p.dumath.getDualNumber(num_str.real, 0)
elseif num_str.numberType == "quaternion" then
return p.dumath.getDualNumber(num_str.real, 0)
end
elseif type(num_str) == type(0) then
return p.dumath.getDualNumber(num_str, 0)
elseif type(num_str) == type("string") then
local check_number = tonumber(num_str)
if check_number ~= nil then return p.dumath.getDualNumber(check_number, 0) end
end
local real, epsilon
if num_str == nil then return nil end
if ( type(num_str)==type(0) or ( (type(num_str)==type({"table"})) and type(num_str.real)==type(0) ) ) then
real, epsilon = tonumber(num_str) or num_str.real, (tonumber(num_str) and 0) or num_str.epsilon
else real, epsilon = p.dumath.toDualNumberPart(num_str)end
if real == nil or epsilon == nil then return nil end
return p.dumath.getDualNumber(real, epsilon)
end,
toDualNumberPart = function(num_str)
local body = ''
local real, epsilon = 0, 0
local split_str = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(
mw.ustring.gsub(num_str or '','(%d)%s*%*%s*([ε])','%1%2'),
'%s+',''),'%++([%d%.])',',+%1'),'%++([ε])',',+1%1'),'%-+([%d%.])',',-%1'),'%-+([ε])',',-1%1'),'%*+([%d%.])',',*%1'),'%*+([ε])',',*1%1'),'%/+([%d%.])',',/%1'),'%/+([ε])',',/1%1'),',')
local first = true
local continue = false
for k,v in pairs(split_str) do
continue = false
local val = mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.ustring.gsub(mw.text.trim(v),'^(%.)','0%1'),'^([%d%.])','+%1'),'([%+%-])([%d%.])','%1\48%2'),'^([ijkl]+)','+1%1')
if mw.ustring.find(val,"%/") or mw.ustring.find(val,"%*") then return end
if val == nil or val == '' then if first == true then first = false continue = true else return end end
if not continue then
local num_text = mw.ustring.match(val,"[%+%-][%d%.]+[ε]*")
if num_text ~= val then return end
local num_part = tonumber(mw.ustring.match(num_text,"[%+%-][%d%.]+"))
if num_part == nil then return end
local f_start, f_end = mw.ustring.find(num_text,"[ε]+")
local part_str = ''
if f_start then part_str = mw.ustring.sub(num_text, f_start, f_end) end
if part_str == "" then real = real + num_part -- +1.0
elseif part_str == "ε" then epsilon = epsilon + num_part -- +ε
end
end
end
return real, epsilon
end,
init = function()
p.dumath.e = p.dumath.getDualNumber(math.exp(1), 0)
p.dumath.pi = p.dumath.getDualNumber(math.pi, 0)
p.dumath["π"] = p.dumath.getDualNumber(math.pi, 0)
p.dumath["°"] = p.dumath.getDualNumber(math.pi/180, 0)
p.dumath.nan = p.dumath.getDualNumber(tonumber("nan"), tonumber("nan"))
p.dumath.zero = p.dumath.getDualNumber(0, 0)
p.dumath.one = p.dumath.getDualNumber(1, 0)
p.dumath[-1] = p.dumath.getDualNumber(-1, 0)
p.dumath["ε"] = p.dumath.getDualNumber(0, 1)
p.dumath.epsilon = p.dumath.getDualNumber(0, 1)
p.dumath[0],p.dumath[1] = p.dumath.zero,p.dumath.one
p.dumath.numberType = sollib._numberType
p.dumath.constructor = p.dumath.toDualNumber
p.dumath.elements = {
p.dumath.getDualNumber(1, 0),
p.dumath.getDualNumber(0, 1)
}
return p.dumath
end
}
return p