以乘法豎式為例,如果我們將一次十進位一位乘法(即99乘法表的乘法)作為一個步驟,那麼兩個n位乘數相乘需要n的二次方個步驟,其時間複雜度就是O(n
2) ,但是如果我們採用某種「巧算」,那麼計算步驟將會大大減少。小學,中學老師教的各種「巧算」技巧,其宗旨都是減少計算量。我們已經承蒙了12年有餘的教誨,現在讓我們進入計算機世界。計算機乘法和我們用豎式計算乘法沒有本質區別。看看加法器,乘法器的門電路就知道了。門電路不是我們要關注的層次,門電路實在是太快了,快到你幾乎無法感知它計算2×3和24890125×98723988的差別。機器是瞬間得到結果的。人背下下來了99乘法表,所以人只能一位一位的計算乘法,但計算機不,計算機依靠自身的硬體門電路可以輕而易舉計算出其內建數據類型乘法,64位的CPU可以輕易計算 0xFFFFFFFFFFFFFFFF 範圍內的任意乘法,就好像我們人類計算99乘法表的乘法一樣(我們早就把這個99乘法表背下來了,深刻在了我們的大腦硬體乘法器裡)。然而,超過計算機內建類型範圍,計算機便無能為力了。32位計算機最多只能處理32位的數字,64位計算機自然只能處理64位數字,計算機處理超過內建數據類型範圍的數字計算的過程稱為 「大數計算」 。以64位為例,當計算機面對超過64位的數字乘法時,就好像我們人類面對超過一位數的乘法一樣,無法 「一下子」 得到結果,必須需要某種步驟來計算結果。這就是說,需要某種算法來進行生成一系列的計算步驟,而 步驟的多少決定了算法的好壞。23567971209865125789034451795247×12345678909988776655443314719047=?我們當然希望設計一種巧算的步驟,但在此之前,我們先設計一種 按部就班的算法,類似我們手算豎式一樣:人就是這麼算的,老老實實地按照十進位99乘法表,一個數字一個數字地進行計算,計算過程中處理進位。手工算豎式人人都會,說這些也無益,上周三下班的班車上,順手擼了一個代碼,感覺還好,發了個朋友圈就想分享出來,本周就休息一天,趕早起來就寫下了這篇文章。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void inline carry_add(char *tmp, char num, int index)
{
char tmp_num = 0;
char carry;
tmp_num = tmp[index] + num;
if (tmp_num > 9) {
carry = tmp_num / 10;
tmp[index] = tmp_num%10;
carry_add(tmp, carry, index-1);
} else {
tmp[index] = tmp_num;
}
}
int mul(char *mul1, char *mul2, char *result)
{
int i, j, mid;
int len1, len2, len, pos = 0;
char *tmp;
len1 = strlen(mul1);
len2 = strlen(mul2);
len = len1 + len2;
tmp = (char *)calloc(len, 1);
for (i = 0; i < len2; i++) {
for (j = 0; j < len1; j++) {
int idx = len - j - i - 1;
mid = (mul2[len2 - i - 1] - '0') * (mul1[len1 - j - 1] - '0');
carry_add(tmp, mid, idx);
}
}
i = 0;
while(tmp[i++] == 0) pos++;
len = len - pos;
memcpy(result, tmp + pos, len);
free (tmp);
for (i = 0; i < len; i++) {
result[i] += '0';
}
return 0;
}
int main(int argc, char **argv)
{
int len1, len2, i, count;
char *m1, *m2, *result;
m1 = argv[1];
m2 = argv[2];
count = atoi(argv[3]);
len1 = strlen(m1);
len2 = strlen(m2);
result = calloc(len1 + len2, 1);
for (i = 0; i < count; i++) {
memset(result, 0, len1 + len2);
mul(m1, m2, result);
}
printf("%s\n", result);
free(result);
return 0;
}
[root@localhost ]
290962605116854555936789385617202938185315195749798588574969609
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void inline carry_add(char *tmp, char num, int index)
{
char tmp_num = 0;
char carry;
tmp_num = tmp[index] + num;
if (tmp_num > 9) {
carry = tmp_num / 10;
tmp[index] = tmp_num%10;
carry_add(tmp, carry, index-1);
} else {
tmp[index] = tmp_num;
}
}
int add(char *s1, int len1, char *s2, int len2, char *result, int *ppos)
{
int i = 0, j = 0, len;
char *c;
len = len1;
if (len2 > len)
len = len2;
for (i = len - 1; i >= 0; i--) {
unsigned char tmp;
if (len1 > len2) {
tmp = s1[i] - '0';
if (i > len1 - len2 - 1)
tmp += s2[i - (len1 - len2)] - '0';
} else {
tmp = s2[i] - '0';
if (i > len2 - len1 - 1)
tmp += s1[i - (len2 - len1)] - '0';
}
carry_add(result, tmp, i + 1);
}
*ppos = 1;
if (result[0] != 0) {
len = len + 1;
*ppos = 0;
}
for (i = 0; i < len + 1; i++) {
result[i] += '0';
}
return len;
}
int zone_mul(char *mul1, char *mul2, int len, char *result, int result_len)
{
int i, j, n = 0, reslen, totlen, pow1size, pow2size, pos = 0, nblocks = len / 8;
unsigned long m1, m2, tmp_res;
char str1[10], str2[10], resstr[20];
char *pow1, *pow2, *tmp_result;
tmp_result = calloc(result_len, 1);
pow1 = calloc(len*2, 1);
pow2 = calloc(len*2, 1);
for (i = 0; i < nblocks; i++) {
memcpy(str1, mul1 + len - i*8 - 8, 8);
m1 = atoi(str1);
for (j = 0; j < nblocks; j++) {
memcpy(str2, mul2 + len - j*8 - 8, 8);
m2 = atoi(str2);
tmp_res = m1*m2;
pow1size = i*8;
pow2size = j*8;
totlen = reslen = sprintf(resstr, "%lu", tmp_res);
totlen += pow2size;
memset(pow2, '0', totlen);
memcpy(pow2, resstr, reslen);
reslen = totlen;
totlen += pow1size;
memset(pow1, '0', totlen);
memcpy(pow1, pow2, reslen);
memset(result, 0, n + pos);
n = add(pow1, totlen, tmp_result, n, result, &pos);
memcpy(tmp_result, result + pos, n);
}
}
memset(result, 0, n + pos);
memcpy(result, tmp_result, n);
free(tmp_result);
free(pow1);
free(pow2);
}
int main(int argc, char **argv)
{
int len1, len2, i = 0, count;
char *m1, *m2, *result;
m1 = argv[1];
m2 = argv[2];
count = atoi(argv[3]);
len1 = strlen(m1);
len2 = strlen(m2);
result = calloc(len1 + len2, 1);
for (i = 0; i < count; i++) {
memset(result, 0, len1 + len2);
zone_mul(m1, m2, len1, result, len1 + len2);
}
printf("%s\n", result);
free(result);
return 0;
}
[root@localhost ]# time ./mul 119334567890334449388883313579158334567098134455 667908995633221198765432134678040000123411113456 50000
79704631383957730438879843848804741889926116047138197998269353980447530720116354515911947726480
real 0m1.891s
user 0m1.889s
sys 0m0.001s
[root@localhost ]# time ./mul2 119334567890334449388883313579158334567098134455 667908995633221198765432134678040000123411113456 50000
79704631383957730438879843848804741889926116047138197998269353980447530720116354515912427726480
real 0m1.475s
user 0m1.472s
sys 0m0.001s
[root@localhost ]#
熱 文 推 薦
☞國際頂級學界和工業界大咖雲集、AIoT 實訓營,你不可錯過的嵌入式 AI 盛會!
☞2019 年度最佳開源軟體出爐,TensorFlow 上榜!
☞【只有光頭才能變強,文末有xx】分享一波Lambda表達式
☞如何解決多機房、多網絡下的物聯網部署方案?
☞從4個維度深度剖析閃電網絡現狀,在CKB上實現閃電網絡的理由 | 博文精選
☞太雞凍了!我用Python偷偷查到暗戀女生的名字
☞一文了解超級帳本DLT、庫、開發工具有哪些, Hyperledger家族成員你認識幾個?
點擊閱讀原文,查看博主原文。