看了就會(huì)的大整數(shù)乘法運(yùn)算與分治算法

在數(shù)據(jù)加密處理中有很多復(fù)雜的加密算法稽莉,這些加密算法往往會(huì)用到很多超大的整數(shù)運(yùn)算课舍。不過(guò)霹俺,程序設(shè)計(jì)語(yǔ)言對(duì)數(shù)據(jù)的大小會(huì)有一定的限制敬特,數(shù)據(jù)太大就會(huì)出現(xiàn)數(shù)據(jù)溢出的情況掰邢,這是無(wú)法進(jìn)行大整型數(shù)據(jù)運(yùn)算的。本文將和大家一起學(xué)習(xí)如何實(shí)現(xiàn)大整數(shù)的數(shù)據(jù)運(yùn)算伟阔,本文代碼我們使用C++實(shí)現(xiàn)辣之。

普通乘數(shù)運(yùn)算

對(duì)于乘數(shù)運(yùn)算有一種比較簡(jiǎn)單較為容易理解的方法,我們可以利用小學(xué)時(shí)期學(xué)的列豎式的計(jì)算方法進(jìn)行乘法運(yùn)算皱炉。

列豎式

參考上圖中的列豎式計(jì)算方法怀估,我們進(jìn)行代碼實(shí)現(xiàn)。

#include <iostream>
#include <string>
#include <stdlib.h>
#include <vector>
#include <cstring>
#include <algorithm>

std::string multiply(std::string a, std::string b)
{
    std::string result = "";
    int row = b.size();
    int col = a.size() + 1;
    int tmp[row][col];
    memset(tmp,0, sizeof(int)*row*col);
    
    reverse(a.begin(),a.end());
    reverse(b.begin(),b.end());
    
    for(int i = 0; i < b.size(); i++)
    {
        for(int j = 0; j < a.size(); j++)
        {
            std::string bit_a = std::string(1, a.at(j));
            std::string bit_b = std::string(1, b.at(i));
            
            tmp[i][j] += std::stoi(bit_a) * std::stoi(bit_b);
        
            tmp[i][j+1] = tmp[i][j] / 10;
            tmp[i][j] %= 10;

        }

    }

    int N =  a.size() + b.size();
    int sum[N];
    memset(sum, 0, sizeof(int)*N);
    
    for(int n = 0; n < N; n++)
    {
        int i = 0;
        int j = n;
        
        while (i <= n && j >= 0 )
        {
            if(i < row && j < col)
            {
                sum[n] += tmp[i][j];
            }
            
            i++;
            j--;
        }

        if( n+1 < N )
        {
            sum[n+1] = sum[n] / 10;
            sum[n] %= 10;
        }

    }

    bool zeroStartFlag = true;
    for (int i = N-1; i >= 0; i--)
    {
        if(sum[i]==0 && zeroStartFlag)
        {
            continue;
        }
        
        zeroStartFlag = false;
        result.append(std::to_string(sum[i]));
    }
    
    return result;
}


int main()
{
    std::string a = "3456";
    std::string b = "1234";

    std::string result = multiply(a, b);    
    std::cout << a << " * " << b << " = " << result <<std::endl;
    
    return 0;
}

為了方便我們先將各個(gè)乘數(shù)做了翻轉(zhuǎn)處理合搅,最后需要再將結(jié)果翻轉(zhuǎn)回來(lái)多搀。在運(yùn)算過(guò)程中用來(lái)存放乘法運(yùn)算結(jié)果的數(shù)組,我們沒(méi)有進(jìn)行移位處理同列相加灾部,而是對(duì)角線相加康铭,這樣可以減少空間和運(yùn)算步驟。上面的代碼運(yùn)行結(jié)果如下所示梳猪。

image

因?yàn)樽址拈L(zhǎng)度沒(méi)有特別的限制麻削,所以上面的算法可以適用大整數(shù)運(yùn)算。

分治算法

雖然上面的列豎式的方法可以很好的解決大整數(shù)乘法的問(wèn)題春弥,但是我們還用一種更加高效的方法可以選擇,這就是分治(Divide and Conquer)算法叠荠。它是一種非常重要的算法匿沛,可以應(yīng)用到很多領(lǐng)域,比如快速排序榛鼎,二分搜索等逃呼。從算法的名字我們可以看出它是將大的問(wèn)題拆分進(jìn)行細(xì)化,由大變小者娱,先將小的問(wèn)題解決抡笼,最終將這個(gè)問(wèn)題解決,所以叫Divide and Conquer黄鳍。在這里我們可以通過(guò)這種方法將大整數(shù)進(jìn)行拆分推姻,拆分成一個(gè)個(gè)可以通過(guò)程序語(yǔ)言直接進(jìn)行運(yùn)算的小整進(jìn)行計(jì)算,最后求得到大整數(shù)的值框沟。

假設(shè)有兩個(gè)大整數(shù)藏古,我們?cè)O(shè)為a(大小為n位)和b(大小為m位)增炭,這里我們將使用二分法對(duì)數(shù)據(jù)進(jìn)行拆分,這兩個(gè)整數(shù)我們可以分解為:

a = a_1 * 10^{n/2} + a_0
b = b_1 * 10^{m/2} + b_0

則,

a*b = (a_1 10^{n/2} + a_0) * (b_1 10^{m/2} + b_0)
= a_1b_1 10^{n/2 + m/2} + a_1b_0 10^{n/2} + a_0b_1 10^{m/2} + a_0b_0

