空间换时间,查表法的经典例子
前言
上一篇分享了:C語言精華知識:表驅動法編程實踐
這一篇再分享一個查表法經典的例子。
我們怎么衡量一個函數/代碼塊/算法的優劣呢?這需要從多個角度看待。本篇筆記我們先不考慮代碼可讀性、規范性、可移植性那些角度。
在我們嵌入式中,我們需要根據實際資源的情況來設計我們的代碼。
比如當我們能用的存儲器空間極其有限的情況,我之前就有遇到這樣子的情況,我能用的flash空間只有4KB,但是要實現的功能很多,稍微不注意就超了,這種情況下我們就得多考慮程序占用方面的問題。
如果我們的存儲器空間很足,有時候可以犧牲一些存儲器空間來換取我們程序的運行速度。查表法就是以空間換取時間的典型例子。下面看一個經典的例子:
基礎例子
編寫程序統計一個4bit數據(0x0~0x0F)中1的個數。這里提供兩種方法:
1、方法一:常規法
常規法就是依次判斷這個4bit的數據的每一位是否為1,并用一個計數變量把1的個數記錄下來:
左右滑動查看全部代碼>>>
#include?<stdio.h>/*?測試結果?*/ struct?test_res {unsigned?int?data;??/*?數據?????????*/unsigned?int?count;?/*?數據中1的個數?*/ };struct?test_res?get_test_res(unsigned?int?data) {/*?保存測試結果?*/struct?test_res?res;/*?保證數據總會在0~0xf之間?*/unsigned?int?temp?=?data?&?0xf;??res.count?=?0;res.data?=?temp;/*?循環判斷每一位?*/for?(int?i?=?0;?i?<?4;?i++){if?(temp?&?0x01){res.count++;}temp?>>=?1;}return?res; }int?main(void) {struct?test_res?res?=?{0};for?(int?i?=?0;?i?<?32;?i++){res?=?get_test_res(i);printf("%2d中二進制位為1的個數有%d\n",?res.data,?res.count);}return?0; }運行結果:
unsigned int temp = data & 0xf; 語句就是為了保證數據都是在0x0~0xf之間,即0~15為一個周期,如果輸入的數據為16,則當做0來看待,輸入的數據為17,則當做1來看待……
2、方法二:查表法
這個例子也可以用查表法來做,把0x0~0xF中的所有數據中每個數據的1的個數都記錄下來,存放到一個表中。
這樣一來,數據與數據中1的個數就建立起了一一對應關系,我們就可以通過數組索引來獲取我們想要的結果:
左右滑動查看全部代碼>>>
int?table[16]?=?{0,?1,?1,?2,?1,?2,?2,?3,?1,?2,?2,?3,?2,?3,?3,?4};struct?test_res?get_test_res(unsigned?int?data) {/*?保存測試結果?*/struct?test_res?res;/*?保證數據總會在0~0xf之間?*/unsigned?int?temp?=?data?&?0xf;??/*?獲取結果?*/res.data?=?temp;res.count?=?table[temp];return?res; }常規法使用for循環的方式來實現,缺點是占用了不少處理器的時間;查表法的優點彌補了常規法的不足,但是額外占用了一些靜態空間。
這里針對這個應用而言處理的數據還是比較簡單的,數據范圍只是0x0~0xF之間,所以這兩種方式可能也都差不多。
那如果以上題目稍微改一下:編寫程序統計一個8bit、16bit數據中1的個數。查表法換取的時間就比較明顯了。
延伸例子
下面我們先來看一下編寫程序統計一個8bit(0x0~0xFF)數據中1的個數的情況。
1、常規法
把以上代碼稍微改一下就可以:
左右滑動查看全部代碼>>>
struct?test_res?get_test_res(unsigned?int?data) {/*?保存測試結果?*/struct?test_res?res;/*?保證數據總會在0~0xf之間?*/?unsigned?int?temp?=?data?&?0xff;??res.count?=?0;res.data?=?temp;/*?循環判斷每一位?*/for?(int?i?=?0;?i?<?16;?i++){if?(temp?&?0x01){res.count++;}temp?>>=?1;}return?res; }運行結果:
2、查表法
上面的數據范圍僅僅是0x0~0xF,數據量比較少,建立數據表也比較容易。
這里的數據量范圍變成了0x0~0xFF,比原來多了兩百多個數據,這也還可以接受,也還可以全都列出來。
但是針對這里的這個問題有更好的方法:
在這個問題中,8bit的數據可以看做兩個4bit數據,這樣就可以共用上面4bit數據的數據表。所以我們只要把2個4bit數據的1的個數相加,就是最后的結果。
獲取8bit數據1的個數:
左右滑動查看全部代碼>>>
struct?test_res?get_test_res(unsigned?int?data) {/*?保存測試結果?*/struct?test_res?res;/*?保證數據總會在0~0xf之間?*/unsigned?int?temp?=?data?&?0xff;??/*?獲取低4位中1的個數?*/unsigned?int?low_data?=?temp?&?0xf;unsigned?int?low_cnt?=?table[low_data];/*?獲取高4位中1的個數?*/unsigned?int?high_data?=?(temp?>>?4)?&?0xf;unsigned?int?high_cnt?=?table[high_data];/*?結果?*/res.count?=?low_cnt?+?high_cnt;res.data?=?temp;return?res; }同樣的,獲取16bit數據也是類似的,把16bit數據當做4個4bit數據。
針對以上這個查表法的例子我們可以總結出:
1、數據表的確定要合適。像上面8bit的情況再重新創建一個數據表把表元素列出來也還可以接受。但是如果是16bit這樣子大數據的情況,建立這么大的數據表也不太現實。所以需要考慮如何建立一個合適的數據表。
2、需要權衡空間換取時間是否值得。像16bit這樣子大數據的情況,全部列出來的話會大幅度的增加我們的存儲開銷,這種以空間換時間的情況可能會得不償失。
推薦閱讀:
? ??專輯|Linux文章匯總
? ??專輯|程序人生
? ??專輯|C語言
嵌入式Linux
微信掃描二維碼,關注我的公眾號?
總結
以上是生活随笔為你收集整理的空间换时间,查表法的经典例子的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity的渲染管线
- 下一篇: OpenGL可编程管线