肥宅钓鱼网
当前位置: 首页 钓鱼百科

0应该是什么样的(0真的代表什么都没有么)

时间:2023-07-06 作者: 小编 阅读量: 5 栏目名: 钓鱼百科

0真的代表什么都没有么说起0这个数字,大家的第一反应都是它代表什么都没有,但是其实0在数学中有极其重要的地位本文结合了几个生动的小例子,诠释了0在数学表达中的“占位”思想,并在最后站在程序员的角度,结合0的“占位”思想,给。

说起0这个数字,大家的第一反应都是它代表什么都没有,但是其实0在数学中有极其重要的地位。本文结合了几个生动的小例子,诠释了0在数学表达中的“占位”思想,并在最后站在程序员的角度,结合0的“占位”思想,给出了一段降低队列锁粒度的代码。

1. 计数

1.1 计数的历史

古埃及人:使用5进制和10进制混合的计数法。5和10为一个单元,用记号标识,比如用一道横线代表1,一道竖线代表10等,但是古埃及人没数位的概念,在表示1万时很容易,比如画一个青蛙,但是表示9999时,非常复杂。古埃及人使用一种草纸来计数。

古巴比伦人:粘土板上使用棱形记号来表示数。他们使用1和10两种棱形记号表示1~59,并将记号所在的位置来表示数位。现在通用的1小时=60分钟的时间换算就是源于古巴比伦的60进制计数法。

NOTE:由于粘土板很难书写很多符号,因此古巴比伦人才需要尽可能少的记号来表示数,也正是这一硬件限制才促成了按位计数法的产生。

罗马人:使用现在也能常见到的罗马数字来计数,类似I、V、XI等,5进制和10进制混合的计数法。

玛雅人:数数从0开始,使用20进制计数法。

印度人:引进古巴比伦的按位计数法的同时,认识到0也是数字,采用的是10进制。现在使用的0、1、2、3、4、5、6、7、8、9被成为阿拉伯数字,可能是因为将印度数字引入欧洲的是阿拉伯人吧。

1.2 计数的意义

为什么人类要发明计数法呢?

罗马数字中,将1、2、3写为I、II、III,4写作IIII或IV,5写作V,为什么不将5也记为IIII呢?显而易见,在罗马数字这种表示法下,数越大越难处理,比如,IIIIIII和IIIIII哪个更大不能马上得知,同时在表示一个较大的数时非常费劲。

从历史上计数的方式可以看出,为了高效地解决较大数的表示,古人想出了两种方法——10进制计数和按位计数

而如今,人类发展到可以分析基因序列、探索宇宙的阶段了,处理的数据呈爆炸性的增长,按位计数法已经力不从心了,比如,100000000和1000000000哪个更大也不能直观的看出了,因此,衍生出了新的计数法——指数计数法。刚才的2个数,如果写为10^8和10^9就能一眼看出大小了。

1.3 指数法则

我相信很多人在看到10^2时,是认为10^2是2个10相乘,那么10的0次方是什么?

如果10的0次方是0个10相乘的话,那么10^0应该等于0,而不是1,问题出在哪里呢?

我们对于指数k^n的定义是k个n相乘,那么如果k=0或者k=-1怎么理解呢?-1个10相乘?很明显,我们对于指数的理解是比较局限的,那么如何理解指数呢?

我们把指数的计算放到一起来寻找规律:

103=1000

102=100

101=10

100=?

每当右上角的数字减1时,值就变位原来的10分之1,那么对于指数的定义呼之欲出:

  • N^1 = N

  • 指数每增加1,数字就变为原来的N倍

  • 指数每减少1,数字就变为原来的N分之一

那么10^-1也很好解释和理解了。

2. 0的作用

2.1 占位

10进制表示的数1024中的0表示百位上“没有”了,但这个0却不能忽略,一旦忽略了,就变成了124,变成了另外一个数了。

2.2 标准、简化

在指数计数法中,使用0以后,能够将按位计数法中的各个数位所对应的大小统一表示成10n。否则需要单独处理”1”这个数位,也就是个位。0在这里标准化了对于位数的表达。正因为有了这个标准,按位计数法的各个数位也能统一写为ak∗10k

3. 程序员中的0

3.1 需求中的0

需求:有一种胶囊,3天服用后停用1次,要求比较方便的服药

方案:设计一个“没有药效”的胶囊,放在事先准备好的有标号或者日期的盒子中,在停用的部分放上“没有药效”的胶囊

NOTE:这里正好借用了0占位的作用,便于统一处理

3.2 设计中的0

队列:队列是一种先入先出的数据结构,这个是广为人知的,为了引入下面的情况,先给出队列的伪代码。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

structnode_t {

TYPE value;

node_t *next;

}

structqueue_t {

node_t *head;

node_t *tail;

}

initialize(queue_t *queue) {

queue->head = ; //初始化头节点为空

queue->tail = ; //初始化尾节点为空

}

enqueue(queue_t *queue, TYPE value) {

node_t *node = newnode_t;

node->value = value;

node->next = ;

if(queue->tail != ) { //尾节点不为空,则将新节点增加到尾部

queue->tail->next = node;

}

if(queue->head == ) { //头节点为空,则将头指针指向这个节点

queue->head = node;

}

queue->tail = node

}

dequeue(queue_t * queue, TYPE *value) {

node_t *node = queue->head;

if(node == ) { //队列为空

returnfalse;

}

*value = node->value;

new_head_node = node->next;

if(new_head_node == ) { //队列中的最后一个元素出队列时,更新tail指针

queue->tail = ;

}

queue->head = new_head_node;

FREE(node);

returntrue;

}

多线程下的有锁队列:在多线程环境下,enqueue和dequeue都会对head和tail指针进行操作,为了保证线程安全,普通的做法加入一个队列锁,伪代码如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

structnode_t {

TYPE value;

node_t *next;

}

structqueue_t {

node_t *head;

node_t *tail;

pthread_mutex_t q_lock;

}

initialize(queue_t *queue) {

queue->head = ; //初始化头节点为空

queue->tail = ; //初始化尾节点为空

queue->q_lock = FREE; //锁初始化为free

}

enqueue(queue_t *queue, TYPE value) {

node_t *node = newnode_t;

node->value = value;

node->next = ;

lock(&queue->q_lock); //锁住队列

if(queue->tail != ) { //尾节点不为空,则将新节点增加到尾部

queue->tail->next = node;

}

if(queue->head == ) { //头节点为空,则将头指针指向这个节点

queue->head = node;

}

queue->tail = node;

unlock(&queue->q_lock); //释放锁

}

dequeue(queue_t * queue, TYPE *value) {

lock(&queue->q_lock); //锁住队列

node_t *node = queue->head;

if(node == ) { //队列为空

returnfalse;

}

*value = node->value;

new_head_node = node->next;

if(new_head_node == ) { //队列中的最后一个元素出队列时,更新tail指针

queue->tail = ;

}

queue->head = new_head_node;

unlock(&queue->q_lock); //解锁

free(node);

returntrue;

}

上面的做法,每次enqueue和dequeue操作都会锁住整个队列,当使用的线程多的时候,就存在锁的竞争造成的性能瓶颈。那么,有没有办法来降低锁的粒度呢?

在上面的伪代码中:

  • enqueue往队列的尾部插入节点,大部分的时候只修改tail指针

  • dequeue从队列头部删除节点,大部分的时候只修改head指针

因此,大部分的时候enqueue或者dequeue的时候没必要锁住整个队列。所以,拆锁的方向很明确了——头部锁&尾部锁。

考虑比较特殊的情况:

  • 当队列为空,enqueue用到了head指针,head需要指向新的节点

  • 当队列只剩一个元素的时候,dequeue用到了tail指针,tail需要指向

