Lua大整数练习

—思路
—用一个列表保存数字值
—符号: 用一个字段表示正负符号
—进制: 目前只考虑支持10进制, 不考虑2/8/16进制等
—精确度: 不考虑小数点, 向下取整截取
—压位最大值: 理论上操作系统(64\32位)与Lua版本可能影响最大值;
— 如果要计算最大值,只要使用循环一直把数字增加到溢出,溢出前的值就是最大值;
— 在新的版本上会有math.maxinteger字段表示最大值;
— 我们暂且认为10^8是最大的整数值吧;(因为太长会被用e显示, 不方便查看);也就是说列表值最大为7个9

---思路
---用一个列表保存数字值
---符号: 用一个字段表示正负符号
---进制: 目前只考虑支持10进制, 不考虑2/8/16进制等
---精确度: 不考虑小数点, 向下取整截取
---压位最大值: 理论上操作系统(64\32位)与Lua版本可能影响最大值;
---      如果要计算最大值,只要使用循环一直把数字增加到溢出,溢出前的值就是最大值;
---      在新的版本上会有math.maxinteger字段表示最大值;
---      我们暂且认为10^8是最大的整数值吧;(因为太长会被用e显示, 不方便查看);也就是说列表值最大为7个9

module(..., package.seeall)
local math = math -- 引入为local后,下文的使用效率更高

SIGN_MINUS = "+" -- 正数
SIGN_PLUS = "-" -- 负数
SIGN_ZERO = "" -- 0

NUM_MAX = math.pow(10,8) - 1
NUM_MIN = -NUM_MAX
NUM_MAX_LEN = #tostring(NUM_MAX)

