詳解C/C++高精度(加減乘除)算法中的壓位優(yōu)化
前言
由于上一章《C/C++ 高精度(加減乘除)算法簡(jiǎn)單實(shí)現(xiàn)》實(shí)現(xiàn)了基本的高精度計(jì)算,數(shù)組的每個(gè)元素存儲(chǔ)一位10進(jìn)制的數(shù)字。這樣的存儲(chǔ)方式并不是最優(yōu)的,32位的整型其實(shí)至少可以存儲(chǔ)9位高精度數(shù)字,數(shù)組元素存儲(chǔ)更多的位數(shù)就是壓位優(yōu)化。本文將展示壓位優(yōu)化的原理以及壓9位的實(shí)現(xiàn)和性能對(duì)比。
一、基本原理
1、存儲(chǔ)方式
壓位優(yōu)化就是將原本存儲(chǔ)一位的數(shù)組元素變成存儲(chǔ)多位,這樣就可以提升運(yùn)算效率,通常最高能存儲(chǔ)9位,如下圖示例為存儲(chǔ)4位。
2、計(jì)算方式
采用模擬立豎式計(jì)算,比如加法的計(jì)算流程,如下圖所示,20481024+80001000=100482024:
二、完整代碼
因?yàn)?strong>接口以及使用方法與上一章《C/C++ 高精度(加減乘除)算法簡(jiǎn)單實(shí)現(xiàn)》是完全一致的,所以這里直接展示完整代碼,省略使用示例。下面代碼為壓9位實(shí)現(xiàn)。
#include<stdio.h> #include<string.h> #include<math.h> #include<stdint.h> /// <summary> /// 通過(guò)字符串初始化 /// </summary> /// <param name="a">[in]高精度數(shù)組</param> /// <param name="value">[in]字符串首地址</param> static void loadStr(int* a, const char* value) { int len = strlen(value); int left = len % 9; char s[8], * p = (char*)value + left; a[0] = ceil(len / 9.0); len = len / 9.0; for (int i = 1; i <= len; i++) sscanf(p + (len - i ) * 9, "%09d", &a[i]); if (left){ sprintf(s, "%%0%dd", left); sscanf(value, s, &a[a[0]]); } } /// <summary> /// 輸出到字符串, /// </summary> /// <param name="a">[in]高精度數(shù)組</param> /// <param name="str">[out]字符串,由外部停供緩沖區(qū),需要保證長(zhǎng)度足夠</param> static void toStr(int* a, char* str) { if (!a[0]) { sprintf(str, "0"); return; } sprintf(str, "%d", a[a[0]]); str += strlen(str); for (int i = a[0]-1; i > 0; i--) sprintf(str +( a[0] -i-1)*9, "%09d", a[i]); str[(a[0]-1)*9] = '\0'; } /// <summary> /// 通過(guò)無(wú)符號(hào)整型初始化 /// </summary> /// <param name="a">[in]高精度數(shù)組</param> /// <param name="value">[in]整型值</param> static void loadInt(int* a, uint64_t value) { a[0] = 0; while (value)a[++a[0]] = value % 1000000000, value /= 1000000000; } /// <summary> /// 比較兩個(gè)高精度數(shù)的大小 /// </summary> /// <param name="a">[in]第一個(gè)數(shù)</param> /// <param name="b">[in]第二個(gè)數(shù)</param> /// <returns>1是a>b,0是a==b,-1是a<b</returns> static int compare(int* a, int* b) { if (a[0] > b[0])return 1; if (a[0] < b[0])return -1; for (int i = a[0]; i > 0; i--) if (a[i] > b[i])return 1; else if (a[i] < b[i])return -1; return 0; } /// <summary> /// 復(fù)制 /// </summary> /// <param name="a">[in]源</param> /// <param name="b">[in]目標(biāo)</param> static void copy(int* a, int* b) { memcpy(b, a, (a[0] + 1) * sizeof(int)); } /// <summary> /// 打印輸出結(jié)果 /// </summary> static void print(int* a) { int i = a[0]; printf("%d", a[i--]); for (; i > 0; i--)printf("%09d", a[i]); } /// <summary> /// 加 /// </summary> /// <param name="a">[in]被加數(shù)</param> /// <param name="b">[in]加數(shù)</param> /// <param name="c">[out]結(jié)果</param> static void plus(int* a, int* b, int* c) { int* p; if (a[0] < b[0])p = a, a = b, b = p;//確保a長(zhǎng)度最大 int i = 1, alen = a[0], blen = b[0]; c[0] = c[alen + 1] = 0; if (a != c)memcpy(c + blen + 1, a + blen + 1, (alen - blen) * sizeof(int));//a多出的部分直接拷貝到結(jié)果 for (; i <= blen; i++) { c[i] = a[i] + b[i]; if (c[i - 1] >= 1000000000)c[i - 1] -= 1000000000, c[i]++;//判斷上一位是否進(jìn)位 } i--; while (c[i++] >= 1000000000)c[i - 1] -= 1000000000, c[i]++;//繼續(xù)判斷進(jìn)位 c[0] = c[alen + 1] ? alen + 1 : alen;//記錄長(zhǎng)度 } /// <summary> /// 加等于 ///結(jié)果會(huì)保存在a中 /// </summary> /// <param name="a">[in]被加數(shù)</param> /// <param name="b">[in]加數(shù)</param> static void plusq(int* a, int* b) { plus(a, b, a); } /// <summary> /// 減 /// </summary> /// <param name="a">[in]被減數(shù),被減數(shù)必須大于等于減數(shù)</param> /// <param name="b">[in]減數(shù)</param> /// <param name="c">[out]結(jié)果</param> static void sub(int* a, int* b, int* c) { int i = 1, alen = a[0]; if (a != c)memcpy(c + b[0] + 1, a + b[0] + 1, (a[0] - b[0]) * sizeof(int));//a多出的部分直接拷貝到結(jié)果 c[0] = 1; for (; i <= b[0]; i++) { c[i] = a[i] - b[i]; if (c[i - 1] < 0)c[i - 1] += 1000000000, c[i] --;//判斷上一位是否補(bǔ)位 } i--; while (c[i++] < 0)c[i - 1] += 1000000000, c[i]--;//繼續(xù)判斷補(bǔ)位 while (!c[alen--]); c[0] = alen + 1;//記錄長(zhǎng)度 } /// <summary> /// 減法等于 ///結(jié)果會(huì)保存在a中 /// </summary> /// <param name="a">[in]被減數(shù),被減數(shù)必須大于等于減數(shù)</param> /// <param name="b">[in]減數(shù)</param> static void subq(int* a, int* b) { sub(a, b, a); } /// <summary> /// 乘 /// </summary> /// <param name="a">[in]被乘數(shù)</param> /// <param name="b">[in]乘數(shù)</param> /// <param name="c">[out]結(jié)果,數(shù)組長(zhǎng)度必須大于等于aLen+bLen+1</param> static void mul(int* a, int* b, int c[]) { int len = a[0] + b[0], d = 0; memset(c, 0, sizeof(int) * (len + 1)); b[b[0] + 1] = 0; c[0] = 1;//防止越界 for (int i = 1; i <= a[0]; i++) for (int j = 1; j <= b[0] + 1; j++){ int64_t t = (int64_t)a[i] * b[j] + c[j + i - 1] + d; c[j + i - 1] = t % 1000000000; d = t / 1000000000; } while (!c[len])len--; c[0] = len; } /// <summary> /// 乘等于 /// 累乘,結(jié)果存放于a /// </summary> /// <param name="a">[in]被乘數(shù),數(shù)組長(zhǎng)度必須大于等于2aLen+bLen+1</param> /// <param name="b">[in]乘數(shù)</param> static void mulq(int* a, int* b) { int* c = a + a[0] + b[0] + 1; memcpy(c, a, (a[0] + 1) * sizeof(int)); mul(c, b, a); } /// <summary> /// 除法 /// 依賴減法subq /// </summary> /// <param name="a">[in]被除數(shù),被除數(shù)必須大于除數(shù)</param> /// <param name="b">[in]除數(shù)</param> /// <param name="c">[out]商,數(shù)組長(zhǎng)度大于等于3aLen-bLen+1</param> /// <param name="mod">[out]余數(shù),可以為NULL,數(shù)組長(zhǎng)度大于等于aLen</param>> static void div(int* a, int* b, int* c, int* mod) { int len = a[0] - b[0] + 1, times, hTimes[32], * temp = c + a[0] + 1; if (!mod)mod = temp + 2*(a[0] + 1)+1;//緩沖區(qū) memcpy(mod, a, (a[0] + 1) * sizeof(int)); memset(c, 0, sizeof(int) * (len + 1)); memset(temp, 0, sizeof(int) * len); c[0] = 1;//防止while越界 for (int i = len; i > 0; i--) { memcpy(temp + i, b + 1, sizeof(int) * b[0]);//升階 temp[0] = b[0] + i - 1; while (compare(mod, temp) != -1) { if (times = (mod[mod[0]] * ((mod[0] - temp[0]) ? 1000000000ll : 1)) / (temp[temp[0]] + (temp[0] == 1 ? 0 : 1)))//升倍數(shù) { loadInt(hTimes,times); mulq(temp, hTimes); } else times = 1; while (compare(mod, temp) != -1)subq(mod, temp), c[i] += times; //減法 memcpy(temp + i, b + 1, sizeof(int) * b[0]);//還原 temp[0] = b[0] + i - 1; } } while (!c[len])len--; c[0] = len; } /// <summary> /// 除等于 /// 商保存在a /// 依賴div /// </summary> /// <param name="a">[in]被除數(shù),被除數(shù)必須大于除數(shù)</param> /// <param name="b">[in]除數(shù)</param> /// <param name="mod">[out]余數(shù),可以為NULL,數(shù)組長(zhǎng)度大于等于aLen</param>> static void divq(int* a, int* b, int* mod) { div(a, b, a, mod); }
三、性能對(duì)比
測(cè)試平臺(tái):Windows 11
測(cè)試設(shè)備:i7 8750h
測(cè)試方式:測(cè)試5次取均值
表1、測(cè)試用例
測(cè)試用例 | 描述 |
---|---|
1 | 整型范圍數(shù)字計(jì)算500000次 |
2 | 長(zhǎng)數(shù)字與整型范圍數(shù)字計(jì)算500000次 |
3 | 長(zhǎng)數(shù)字與長(zhǎng)數(shù)字計(jì)算500000次 |
基于上述用例編寫(xiě)程序進(jìn)行測(cè)試,測(cè)試結(jié)果如下表
表2、測(cè)試結(jié)果
計(jì)算 | 測(cè)試用例 | 1位實(shí)現(xiàn)(上一章)耗時(shí) | 9位優(yōu)化(本章)耗時(shí) |
---|---|---|---|
加法 | 測(cè)試用例1 | 0.003926s | 0.002620s |
加法 | 測(cè)試用例2 | 0.026735s | 0.005711s |
加法 | 測(cè)試用例3 | 0.029378s | 0.005384s |
累加 | 測(cè)試用例1 | 0.003255s | 0.002536s |
累加 | 測(cè)試用例2 | 0.017843s | 0.002592s |
累加 | 測(cè)試用例3 | 0.034025s | 0.006474s |
減法 | 測(cè)試用例1 | 0.004237s | 0.002078s |
減法 | 測(cè)試用例2 | 0.024775s | 0.004939s |
減法 | 測(cè)試用例3 | 0.027634s | 0.004929s |
累減 | 測(cè)試用例1 | 0.004272s | 0.002034s |
累減 | 測(cè)試用例2 | 0.005407 | 0.001942s |
累減 | 測(cè)試用例3 | 0.019363s | 0.004282s |
乘法 | 測(cè)試用例1 | 0.043608s | 0.004751s |
乘法 | 測(cè)試用例2 | 0.479071s | 0.028358s |
乘法 | 測(cè)試用例3 | 3.375447s | 0.064259s |
累乘 | 測(cè)試用例1 只計(jì)算1000次 | 0.001237s | 0.000137s |
累乘 | 測(cè)試用例2 只計(jì)算1000次 | 0.001577s | 0.000187s |
累乘 | 測(cè)試用例3 只計(jì)算1000次 | 5.792887s | 0.081988s |
除法 | 測(cè)試用例1 | 0.025391s | 0.024763s |
除法 | 測(cè)試用例2 | 5.292809s | 0.516090s |
除法 | 測(cè)試用例3 | 0.395773s | 0.073812s |
累除 | 測(cè)試用例1 只計(jì)算1000次 | 0.059054s | 0.035722s |
累除 | 測(cè)試用例2 只計(jì)算1000次 | 0.103727s | 0.060936s |
累除 | 測(cè)試用例3 只計(jì)算1000次 | 89.748837s | 25.126072s |
將上表數(shù)據(jù)進(jìn)行分類相同類型取均值計(jì)算出提升速度如下圖所示,僅作參考。
圖1、速度提升
總結(jié)
以上就是今天要講的內(nèi)容,壓位優(yōu)化性能提升是比較顯著的,而且實(shí)現(xiàn)也很容易,大部分邏輯是一致的只是底數(shù)變大了而已。從性能測(cè)試結(jié)果來(lái)看所有計(jì)算至少由4倍的提升,乘法性能提升較大有可能是測(cè)試方法不嚴(yán)重,這個(gè)待以后驗(yàn)證??偟膩?lái)說(shuō),對(duì)高精度運(yùn)算進(jìn)行壓位優(yōu)化還是很有必要的,尤其是對(duì)時(shí)間和空間有要求的場(chǎng)景還是比較適用的。
附錄 1、性能測(cè)試代碼
#include<Windows.h> #include <iostream> static int a[819200]; static int b[819200]; static int c[819200]; static int mod[819200]; static char str[81920]; /// <summary> /// 返回當(dāng)前時(shí)間 /// </summary> /// <returns>當(dāng)前時(shí)間,單位秒,精度微秒</returns> static double getCurrentTime() { LARGE_INTEGER ticks, Frequency; QueryPerformanceFrequency(&Frequency); QueryPerformanceCounter(&ticks); return (double)ticks.QuadPart / (double)Frequency.QuadPart; } /// <summary> /// 性能測(cè)試 /// </summary> static void test() { double d = getCurrentTime(); loadStr(a, "50000"); loadInt(b, 50000); for (int64_t i = 1; i <= 500000; i++) { plus(a, b, c); } printf("plus performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadInt(b, 5); for (int64_t i = 1; i <= 500000; i++) { plus(a, b, c); } printf("plus performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 500000; i++) { plus(b, a, c); } printf("plus performence 3: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "50000"); loadInt(b, 50000); for (int64_t i = 1; i <= 500000; i++) { plusq(a, b); } printf("plusq performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); for (int64_t i = 500000000; i <= 500000000 + 500000; i++) { loadInt(b, i); plusq(a, b); } printf("plusq performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(b, "999999999999999999999999999999999999999999999999999999999999999999"); for (int64_t i = 500000000; i <= 500000000 + 500000; i++) { loadInt(a, i); plusq(a, b); } printf("plusq performence 3: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "50000"); loadInt(b, 10000); for (int64_t i = 1; i <= 500000; i++) { sub(a, b, c); } printf("sub performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "100000000000000000000000000000000000000000000000000000000000000000"); loadInt(b, 11111); for (int64_t i = 1; i <= 500000; i++) { sub(a, b, c); } printf("sub performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "100000000000000000000000000000000000000000000000000000000000000000"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 500000; i++) { sub(a, b, c); } printf("sub performence 3: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "50000000000"); loadInt(b, 500000); for (int64_t i = 1; i <= 500000; i++) { subq(a, b); } printf("subq performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "100000000000000000000000000000000000000000000000000000000000000000"); loadInt(b, 11111); for (int64_t i = 1; i <= 500000; i++) { subq(a, b); } printf("subq performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "100000000000000000000000000000000000000000000000000000000000000000"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 500000; i++) { subq(a, b); } printf("subq performence 3: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "50000"); loadInt(b, 12345); for (int64_t i = 1; i <= 500000; i++) { mul(a, b, c); } printf("mul performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadInt(b, 12345); for (int64_t i = 1; i <= 500000; i++) { mul(a, b, c); } printf("mul performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 500000; i++) { mul(b, a, c); } printf("mul performence 3: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "2"); loadInt(b, 2); for (int64_t i = 1; i <= 1000; i++) { mulq(a, b); } printf("mulq performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadInt(b, 2); for (int64_t i = 1; i <= 1000; i++) { mulq(a, b); } printf("mulq performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 1000; i++) { mulq(b, a); } printf("mulq performence 3: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "50000"); loadInt(b, 12345); for (int64_t i = 1; i <= 500000; i++) { div(a, b, c, mod); } printf("div performence 1: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "100000000000000000000000000000000000000000000000000000000000000000"); loadInt(b, 12345); for (int64_t i = 1; i <= 500000; i++) { div(a, b, c, NULL); } printf("div performence 2: %llfs\n", getCurrentTime() - d); d = getCurrentTime(); loadStr(a, "100000000000000000000000000000000000000000000000000000000000000000"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 500000; i++) { div(a, b, c, mod); } printf("div performence 3: %llfs\n", getCurrentTime() - d); loadStr(a, "1"); loadStr(b, "2"); for (int64_t i = 1; i <= 1000; i++) { mulq(a, b); } d = getCurrentTime(); for (int64_t i = 1; i <= 1000; i++) { divq(a, b, mod); } printf("divq performence 1: %llfs\n", getCurrentTime() - d); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadStr(b, "2"); for (int64_t i = 1; i <= 1000; i++) { mulq(a, b); } d = getCurrentTime(); for (int64_t i = 1; i <= 1000; i++) { divq(a, b, mod); } printf("divq performence 2: %llfs\n", getCurrentTime() - d); loadStr(a, "999999999999999999999999999999999999999999999999999999999999999999"); loadStr(b, "11111111111111111111111111111111111111"); for (int64_t i = 1; i <= 500; i++) { mulq(a, b); } d = getCurrentTime(); for (int64_t i = 1; i <= 500; i++) { divq(a, b, mod); } printf("divq performence 3: %llfs\n", getCurrentTime() - d); }
到此這篇關(guān)于詳解C/C++高精度(加減乘除)算法中的壓位優(yōu)化的文章就介紹到這了,更多相關(guān)C++壓位優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- c++加法高精度算法的簡(jiǎn)單實(shí)現(xiàn)
- 使用C++的string實(shí)現(xiàn)高精度加法運(yùn)算的實(shí)例代碼
- c++實(shí)現(xiàn)高精度加法
- C/C++高精度算法的實(shí)現(xiàn)
- C++高精度算法的使用場(chǎng)景詳解
- C/C++高精度運(yùn)算(大整數(shù)運(yùn)算)詳細(xì)講解
- C/C++高精度(加減乘除)算法的實(shí)現(xiàn)
- 詳解C/C++高精度算法的簡(jiǎn)單實(shí)現(xiàn)
- C++?高精度乘法運(yùn)算的實(shí)現(xiàn)
- C/C++高精度算法實(shí)現(xiàn)思路與代碼
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)手寫(xiě)紅黑樹(shù)的示例代碼
紅黑樹(shù)在表意上就是一棵每個(gè)節(jié)點(diǎn)帶有顏色的二叉搜索樹(shù),并通過(guò)對(duì)節(jié)點(diǎn)顏色的控制,使該二叉搜索樹(shù)達(dá)到盡量平衡的狀態(tài)。本文主將用C語(yǔ)言實(shí)現(xiàn)手寫(xiě)紅黑樹(shù),需要的可以參考一下2022-09-09C++實(shí)現(xiàn)線性表順序存儲(chǔ)的示例代碼
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)線性表順序存儲(chǔ)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下2023-03-03C語(yǔ)言入門(mén)篇--初識(shí)結(jié)構(gòu)體
本篇文章是基礎(chǔ)篇,適合c語(yǔ)言剛?cè)腴T(mén)的朋友,本文對(duì)c語(yǔ)言的結(jié)構(gòu)體做了簡(jiǎn)單的分析,幫助大家快速入門(mén)c語(yǔ)言的世界,更好的理解c語(yǔ)言2021-08-08c++?error:crosses?initialization?of問(wèn)題解決分析
這篇文章主要介紹了c++?error:crosses?initialization?ofde?問(wèn)題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08C++ boost 時(shí)間與日期處理詳細(xì)介紹
這篇文章主要介紹了C++ boost 時(shí)間與日期處理詳細(xì)介紹的相關(guān)資料,這里提供實(shí)例代碼,及實(shí)現(xiàn)效果,需要的朋友可以參考下2016-11-11關(guān)于C語(yǔ)言一維數(shù)組算法問(wèn)題詳解
數(shù)組是以順序格式排列的均勻數(shù)據(jù)的集合,在C語(yǔ)言中學(xué)習(xí)數(shù)組的概念非常重要,因?yàn)樗腔镜臄?shù)據(jù)結(jié)構(gòu),這篇文章主要給大家介紹了關(guān)于C語(yǔ)言一維數(shù)組算法問(wèn)題的相關(guān)資料,需要的朋友可以參考下2021-11-11