令拧晕,
z_3 = a_1b_1
z_2 = a_1b_0
z_1 = a_0b_1
z_0 = a_0b_0

根據(jù)上面公式里隙姿,我們可以將a*b分解為四個(gè)小的整數(shù)的乘法,即z3,z2,z1,z0四個(gè)表達(dá)式厂捞。如果分解出來(lái)的乘法數(shù)值還是很大输玷,還可以按照同樣的方法進(jìn)行拆解直到拆解成較小的數(shù)值乘法,直到計(jì)算機(jī)程序語(yǔ)言可以直接運(yùn)算靡馁。

比如饲嗽,上面的3456和1234相乘,參考下圖通過(guò)二分法逐級(jí)對(duì)整數(shù)進(jìn)行拆分奈嘿,我們將兩個(gè)整數(shù)拆分到一位整數(shù)進(jìn)行運(yùn)算貌虾。

image

下面我們看一下分治算法的代碼實(shí)現(xiàn),這里我們使用遞歸的方法裙犹。

#include <iostream>
#include <string>
#include <stdlib.h>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cmath>

std::string add(std::string a, std::string b)
{
    int N = std::max(a.size(), b.size());
    int sum[N];
    memset(sum, 0, sizeof(int)*N);
    
    reverse(a.begin(),a.end());
    reverse(b.begin(),b.end());

    for(int i = 0; i< N; i++)
    {
        int bit_a = 0;
        int bit_b = 0;
        if(i < a.size()) bit_a = std::stoi(std::string(1, a.at(i)));
        if(i < b.size()) bit_b = std::stoi(std::string(1, b.at(i)));

        sum[i] += (bit_a + bit_b);

        if(i < N-1 && sum[i]>9)
        {
            sum[i+1] = sum[i] / 10;
            sum[i] %=10;
        }
    }

    std::string result="";
    bool zeroStartFlag = true;
    for (int i = N-1; i >= 0; i--)
    {
        if(sum[i]==0 && zeroStartFlag)
        {
            continue;
        }
        
        zeroStartFlag = false;
        result.append(std::to_string(sum[i]));
    }


    return result;
}

std::string divideAndConquer(std::string a, std::string b)
{
    if( a.size() < 2 && b.size() < 2) 
    {
        return std::to_string(std::stoi(a) * std::stoi(b));
    }
    
    int n = a.size();
    int m = b.size();
    
    int halfN = n/2;
    int halfM = m/2;

    std::string a0 = "0";
    std::string a1 = "0";
    if(a.size() > halfN && halfN > 0)
    {
        a1 = a.substr(0, halfN);
        a0 = a.substr(halfN, a.size() - halfN);
    }
    else
    {
        a1 = "0";
        a0 = a;
    }
    
    std::string b0 = "0";
    std::string b1 = "0";
    if(b.size() > halfM && halfM > 0)
    {
        b1 = b.substr(0, halfM);
        b0 = b.substr(halfM, b.size() - halfM);

    }
    else
    {
        b1 = "0";
        b0 = b;
    }

    std::string a1b1 = divideAndConquer(a1, b1);
    std::string a0b0 = divideAndConquer(a0, b0);
    std::string a1b0 = divideAndConquer(a1, b0);
    std::string a0b1 = divideAndConquer(a0, b1);
    
    a1b1.append((n - halfN) + (m - halfM), '0');
    a1b0.append(n - halfN, '0');
    a0b1.append(m - halfM, '0');

    std::string result = "";
    result = add(a1b1, a1b0);
    result = add(result, a0b1);
    result = add(result, a0b0);

    return result;
}

int main()
{
    std::string a = "3456";
    std::string b = "1234";

    std::cout << a << " * " << b << " = " << divideAndConquer(a, b) << std::endl; 

    return 0;
}


程序的運(yùn)行結(jié)果如下:

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尽狠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子叶圃,更是在濱河造成了極大的恐慌袄膏,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掺冠,死亡現(xiàn)場(chǎng)離奇詭異沉馆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)德崭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門斥黑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人眉厨,你說(shuō)我怎么就攤上這事锌奴。” “怎么了憾股?”我有些...
    開(kāi)封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵鹿蜀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我服球,道長(zhǎng)茴恰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任斩熊,我火速辦了婚禮往枣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己婉商,他們只是感情好似忧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著丈秩,像睡著了一般盯捌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蘑秽,一...
    開(kāi)封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天饺著,我揣著相機(jī)與錄音,去河邊找鬼肠牲。 笑死幼衰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缀雳。 我是一名探鬼主播渡嚣,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肥印!你這毒婦竟也來(lái)了识椰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤深碱,失蹤者是張志新(化名)和其女友劉穎腹鹉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敷硅,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡功咒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绞蹦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片力奋。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坦辟,靈堂內(nèi)的尸體忽然破棺而出刊侯,到底是詐尸還是另有隱情,我是刑警寧澤锉走,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站藕届,受9級(jí)特大地震影響挪蹭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜休偶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一梁厉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦词顾、人聲如沸八秃。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昔驱。三九已至,卻和暖如春上忍,著一層夾襖步出監(jiān)牢的瞬間骤肛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工窍蓝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腋颠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓吓笙,卻偏偏與公主長(zhǎng)得像淑玫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子面睛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容