【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這樣子大數據的情況,全部列出來的話會大幅度的增加我們的存儲開銷,這種以空間換時間的情況可能會得不償失。

看到這里你是不是又學到了很多新知識呢~

如果你很想學編程,小編推薦我的C語言/C++編程學習基地【點擊進入】!

都是學編程小伙伴們,帶你入個門還是簡簡單單啦,一起學習,一起加油~

還有許多學習資料和視頻,相信你會喜歡的!

涉及:游戲開發、常用軟件開發、編程基礎知識、課程設計、黑客等等......


 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM