我们怎么衡量一个函数/代码块/算法的优劣呢?这需要从多个角度看待。本篇笔记我们先不考虑代码可读性、规范性、可移植性那些角度。

在我们嵌入式中,我们需要根据实际资源的情况来设计我们的代码。比如当我们能用的存储器空间极其有限的情况,我之前就有遇到这样子的情况,我能用的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++编程学习基地【点击进入】!

都是学编程小伙伴们,带你入个门还是简简单单啦,一起学习,一起加油~

还有许多学习资料和视频,相信你会喜欢的!

涉及:游戏开发、常用软件开发、编程基础知识、课程设计、黑客等等......

 

 

【C语言学习笔记】空间换时间,查表法的经典例子!知识就是这么学到的~的更多相关文章

  1. MySQL学习笔记:生成时间维度表

    # ---- 对应时间戳怎么生成的? ---- /*TIME_CD TIME_CD1000000 000005000001 000005000002 000005000003 000005000004 ...

  2. MySQL学习笔记:生成时间维度表2

    实现目的: 测试: # 测试 加一秒 SECOND), INTERVAL SECOND); SECOND),'%H%i%s');# 第一秒 SECOND),'%H%i%s');# 最后一秒 SELEC ...

  3. java学习进制转换之查表法

    10进制转16进制,以及10进制转2进制,还有10进制转8进制,这些转换如果按照常规思路的话,会灰常的麻烦. 我们来看一下 10进制转16进制: 假如这里有一个十进制数字:35,我们的需求就是把这个3 ...

  4. Redis学习笔记~关于空间换时间的查询案例

    回到目录 空间与时间 空间换时间是在数据库中经常出现的术语,简单说就是把查询需要的条件进行索引的存储,然后查询时为O(1)的时间复杂度来快速获取数据,从而达到了使用空间存储来换快速的时间响应!对于re ...

  5. 【GO】GO语言学习笔记三

    7.数组: 几乎是最常用的数据类型了... 数组就是指一系列同一类型数据 的集合.数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数 组的长度. 常规的数组声明方法: ...

  6. 你好,C++(28)用空间换时间 5.2 内联函数 5.3 重载函数

    5.2  内联函数 通过5.1节的学习我们知道,系统为了实现函数调用会做很多额外的幕后工作:保存现场.对参数进行赋值.恢复现场等等.如果函数在程序内被多次调用,且其本身比较短小,可以很快执行完毕,那么 ...

  7. 2017-05-4-C语言学习笔记

    C语言学习笔记... ------------------------------------ Hello C语言:什么是程序:程序是指:完成某件事的既定方式和过程.计算机中的程序是指:为了让计算机执 ...

  8. GO语言学习笔记(一)

    GO语言学习笔记 1.数组切片slice:可动态增长的数组 2.错误处理流程关键字:defer panic recover 3.变量的初始化:以下效果一样 `var a int = 10` `var ...

  9. Go语言学习笔记九: 指针

    Go语言学习笔记九: 指针 指针的概念是当时学C语言时了解的.Go语言的指针感觉与C语言的没啥不同. 指针定义与使用 指针变量是保存内存地址的变量.其他变量保存的是数值,而指针变量保存的是内存地址.这 ...

随机推荐

  1. C#开发PACS医学影像处理系统(一):开发背景和说明

    本系列文章将从以下模块和大家分享和讨论使用C#开发医学软件PACS和RIS系统, 国内相关资料比较少,也借此机会丰富一下医学软件开发生态,讨论技术难点,希望大家互相帮助共同进步. 章节介绍及截图预览: ...

  2. js去掉最右边的逗号

    str=(str.substring(str.length-1)==',')?str.substring(0,str.length-1):str;

  3. JZOJ1496 页

    Description 战神阿瑞斯听说2008年在中华大地上,将举行一届规模盛大的奥林匹克运动会,心中顿觉异常兴奋,他想让天马在广阔的天空上,举行一场精彩的天马队列变换表演.首先,战神安排n头高度不同 ...

  4. 常见重构技巧 - 5种方式去除多余的if else

    常见重构技巧 - 去除多余的if else 最为常见的是代码中使用很多的if/else,或者switch/case:如何重构呢?方法特别多,本文带你学习其中的技巧. 常见重构技巧 - 去除多余的if ...

  5. json出现引用 "$ref": "$.conpolice[2]"

    1. 出现这个问题一般是因为代码循环引用出现的问题,可以改变逻辑,也可以直接加上下面加粗的代码 JSONObject jsonObject = new JSONObject(); jsonObject ...

  6. k8s数据管理(八)

    volume 我们经常会说:容器和 Pod 是短暂的.其含义是它们的生命周期可能很短,会被频繁地销毁和创建.容器销毁时,保存在容器内部文件系统中的数据都会被清除. 为了持久化保存容器的数据,可以使用 ...

  7. Infor EAM:注重行业属性,实现对轨道交通线性资产的可视化管理

    Infor EAM:注重行业属性,实现对轨道交通线性资产的可视化管理 企业得利,一要开源,二要节流.而企业资产管理的目的,也正是从资产的角度出发,一方面通过相关资源与活动的合理安排提高设备可利用率.增 ...

  8. Kubernetes入门(四)——如何在Kubernetes中部署一个可对外服务的Tensorflow机器学习模型

    机器学习模型常用Docker部署,而如何对Docker部署的模型进行管理呢?工业界的解决方案是使用Kubernetes来管理.编排容器.Kubernetes的理论知识不是本文讨论的重点,这里不再赘述, ...

  9. Java多线程--原子性、可见性、有序性

    计算机的内存模型: 计算机在运行行程序的时候,指令由CPU执行,计算机上数据存放在物理内存当中,CPU在执行指令的时候免不了要和数据打交道.刚开始,还相安无事的,但是随着CPU技术的发展,CPU的执行 ...

  10. python中生成随机整数(random模块)

    1.从一个序列中随机选取一个元素返回:   random.choice(sep)    2.用于将一个列表中的元素打乱   random.shuffle(sep)    3.在sep列表中随机选取k个 ...