local function add_digits(d1, d2)
    -- 两个列表相加
    local len;
    local up = 0
    local v1, v2;
    local digits_sum_temp;
    local digits = {}
    if #d1 > #d2 then
        len = #d1
    else
        len = #d2
    end
    for i=1, len do
        v1 = d1[i]
        v2 = d2[i]
        if v1 == nil then v1 = 0 end
        if v2 == nil then v2 = 0 end
        digits_sum_temp = v1 + v2 + up
        if digits_sum_temp > NUM_MAX then
            up = 1
            digits_sum_temp = digits_sum_temp - NUM_MAX - 1
        else
            up = 0
        end
        digits[i] = digits_sum_temp
    end
    if up == 1 then
        digits[#digits + 1] = 1
    end
    return digits
end

local function sub_digits(d1, d2)
    -- 两个列表相减
    -- 如果d1 - d2 大于等于0 会返回新的列表
    -- 否则会返回false
    local len
    local v1, v2
    local digits_sum_temp
    local digits = {}
    local flag = false -- 去掉0

    if #d1 < #d2 then
        return false
    end
    len = #d1
    for i=1, len do
        k = len - i + 1
        v1 = d1[k]
        v2 = d2[k]
        if v1 == nil then v1 = 0 end
        if v2 == nil then v2 = 0 end
        digits_sum_temp = v1 - v2
        if digits_sum_temp < 0 then
            return false
        end
        if digits_sum_temp > 0 then
            flag = true
        end
        if flag then
            digits[k] = digits_sum_temp
        end
    end
    return digits
end

local function is_digits_zero(d)
    -- 列表值为0
    return d and type(d) == "table" and #d == 1 and d[1] == 0
end

local function is_digits_lt(d1, d2)
    -- 比较列表 小于
    return sub_digits(d1, d2) == false
end

local function is_digits_eq(d1, d2)
    -- 比较列表大小 =
    local d = sub_digits(d2, d1)
    return is_digits_zero(d)
end

local CBigInt = {isBigInt=true}

CBigInt.__tostring = function(a)
    if #a._digits == 0 then
        return "0"
    end
    local res
    if a._sign == SIGN_PLUS then
        res = tostring(a._sign)
    else
        res = ""
    end
    local str_format = string.format("%%.%sd", NUM_MAX_LEN) -- 补0
    for i=1, #a._digits do
        k = #a._digits - i + 1
        v = a._digits[k]
        if k ~= #a._digits then
            v = string.format(str_format, tonumber(v))
        else
            v = tostring(v)
        end
        res = res..v
    end
    return res
end

CBigInt.__add = function(a, b)
    a, b = CBigInt.getBigInt(a), CBigInt.getBigInt(b)

    local sum = CBigInt:create(0);
    if b._sign == SIGN_ZERO then
        sum = a
    elseif a._sign == SIGN_ZERO then
        sum = b
    elseif a._sign == SIGN_MINUS and b._sign == SIGN_MINUS then
        -- sum = a + b
        sum._sign = SIGN_MINUS
        sum._digits = add_digits(a._digits, b._digits) -- 其实有浪费计算
    elseif a._sign == SIGN_MINUS and b._sign == SIGN_PLUS then
        -- sum = a - b
        if is_digits_lt(a._digits, b._digits) then
            sum._sign = SIGN_PLUS
            sum._digits = sub_digits(b._digits, a._digits)
        elseif is_digits_eq(a._digits, b._digits) then
            sum._sign = SIGN_ZERO
            sum._digits = {[1] = 0}
        else
            sum._sign = SIGN_MINUS
            sum._digits = sub_digits(a._digits, b._digits)
        end
    elseif a._sign == SIGN_PLUS and b._sign == SIGN_MINUS then
        -- sum = b - a
        if is_digits_lt(b._digits, a._digits) then
            sum._sign = SIGN_PLUS
            sum._digits = sub_digits(a._digits, b._digits)
        elseif is_digits_eq(a._digits, b._digits) then
            sum._sign = SIGN_ZERO
            sum._digits = {[1] = 0}
        else
            sum._sign = SIGN_MINUS
            sum._digits = sub_digits(b._digits, a._digits)
        end
    else
        -- sum = -(a + b)
        sum._sign = SIGN_PLUS
        sum._digits = add_digits(a._digits, b._digits)
    end
    return sum
end

CBigInt.__sub = function(a, b)
    a, b = CBigInt.getBigInt(a), CBigInt.getBigInt(b)
    local res = CBigInt:create(0)
    if a._sign == SIGN_ZERO then
        res = CBigInt.copy(CBigInt.reverse_sign(b))
    elseif b._sign == SIGN_ZERO then
        res = CBigInt.copy(a)
    elseif a._sign == SIGN_MINUS and b._sign == SIGN_MINUS then
        if is_digits_eq(a._digits, b._digits) then
        elseif is_digits_lt(a._digits, b._digits) then
            res._sign = SIGN_PLUS
            res._digits = sub_digits(b._digits, a._digits)
        else
            res._sign = SIGN_MINUS
            res._digits = sub_digits(a._digits, b._digits)
        end
    elseif a._sign == SIGN_MINUS and b._sign == SIGN_PLUS then
        res._sign = SIGN_MINUS
        res._digits = add_digits(a._digits, b._digits)
    elseif a._sign == SIGN_PLUS and b._sign == SIGN_MINUS then
        res._sign = SIGN_PLUS
        res._digits = add_digits(a._digits, b._digits)
    else
        if is_digits_eq(a._digits, b._digits) then
        elseif is_digits_lt(a._digits, b._digits) then
            res._sign = SIGN_MINUS
            res._digits = sub_digits(b._digits, a._digits)
        else
            res._sign = SIGN_PLUS
            res._digits = sub_digits(a._digits, b._digits)
        end
    end
    return res
end

--CBigInt.__mul = function(a, b)
--    return a * b
--end

--CBigInt.__div = function(a, b)
--    return a / b
--end

--CBigInt.__len = function(a)
--    local s = CBigInt.__tostring(a)
--    return #s
--end

CBigInt.__eq = function(a, b)
    a, b = CBigInt.getBigInt(a), CBigInt.getBigInt(b)
    return a._sign == b._sign and is_digits_eq(a._digits, b._digits)
end

CBigInt.__lt = function(a, b)
    -- 小于
    a, b = CBigInt.getBigInt(a), CBigInt.getBigInt(b)
    if a._sign == SIGN_MINUS then
        if b._sign == SIGN_MINUS then
            return is_digits_lt(a._digits, b._digits)
        elseif b._sign == SIGN_PLUS then
            return false
        else
            return false
        end
    elseif a._sign == SIGN_PLUS then
        if b._sign == SIGN_MINUS then
            return true
        elseif b._sign == SIGN_PLUS then
            return is_digits_lt(b._digits, a._digits)
        else
            return true
        end
    else
        if b._sign == SIGN_MINUS then
            return true
        elseif b._sign == SIGN_PLUS then
            return false
        else
            return false
        end
    end
end

CBigInt.__le = function(a, b)
    -- 小于等于
    return CBigInt.__lt(a, b) or CBigInt.__eq(a, b)
end

function CBigInt:_new(o)
    o = o or {}
    o._sign = SIGN_ZERO
    o._digits = {[1] = 0}
    self.__index = self
    setmetatable(o, self)
    return o
end

function CBigInt:_set(n)
    if n == nil then
        print("请输入有意义数字")
        return
    end
    if type(n) == "number" then
        return self:_set_number(n)
    elseif type(n) == "string" then
        return self:_set_string(n)
    else
        print(string.format("暂时不支持%s类型参数%s", type(n), tostring(n)))
    end
end

function CBigInt:_set_number(n)
    n = math.floor(n)
    if n >= NUM_MIN and n <= NUM_MAX then
        if n == 0 then
            self._sign = SIGN_ZERO
        elseif n < 0 then
            n = -n
            self._sign = SIGN_PLUS
        else
            self._sign = SIGN_MINUS
        end
        self._digits = {[1] = n}
        return true
    else
        return self:_set_string(n)
    end
end

function CBigInt:_set_string(n)
    n = tostring(n)
    n = n:gsub(" ", "")
    local tmp = n:gsub("-?+?%.?", "")
    if string.match(tmp, "%D") then
        print(string.format("请检查输入的值不合法, 不支持字母:%s", n))
        return
    end
    n = n:gsub("%.%d*", "")
    if n == "" then
        print(string.format("请检查输入的值不合法, 没有正值:%s", n))
        return
    end
    if n:sub(1, 1) == "-" then
        self._sign = SIGN_PLUS
        n = n:sub(2)
    elseif n:sub(1, 1) == "+" then
        self._sign = SIGN_MINUS
        n = n:sub(2)
    else
        self._sign = SIGN_MINUS
    end

    n = n:gsub("^0*", "")
    if n == "" then
        n = "0"
    end
    if n == "0" then
        self._digits[1] = 0
        self._sign = SIGN_ZERO
        return true
    end

    local str;
    local int;
    self._digits = {}
    for index=1, #n, NUM_MAX_LEN do
        str = n:sub(-index-NUM_MAX_LEN+1, -index)
        int = tonumber(str)
        self._digits[#self._digits+1] = int
    end
    return true
end

function CBigInt.copy(a)
    local b = CBigInt:create(0)
    b._sign = a._sign
    b._digits = {}
    for i=1, #a._digits do
        b._digits[i] = a._digits[i]
    end
    return b
end

function CBigInt.reverse_sign(a)
    -- 正负符号反转
    a = CBigInt.copy(a)
    if a._sign == SIGN_MINUS then
        a._sign = SIGN_PLUS
    elseif a._sign == SIGN_PLUS then
        a._sign = SIGN_MINUS
    end
    return a
end

function CBigInt.isBigInt(n)
    return type(n) == "table" and n.isBigInt
end

function CBigInt.getBigInt(n)
    if CBigInt.isBigInt(n) then
        return CBigInt.copy(n)
    else
        return CBigInt:create(n)
    end
end

function CBigInt:create(n)
    local o = self:_new()
    o:_set(n)
    return o
end


--local a = "1"
--local b = ""
--for i=1, 10000 do
--    a = a.."1"
--    b = b.."2"
--end
--
--a = CBigInt:create(a)
--b = CBigInt:create(b)
--print(a + b)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值