说到数字0,大家的第一反应是毫无意义,但实际上0在数学中起着极其重要的作用。结合几个生动的例子,说明了数学表达式中0的“占位”思想,最后从程序员的角度,结合0的“占位”思想,给出了一个降低队列锁粒度的代码。
1.计数1.1计数历史
古埃及人:他们使用5和10的混合计数法。5和10是一个单元,用标记来标记,例如代表1的水平线和代表10的垂直线等。但是古埃及人没有数字的概念,表示一万很容易,比如画一只青蛙,但是表示9999就很复杂。古埃及人用一种纸莎草纸来计数。
巴比伦人:用粘土板上的棱形标记来表示数字。他们用1和10两种棱形标记来表示1~59,标记的位置用来表示数字。现在1小时=60分钟的时间通用换算是起源于古巴比伦的60制计数法。
注:由于在粘土板上很难写出很多符号,古巴比伦人需要尽可能少的符号来表示数字,正是这种硬件限制导致了位计数法的产生。
罗马数字:用常见的罗马数字来计数,如I、v、XI等。,5位小数和10位小数的混合计数法。
玛雅:计数从0开始,使用20系统计数法。
印度人:在介绍巴比伦的按位计数法时,我意识到0也是一个数字,采用了十进制。0,1,2,3,4,5,6,7,8,9现在被称为阿拉伯数字,可能是因为是阿拉伯人把印度数字引入欧洲的。
1.2计数的意义
人类为什么发明了计数?
罗马数字中,1、2、3写成I、II、IIII,4写成IIII或IV,5写成v,为什么不把5写成IIII?显然,在罗马数字中,数字越大,越难处理。比如哪个大一点不是马上就知道的,表达一个大一点的数字很费力。
从历史上的计数方式可以看出,为了高效地解决大数的表示,古人想出了两种方法——十进制计数和逐位计数。
现在人类已经发展到可以分析基因序列,探索宇宙的阶段,处理的数据爆炸了,所以比特计数法失败了。比如100,000,000或者100,000,000,不能直观的看到。因此,导出了一种新的计数方法——指数计数法。如果把刚才的两个数字写成10 8和10 9,一眼就能看出大小。
1.3指数定律
我相信很多人看到10 ^ 2都认为10 ^ 2是两个10的乘积,那么10的0次方是什么呢?
如果10乘以0 ^ 10,那么10 ^ 0应该等于0而不是1。有什么问题?
我们对指数k ^ n的定义是k乘以n,那么如果k=0或者k=-1该如何理解呢?-乘以10?显然,我们对指数的理解相当有限,那么如何理解指数呢?
我们把指数的计算放在一起寻找规律:
103=1000
102=100
101=10
100=?
每当右上角的数字减1,数值就会偏移原值的1/10,于是指数的定义就呼之欲出了:
N^1 = N
每次指数增加1,这个数就变成原来的N倍
每当指数减少1,这个数就变成原来数的1/n
那么10-1就很好解释和理解了。
2.0 2.1占位符的角色
十进制数字1024中的0在百位数中表示“无”,但这个0不能忽略。一旦忽略,就变成124,变成另一个数字。
2.2标准和简化
在指数计数法中,使用0后,位计数法中每个数字对应的大小可以统一表示为10n。否则,数字“1”需要单独处理,即一位数字。0在这里规范了数字的表达。因为有了这个标准,位计数法的每一位数字都可以统一写成AK∫10k。
3.程序员3.1中的0,需求中的0
需求:有一种胶囊,3天后停用一次,需要方便服用。
方案:设计一个无药效的胶囊,放在事先准备好标签或日期的盒子里,将无药效的胶囊放在停用的部位
注意:这里只是借用了0占位符的作用,方便统一处理
3.2 设计中的0队列:队列是一种先进先出的数据结构,这是众所周知的。为了介绍以下情况,先给出队列的伪代码。
一个
2
三
四
五
六
七
八
九
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
四十二岁
43
四十四
45
46
47
48
四十九
50
51
五十二
53
54
55
56
57
58
结构节点_t {
类型值;
node _ t * next
}
结构队列_t {
node _ t * head
node _ t * tail
}
初始化 {
队列->;head = null//将头节点初始化为空
队列->;tail = null//将尾节点初始化为空
}
排队{
node _ t * node = new node _ t
节点->;value = value
节点->;next = null
if {//如果尾节点不是空,将新节点添加到尾部
队列->;tail->;next =节点;
}
if {//如果头节点为空,则将头指针指向该节点
队列->;head = node
}
队列->;tail =节点
}
出列 {
node_t *node = queue->头;
If {//队列为空
返回false
}
*value = node->;价值;
new_head_node = node->接下来;
If {//队列中最后一个元素离开队列时更新尾指针
队列->;tail =空;
}
队列->;head = new _ head _ node
自由;
返回true
}
多线程下带锁队列:在多线程环境下,入队和出队都操作头指针和尾指针。为了确保线程安全,通常的做法是添加队列锁。伪代码如下。
一个
2
三
四
五
六
七
八
九
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
四十二岁
43
四十四
45
46
47
48
四十九
50
51
五十二
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
六十八
六十九
70
71
七十二
73
74
75
结构节点_t {
类型值;
node _ t * next
}
结构队列_t {
node _ t * head
node _ t * tail
pthread _ mutex _ t q _ lock
}
初始化 {
队列->;head = null//将头节点初始化为空
队列->;tail = null//将尾节点初始化为空
队列->;q _ lock = FREE//锁被初始化为空闲
}
排队{
node _ t * node = new node _ t
节点->;value = value
节点->;next = null
锁定;//锁定队列
if {//如果尾节点不是空,将新节点添加到尾部
队列->;tail->;next =节点;
}
if {//如果头节点为空,则将头指针指向该节点
队列->;head = node
}
队列->;tail = node
解锁;//松开锁
}
出列 {
锁定;//锁定队列
node_t *node = queue->头;
If {//队列为空
返回false
}
*value = node->;价值;
new_head_node = node->接下来;
If {//队列中最后一个元素离开队列时更新尾指针
队列->;tail =空;
}
队列->;head = new _ head _ node
解锁;//解锁
自由;
返回true
}
在上面的实践中,每个入队和出队操作都会锁定整个队列。当使用的线程较多时,会出现锁竞争导致的性能瓶颈。那么,有什么方法可以降低锁的粒度呢?
在上面的伪代码中:
Enqueue将节点插入队列的末尾,大多数情况下只有尾部指针被修改
出列从队列头删除节点,大多数情况下只改变头指针
因此,大多数情况下,入队或出列时不需要锁定整个队列。所以开锁的方向是清头锁&:尾锁。
考虑特殊情况:
当队列为空时,入队使用头指针,头需要指向新节点
当队列中只剩下一个元素时,出列使用尾部指针,尾部需要指向空
这种情况下,如果使用头锁和尾锁,那么这两个锁是分开应用的,很明显很容易造成死锁。有什么优雅的方法可以解决这个问题?
在两种特殊情况下,从空排队到have和从have到空需要一些特殊处理。如果队列从不空,则
入队,你不需要告诉头部指针指向新的头部
出列,不需要通知尾指针没有节点
显然,如果引入一个无意义的“空节点,队列就不会是空,上述问题也就不复存在了。伪代码如下:
一个
2
三
四
五
六
七
八
九
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
四十二岁
43
四十四
45
46
47
48
四十九
50
51
五十二
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
六十八
结构节点_t {
类型值;
node _ t * next
}
结构队列_t {
node _ t * head
node _ t * tail
pthread _ mutex _ t q _ head _ lock
pthread _ mutex _ t q _ tail _ lock
}
初始化 {
node _ t * node = new node _ t
节点->;next =空;
队列->;head = node//将头节点初始化为空
队列->;tail = node//将尾节点初始化为空
队列->;q _ head _ lock = FREE//头锁初始化为自由
队列->;q _ tail _ lock = FREE//尾锁初始化伪自由
}
排队{
node _ t * node = new node _ t
节点->;value = value
节点->;next = null
锁定;//锁定尾节点
node_t *tail = queue->尾巴;
tail->;next =节点;//如果尾部节点不是空,将新节点添加到尾部
tail = node
解锁;//松开锁
}
出列 {
锁定;//锁定头节点
node_t *node = queue->头;
new_head_node = node->接下来;
If {//此时队列为空,头节点已经指向空节点
解锁;
返回false
}
*value = node->;价值;
队列->;head = new _ head _ node
解锁;//解锁
自由;
返回true
}
1.《0表示什么 0真的代表什么都没有么?》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《0表示什么 0真的代表什么都没有么?》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/fangchan/1744151.html