在这种情况下,如果使用头部和尾部锁,两个锁是分开申请的,因此显而易见,很容易造成死锁。有没有什么优雅的方法解决这个问题呢?

在队列从空到有和从有到空的两种特殊情况下,是需要一些特殊的处理。如果队列一直不为空,那么

  • enqueue的时候,就不需要通知head指针指向新的头部

  • dequeue的时候,就不需要通知tail指针已经没有节点了

很显然,引入一个没有实际意义的”空节点“,那么队列就不会为空,上述的问题也就不复存在了,伪代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

structnode_t {

TYPE value;

node_t *next;

}

structqueue_t {

node_t *head;

node_t *tail;

pthread_mutex_t q_head_lock;

pthread_mutex_t q_tail_lock;

}

initialize(queue_t *queue) {

node_t *node = newnode_t;

node->next = ;

queue->head = node; //初始化头节点为空

queue->tail = node; //初始化尾节点为空

queue->q_head_lock = FREE; //头部锁初始化为free

queue->q_tail_lock = FREE; //尾部锁初始化伪free

}

enqueue(queue_t *queue, TYPE value) {

node_t *node = newnode_t;

node->value = value;

node->next = ;

lock(&queue->q_tail_lock); //锁住尾节点

node_t *tail = queue->tail;

tail->next = node; //尾节点不为空,则将新节点增加到尾部

tail = node;

unlock(&queue->q_tail_lock); //释放锁

}

