大數相乘



local bignum = {}


function bignum.len(num)
    return string.len(num)
end


function bignum.splitAt(num, pos)
    local low = string.sub(num, -pos)
    local high = string.sub(num, 1, -pos - 1)
    if high == '' then high = '0' end
    return high, low
end


function bignum.plus(str1, str2)
    local increase = 0
    local t = {}
    local max = math.max(string.len(str1), string.len(str2))
    for i = 0, max-1 do
        local ch1 = string.sub(str1, -1 - i, -1 - i)
        local ch2 = string.sub(str2, -1-i, -1-i)
        if ch1 == "" then ch1 = "0" end
        if ch2 == "" then ch2 = "0" end
        local newv = increase + ch1 + ch2
        increase = (newv - newv % 10) / 10
        table.insert(t, newv%10)
    end
    if increase > 0 then
        table.insert(t, increase)
    end
    local len = #t
    local tmp
    for i = 1, len/2 do
        tmp = t[i]
        t[i] = t[len-i+1]
        t[len-i+1] = tmp
    end
    return table.concat(t)
end


function bignum.plusEx(...)
    local t = {...}
    local result = t[1]
    for key=2, #t do
        result = bignum.plus(result, t[key])
    end
    return result
end




function bignum.multi(num1, num2)
    local increase = 0
    local t = {}
    local len1, len2 = string.len(num1),  string.len(num2)
    local min = len1 > len2 and len2 or len1
    local minstr = min == len1 and num1 or num2
    local maxstr = minstr == num1 and num2 or num1


    for i = 0, min-1 do
        local val = string.sub(minstr, -1 - i,  -1 - i)
        val = tonumber(val)
        local tb = {}
        for j = 1, i do
            table.insert(tb, 0)
        end
        increase = 0
        for k = 0, string.len(maxstr)-1 do
            local v = string.sub(maxstr, -1-k, -1-k)
            local m = val * v + increase
            local rem = m % 10
            increase = (m - rem) / 10
            table.insert(tb, rem)
        end
        if increase > 0 then
            table.insert(tb, increase)
        end
        table.insert(t, tb)
    end
    
    local tb2reverseStr = function(tbl)
        local len = #tbl
        local tmp
        for i = 1, len/2 do
            tmp = tbl[i]
            tbl[i] = tbl[len-i+1]
            tbl[len-i+1] = tmp
        end
        return table.concat(tbl)
    end
    for k, v in ipairs(t) do
        repeat
            if #v == 1 or v[#v] ~= 0 then
                break
            end
            table.remove(v)
        until #v == 1
        t[k] = tb2reverseStr(v)
    end
    return bignum.plusEx(unpack(t))
end


function bignum.minus(m1, m2)
    local borrow = 0
    local t = {}
    local max = math.max(string.len(m1), string.len(m2))
    for i = 0, max-1 do
        local ch1 = string.sub(m1, -1 -i, -1-i)
        local ch2 = string.sub(m2, -1-i, -1-i)
        if ch2 == "" then ch2 = '0' end
        if ch1 - ch2 - borrow >= 0 then
            table.insert(t, ch1-ch2-borrow)
            borrow = 0
        else
            table.insert(t, ch1+10 - ch2 - borrow)
            borrow = 1
        end
    end
    repeat
        if #t == 0 or t[#t] ~= 0 then
            break
        end
        table.remove(t)
    until #t == 0
    local len = #t
    local tmp
    for i = 1, len/2 do
        tmp = t[i]
        t[i] = t[len-i+1]
        t[len-i+1] = tmp
    end
    return table.concat(t)
end


function bignum.pow(base, val)
    local str = {1}
    for i = 1, val do
        table.insert(str, 0)
    end
    return table.concat(str)
end


function bignum.karatsuba(num1, num2)
    local l1 = bignum.len(num1)
    local l2 = bignum.len(num2)
    if l1 < 10 or l2 < 10 then
        return bignum.multi(num1, num2)
    end
    local m = math.max(l1, l2)
    local m2 = math.floor(m/2)
    local high1, low1 = bignum.splitAt(num1, m2)
    local high2, low2 = bignum.splitAt(num2, m2)
    local z0 = bignum.karatsuba(low1, low2)
    local z1 = bignum.karatsuba(bignum.plus(high1, low1), bignum.plus(high2, low2))
    local z2 = bignum.karatsuba(high1, high2)
    local result = bignum.plusEx(bignum.multi(z2, bignum.pow(10, 2*m2)), bignum.multi(bignum.minus(bignum.minus(z1, z2), z0), bignum.pow(10, m2)), z0)
    return result
end


return bignum
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章