dequeue(queue_t * queue, TYPE *value) {

lock(&queue->q_head_lock); //锁住头节点

node_t *node = queue->head;

new_head_node = node->next;

if(new_head_node == ) { //此时队列为空,头节点已经指向了空节点

unlock(&queue->q_head_lock);

returnfalse;

}

*value = node->value;

queue->head = new_head_node;

unlock(&queue->q_lock); //解锁

free(node);

returntrue;

}

    推荐阅读
  • 松江三大必游景区(松江这些景点3月11日起关闭或调整开闭时间)

    上海欢乐谷上海世茂精灵之城主题乐园上海辰山植物园根据市、区疫情防控要求,接上级通知,辰山植物园温室3月11日起关闭,何时恢复另行通知。造成不便,敬请谅解。上海辰山植物园2022年3月11日广富林文化遗址尊敬的游客朋友们:为落实疫情防控要求,广富林文化遗址于2022年3月11日起各室内场馆临时闭馆,场馆恢复对外开放时间将另行通知。

  • 大侦探福尔摩斯其实没有破案(福尔摩斯东飘来华)

    国门不复,昔日被视为猛水猛兽的东西源源不断地涌进中国。梁启超就专门创办了一个名为《新小说》的报纸,在他主管的《时务报》上也会连续刊登新小说。英剧福尔摩斯剧照但是事实并非如此。到1936年,《福尔摩斯侦探全集》已经重印了20版,可见受欢迎之程度。当时有两个学者,一位名为陈景韩,一位名为包天笑。两人同是民国时期著名的翻译家、报人和小说家。

  • 窗户上不能放的东西(窗户上千万别放这东西)

    若是电线杆不巧立在了窗户边,则会因为线路所产生的辐射场等而形成煞气,对居住者的身心健康会造成不利影响。并且因为绿色植物的遮挡,则会因为其本身所带阴性破坏室内阴阳平衡,出现阴气过旺的现象。若是居住者为女子的话,加上女子的阴寒体性而加重室内的阴阳失调,进而影响到居住者的健康或运势。如果为了求财,则需在专业人士指导下选择开光物品进行吊挂。会导致居住着感情不稳,或是财运不济。

  • 人际交往需要注意的禁忌(人际交往需要注意的禁忌是什么)

    即使你临时出去半个小时,也要与同事打个招呼。互相告知,既是共同工作的需要,也是联络感情的需要,它表明双方互有的尊重与信任。有事不肯向同事求助:轻易不求人,这是对的。但任何事物都是辩证的,有时求助别人反而能表明你对别人的信赖,能融洽关系,加深感情。良好的人际关系是以互相帮助为前提的。因此,求助他人,在一般情况下是可以的。当然,要讲究分寸,尽量不要使人家为难。

  • 如何登陆增值税发票综合服务平台(一般纳税人请查收)

    我是寡言少语,一个怀揣梦想的小伙伴感谢~点赞~关注~评论~转发~每天与您分享更多干货您的支持,是我前进的动力增值税发票综合服务平台有什么作用?一、注意收藏网址,方便使用;二、主要功能:1、发票抵扣勾选;2、抵扣勾选统计;3、发票查询统计;4、发票真伪查验;5、企业档案信息;发票抵扣勾选一、发票勾选,注意勾选所发期,正常显示当期;二、勾选选取范围:1、起始日期可勾选以前期间;2、截止日期,如为当期,

  • 2021上海樱花节游玩攻略(上海樱花节2021最佳观赏地点)

    成人票20元/人,大中小学生凭学生证10元/人,60至64周岁老人凭有效身份证件16元/人,军人、65周岁及以上老人、残疾人等免票。赏樱地点:顾村公园地址:宝山区顾村镇沪太路4788号公园严格落实疫情防控措施,实行线上分时实名制预约与购票。度假住宿衡山北郊宾馆是度假和举办商务、会务活动的理想场所。宾馆将向游园度假和参加活动的宾客推出住宿、餐饮套餐及团购等优惠活动。

  • 曹怎么组词(曹如何组词以及音节)

    接下来我们就一起去研究一下吧!曹怎么组词阴曹地府[yīncáodìfǔ]地狱,阴间的官府。尔曹[ěrcáo]代词,汝辈,你们。曹白鱼[cáobáiyú]即鳓鱼。也叫鲙鱼、白鳞鱼。萧规曹随[xiāoguīcáosuí]萧何和曹参都是汉高祖的大臣。萧何创立了规章制度,死后,曹参做宰相,仍照章实行。比喻后一辈的人完全照前一辈的方式进行工作。

  • 第一次世界大战到底有多厉害(第一次世界大战为何会持续四年之久)

    历史第一次世界大战从1914年到1918年,持续了四年之久,范围波及亚欧非三大洲,最终造成了一千多万人死亡,两千多万人受伤,3000亿美元的财产损失。一方是以英国、法国、俄国为首的协约国集团,另一方就是以德国、奥匈帝国为首的同盟国集团。尤其是双方的最主要的交战国,法国和德国。所以德国国家安全战略的核心就是不要同时和俄国、法国威敌,从而避免东西两线作战。可以说德国人他在这个计划的细节,还有精确方面,确实是名不虚传。

  • 2022年3月济宁计算机等级考试报名公告 计算机等级考试报名时间2021山东

    所有考生须持有考前48小时内的核酸检测阴性纸质报告参加考试,核酸检测出结果的时间须在3月24日上午8:00后。本校学生可统一安排在校内进行核酸检测,其他考生应于规定时间内自行到相关医疗机构进行核酸检测。鼓励考生接种新冠病毒疫苗加强针。如考生2021年下半年已通过四级Linux考试,成绩将保留至2023年3月。

  • 里面穿的线裤腰瘦怎么接(裤子腰短怎么接)

    里面穿的线裤腰瘦了可以在腰部加一根松紧带,既不用剪也不用破坏线裤腰的整体性。线裤指线织的裤子,实际上是气温比较冷时候穿在外裤里面用来保暖的舒适贴身的长裤。线裤属于内衣类,不能外穿出门。最好用40-45℃的温水洗,因为线衣线裤是贴身衣物,比较容易沾染身体油脂、油泥和新陈代谢下来的皮肤残渣,用温水洗的话比较容易去掉这些污垢,水太凉不容易去污,水太热容易对衣物造成损害。