分布式一致性算法04:ZAB协议

分布式一致性算法04 ZAB协议

一、基本概念
ZAB(Zookeeper Atomic Broadcast)协议是为Apache Zookeeper框架设计的一致性协议。
与Paxos仅能保证事务的一致性不同,ZAB可以同时保证事务的顺序性和一致性。

二、ZAB算法涉及三种角色和四种状态
三种角色:
1、领导者(Leader): 在同一时间,集群只会有一个领导者。所有的写请求都必须在领导者节点执行。
2、跟随者(Follower):集群可以有多个跟随者,它们会响应领导者的心跳,并参与领导者选举和提案提交的投票。跟随者可以直接处理来自客户端的读请求,但会将写请求转发给领导者处理。
3、观察者(Observer):与跟随者类似,但不参与竞选和投票权。

节点有四种状态:
LOOKING:选举状态,该状态下的节点认为当前集群中没有领导者,会发起领导者选举。
LEADING :领导者状态,意味着当前节点是领导者。
FOLLOWING :跟随者状态,意味着当前节点是跟随者。
OBSERVING: 观察者状态,意味着当前节点是观察者。

三、关键概念
1、Epoch(纪元)
定义:Epoch是ZXID的高32位,表示领导者的任期编号。每次新的领导者被选举出来时,Epoch都会增加。
作用:
标识领导者的任期。不同的Epoch代表不同的领导者任期,确保了领导者的唯一性。
当发生分区或领导者故障时,新的领导者会具有更高的Epoch,从而确保了新的领导者具有最新的状态。

2、ZXID(事务ID)
定义:ZXID是一个64位的数字,用于唯一标识Zookeeper中的每个事务。
组成:ZXID的高32位表示Epoch,低32位表示事务计数器(XID)。
作用:
确保事务的全局顺序性。每个事务都有一个唯一的ZXID,保证了事务在所有服务器上的处理顺序一致。
通过比较ZXID,服务器可以确定事务的先后顺序,以及是否已经处理过某个事务。

四、ZAB选举流程
1、初始化:
集群中的每个服务器(Server)启动时,都处于“Looking”状态,即寻找Leader状态。

2、投票:
每个处于“Looking”状态的服务器都会发送投票(Vote)给其他服务器,尝试将自己选举为Leader。
投票格式大概为:
(新Leader节点的ID, 新Epoch编号, 当前最新的ZXID,投票节点的ID)

3、收集选票:
收到各节点的选票后,各节点会根据以下规则,判定如何投票:
优先检查任期编号Epoch,任期编号大的节点作为领导者;
如果任期编号Epoch相同,比较事务标识符ZXID的最大值,值大的节点作为领导者;
如果事务标识符的最大值相同,比较节点ID,节点ID大的节点作为领导者;
通过这样的规则,首先保证了数据最新的节点,获取更高的票数。

4、确定Leader
如果没有任何节点获取了过半选票,则要开始下一轮选举,回到步骤2,但此时不再会投票给自己,而是会投票给上一轮最合适的节点。
如果某个服务器获得了超过半数的投票,它就认为自己被选举为新的Leader。

5、同步:
新Leader会与集群中的Follower进行数据同步,确保所有Follower的数据与Leader一致。
数据同步完成,Leader就可以接收客户端的事务请求了。

五、ZAB选举示例
假设我们有一个Zookeeper集群,包含5个节点(P1~P5),所有节点都处于“Looking”状态。
Epoch=2;XID=5;ZXID=(2,5)
投票信息格式为:
(新Leader的ID, 新Leader的Epoch编号, 新Leader的最新的ZXID,选举轮次,投票节点的ID)

1、初始化
P1~P5开始寻找Leader,并发送投票请求,并投票给自己。

2、投票
P1:(新Leader=P1, 新Leader的Epoch=2, 新Leader的最新的ZXID=(2,5),选举轮次=1,投票节点=P1)
P2:(新Leader=P2, 新Leader的Epoch=2, 新Leader的最新的ZXID=(2,5),选举轮次=1,投票节点=P2)
P3:(新Leader=P3, 新Leader的Epoch=2, 新Leader的最新的ZXID=(2,3),选举轮次=1,投票节点=P3)
P4:(新Leader=P4, 新Leader的Epoch=2, 新Leader的最新的ZXID=(2,2),选举轮次=1,投票节点=P4)
P5:(新Leader=P5, 新Leader的Epoch=1, 新Leader的最新的ZXID=(1,1),选举轮次=1,投票节点=P5)

3、收集选票
对于同一轮的选票,节点自行对比判定选举结果:
P1:新Leader=P2,因为P2节点ID更大
P2:新Leader=P2,不变
P3:新Leader=P2,因为P2 ZXID更大
P4:新Leader=P2,因为P2 ZXID更大
P5:新Leader=P2,因为P2 Epoch更大
但如果一个节点收到了更小选举轮次(logicalclock)的投票,该投票会被忽略。

4、再次投票,这样P2会获得全部5票

5、确定Leader
P2认为被选举为Leader。其余节点,会主动与P2交换最大的ZXID,如果P2的更新则承认P2的领导地位,否则重新发起选举。
当过半节点承认P2的领导地位时,P2正式成为Leader。
同时P2会将Epoch+1,此时:
Epoch=3;ZXID=(3,0)

6、同步
P2根据之前各节点反馈的最大ZXID,开始与其他节点进行数据同步,强制要求所有Follower的数据与自己的数据一致。
数据同步完成后,P2告知全部Follower数据同步完毕,P2开始接收提案,各节点开始同步P2的提案。

六、ZAB同步算法的工作流程
延续上面的例子,Zookeeper集群,包含5个节点(P1~P5),P2是Leader
Epoch=3;XID=0;ZXID=(3,0)

1、客户端请求:
客户端向P3发送写请求,P3会将写请求转交给Leader P2。

2、事务提案:
Leader接收到请求后,会生成一个事务提案Proposal,分配一个全局唯一的ZXID,并进行持久化。

3、广播提案:
Leader将事务提案发送给所有Follower。

4、Follower投票:
Follower接收到提案后,会将Proposal写入本地日志,并根据自身的数据状态进行投票(必须按ZXID顺序提交),并将投票结果发送回Leader。

5、Leader决策
当Leader收到超过半数Follower的同意投票后,提交事务。同时将通知所有Follower更新状态。
如果提案被拒绝,事务将被回滚。

6、客户端响应
P3收到Leader事务提交的消息,P3会向客户端返回操作结果,客户端根据结果进行相应的处理。

七、ZAB协议的关键点:
1、顺序一致性:ZAB协议通过全局唯一的ZXID,保证了客户端请求的顺序性。
2、可靠性:即使在部分节点宕机的情况下(投票数可以过半),ZAB协议也能够保证系统的可靠性和数据的一致性。
3、防止分区:当新Leader选举成功后,如果旧Leader恢复过来,由于新Leader有更高的Epoch,旧Leader的请求不再会被接受;一个节点,如果与Leader失联,则是无法处理读写请求的;

可以看出,ZAB算法与RAFT算法整体的考虑方向是一致的,就是先选择一个Leader,通过Leader控制事务提交的顺序。
但就具体的实现而言,RAFT的实现更简单可以实现强一致性,ZAB的实现更复杂一些可以实现最终一致性。

分布式一致性算法03:RAFT

分布式一致性算法03 RAFT协议

一、基本概念
Raft算法首选会选举一位领导者(Leader),所有的写请求都先提交给Leader,Leader通过日志复制(Log Replication)来保持集群中所有节点的状态一致。

在这个过程中有一些限制:
1、Leader只能有一个,Leader会通过心跳包告知其他节点,“我是Leader,我还活着,你们别想选举”
2、Leader和Follower之间通过日志同步的方式达成一致;在一条日志中,包含了索引编号、指令、Leader任期等信息
3、日志记录了所有的事务操作,必须在所有节点上顺序一致(索引编号是递增的)
4、Raft算法通过任期编号和选举超时机制来避免分裂脑现象,确保在任期内只有一个Leader

二、RAFT算法通常涉及三种角色
领导者(Leader):负责处理所有客户端请求,将日志条目(Log Entries)复制到其他节点。
跟随者(Follower):接收领导者的日志条目并保持与领导者的心跳连接。
候选者(Candidate):当跟随者在一定时间内没有收到领导者的心跳时,它们会变成候选者并尝试成为新的领导者。
在实际情况中,一个节点通常只会承担一个角色,但这个角色通过选举机制是可以相互转换的。

三、RAFT Leader初始过程
1、当系统初始化时,或者Leader状态异常无法正常发送心跳包后,会开启选举过程。
2、此时,每个节点都是Follower,每个Follower都持有一个时间随机的选举计时器。
3、当一个Follower的选举计时器超时后,该Follower会判定Leader不可用,于是它自增任期号,并转换为Candidate。
4、Candidate首选会给自己投票,并向其他节点发送 RequestVote RPCs 请求投票,该请求中包含新任期号以及日志信息。
5、其余节点收到请求后,会检查请求中的任期号和自己的日志信息。如果受到的任期号更大,它们会投票给该Candidate。
6、如果一个Candidate从集群中大多数节点那里收到投票,它就赢得了选举,成为新的Leader。
7、新的Leader会立即向所有节点发送心跳信息,以通知它们自己的存在,并防止它们变成Candidate。

四、选举过程举例
假设一个5节点组成的Raft集群P1~P5,其中P1是Leader,其余为Follower,此时任期为Term1。此时P1故障,无法发送心跳包。

1、P2~P4节点都为Follower状态,每个节点都会初始化一个随机的选举计时器。
计时器分别为:P2 100ms,P3 200ms,P4 300ms,P5 400ms

2、由于一直收不到心跳包,P2的选举计时器会首先超时。
此时P2会在任期号Term1上增加,生成任期号Term2,并转换为 Candidate 状态。

3、P2给自己投票,并向其他全部节点发送 RequestVote RPCs 请求投票,并带上任期号Term2

4、P3~P5收到投票后,对比Term2和自己日志中的任期号
如果日志任期号更达,则拒绝投票
在本例中,Term2任期号更大,P3~P5就都会投票给P2

5、P2收到P3~P5的投票,加上自己的一票,超过半数,选举成功,P2成为新的Leader

6、P2会立即向所有节点发送心跳信息,其他竞选失败的Candidate,退回为Follower状态,开始跟随Leader的日志。
所有Follower会重置选举计时器,等待再次成为Candidate,并发起选举。

7、在日志复制之前,P2需要确定自己的日志与Follower的日志是否一致。它会发送AppendEntries RPC请求给Follower,并在请求中包含自己日志的最后一个日志Entry(索引和命令)和任期(PrevLogEntry和PrevLogTerm)。
如果Follower的日志与Leader匹配,则可以开始日志同步。
如果Follower的日志与Leader不匹配,它会发送一个失败的响应。Leader则会回退一个日志Entry,再次与Follower确认,直至双方一致为止。此时,Leader会将最新日志同步给Follower,从而让双方日志完全一致。

8、P2此时正式成为Leader,开始接收请求,并开始同步日志(AppendEntries RPC)

投票过程有一些限制:
单次投票:当Follower收到不同Candidate的同一Term的 RequestVote RPCs时,只会投票给第一个
仅投票给日志完整性高的Candidate:当Follower收到Candidate的RequestVote RPCs时,如果Candidate的日志完整性不如自己高,则拒绝投票
过半投票:Candidate必须有过半的投票,才能升级为Leader,从而避免脑裂

当然,也会有一些特殊情况:
系统启动:节点启动后默认为Follower,由于没有Leader,各节点很快会进入选举超时,并转换为Candidate,并发起选举。
选举失败:没有任何Candidate获得足够的票数,这样会继续进入下一轮选举,而且这个等待选票的时间,也是随机的

四、Raft算法的工作流程
当完成Leader的选举后,工作流程就比较简单了。

接收请求:
客户端向Leader发送一个写请求,比如设置键值对key1=value1。

日志复制:
Leader创建一个新的日志条目,并将其追加到自己的日志末尾。

日志同步:
Leader将这个日志条目发送给跟所有的Follower,包含了日志条目的信息以及领导者的任期号等。
Follower收到日志后,首先验证任期是否有效,如果任期有效,而且日志条目与本地日志记录兼容,则存储这个日志条目到自己的日志末尾。
跟随者在成功存储日志条目后,会向领导者发送一个确认响应(包含当前的任期号和成功标识)。

日志提交:
Leader等待Follower的确认消息,一旦收到大多数Follower的确认,它就将日志记录为“已提交”状态。
此时,Leader会向客户端返回操作成功。

应用日志:
Leader会在心跳包以及新日志复制的消息中,带上当前最新日志索引编号。
Follower接收到指令后,会将编号小于等于最新日志索引编号的日志条目,应用到自己的状态机中。
由于所有节点都按照相同的日志条目顺序执行操作,因此所有节点的状态机最终将保持一致。

五、效率提升
仅Leader接收请求:
避免集群多次通讯达成一致的过程,Leader接收并处理全部请求,然后同步给Follower。大幅提高吞吐能力。

日志压缩:
为了减少存储空间的使用,会定期进行日志压缩,保留关键的日志信息,而丢弃已经被持久化和应用的日志条目。

随机选举计时器:
通过随机时间,避免了所有节点一起发起投票,只投给自己的情况。用相对较低

单节点变更:
当增减节点的时候,保证一次只增减1个节点,可以有效的避免脑裂的出现

分布式一致性算法02:Multi-Paxos

分布式一致性算法02 Multi-Paxos算法

一、基本概念
Multi-Paxos算法,在Paxos的基础上,通过引入领导者(Leader)的概念,大幅提升了效率。

二、Multi-Paxos算法通常涉及三种角色:
Leader(领导者)/Proposer(提议者) :在Multi-Paxos中,通常会选举出一个Leader作为Proposer,负责提出提议,并尝试让多数接受者接受该提议。
Acceptor(接受者) :负责接受或拒绝提议者的提议。每个节点都可以是Acceptor,负责记录和确认提议。
Learner(学习者) :负责学习最终达成一致的提议。

三、Multi-Paxos算法的基本过程
1、初始化阶段
首先,通过Paxos算法,选择一个领导者(Leader),它负责发起全部提议。

2、准备阶段(Prepare Phase)
领导者向集群中的其他节点发送Prepare消息,这些消息包含当前的最大提议编号。
Acceptor(接受者)接收到Prepare消息后,会返回一个响应,表明它们是否已经接收到更高编号的提议。
如果领导者接收到大多数节点的响应,表示它们没有更高的提议,则可以继续下一步。

3、接受阶段(Accept Phase)
领导者根据收到的Prepare响应选择一个提议编号,并向集群中的其他节点发送Accept消息。这些消息包含提议编号和提议值。
节点接收到Accept消息后,如果该提议编号高于它们当前已接受的提议,则会接受该提议并返回确认消息。如果大多数节点确认接受该提议,则该提议被接受。

4、提交阶段(Commit Phase)
一旦提议被大多数节点接受,领导者将该提议标记为已提交,并通知集群中的其他节点。
节点接收到提交通知后,将执行该提议,并更新其状态。

5、应用阶段(Apply Phase)
最后,领导者将已提交的提议应用到状态机中,并通知集群中的其他节点。
节点接收到应用通知后,也会将提议应用到其状态机中,从而确保所有节点的状态一致。

四、举例说明
假设我们有一个分布式系统,由10个节点组成:P1、P2是提议者(Proposer),A1、A2、A3、A4、A5是接受者(Acceptor),L1、L2、L3是学习者(Learner)。

1、初始化阶段
在Multi-Paxos中,任何提议者都可以被选为领导者。
系统启动时,通过某种机制(比如Paxos算法)选举出一个新的领导者,这里假设P1被选为领导者。

2、准备阶段(Prepare Phase)
领导者P1,生成一个提议编号(例如7),向所有接受者询问,是否可以接受编号为7的提议。
接受者A1~A5收到消息后,由于提议编号7大于它们之前见过的任何提议编号,将进入承诺状态,并向P1发送可接受提议编号7的提议,并承诺不会接受任何编号小于7的提议。
【通过算法优化,其实可以节省提议编号这个阶段,直接发起提议】

3、接受阶段(Accept Phase)
当受到大多数接受者同意提议编号的消息后,P1向集群中的所有接受者发送Accept消息,提议编号7的内容为V7。
接收者收提议后,如果该提议编号高于它们当前已接受的提议,则会接受该提议并返回确认消息。

4、提交阶段(Commit Phase)
一旦P1收到来自多数接受者的已接受消息,它将向所有接受者发送提交消息,指示它们可以提交这个提议。

5、应用阶段(Apply Phase)
接受者在收到提交消息后,将提议的键值对应用到状态机中,并将结果通知学习者。

五、与Paxos算法对比
并发性:Paxos算法在每次达成共识时都需要进行两轮消息传递,这限制了它的并发能力。而Multi-Paxos通过引入领导者(Leader)的概念,允许多个提议者并发地提出提议,从而提高了并发性。
消息复杂度:在Paxos算法中,每个提议都需要两轮的通信(Prepare和Accept阶段),这增加了消息的复杂度。Multi-Paxos通过减少通信轮次,允许领导者在一轮中提出多个提议,从而减少了消息的复杂度。
实时性:由于Multi-Paxos允许并发提议,它在实时性方面通常优于Paxos算法。在Paxos算法中,每个提议都需要等待前一个提议完成后才能开始,这可能导致延迟。
容错性:Paxos算法和Multi-Paxos都具有很好的容错性,但Multi-Paxos由于其并发性,在某些容错情况下可能表现更好,例如在领导者失败时可以快速选举新领导者并继续处理提议。
实现复杂度:Paxos算法的实现相对复杂,而Multi-Paxos虽然在理论上提供了并发性的优势,但其实现也引入了额外的复杂性,如领导者选举和故障恢复机制。
优化和变种:Multi-Paxos有许多优化和变种,如Fast Paxos和EPaxos,它们通过进一步减少通信轮次或利用特定的系统特性来提高性能。而在实际应用中,许多系统采用Multi-Paxos或其变种,如Raft算法,以提高性能。

分布式一致性算法01:Paxos

分布式一致性算法01 Paxos算法

一、基本概念
Paxos是分布式一致性的经典算法,由Leslie Lamport在1990年提出。

二、Paxos算法通常涉及三种角色:
Proposer(提议者) :负责提出提议(Proposal),即向系统中提出一个值。Proposer通常是客户端,负责发起提议并分配一个不重复的自增ID给每个提议。
Acceptor(接受者) :参与决策过程,负责接收并回应Proposer的提议。每个Acceptor只能接受一个值,并且为了保证最多只有一个值被选定(Chosen),Proposal必须被超过一半的Acceptors所接受。
Learner(学习者) :负责学习最终被选定的值。Learner不参与协商过程,只是接收并记录最终被选定的值。
在实际过程中,一个节点可以同时承担1~3个角色。

三、Paxos算法的基本过程
1、准备阶段(Prepare Phase):
提议者(Proposer)选择一个提议编号(ballot number),并将其发送给所有接受者(Acceptor)。
接受者在接收到提议者的准备请求后,如果当前编号大于其已承诺的最高编号,则更新其承诺编号,并返回一个承诺(promise)给提议者,承诺中包含当前已接受的最高编号和值。

2、提议阶段(Proposal Phase):
提议者收集到多数接受者的承诺后,选择一个值(value),并结合最新的提议编号,生成提议(propose)消息发送给接受者。
接受者在接收到提议消息后,如果提议编号大于其已有的承诺编号,则接受该提议,并返回确认消息给提议者。

3、决定阶段(Decide Phase):
提议者收集到多数接受者的确认消息后,可以决定该值为最终值,并将其写入日志或状态机中。
学习者(Learner)从提议者处获取并学习最终决定的值,确保所有节点都有一致的状态。

四、举例说明:
假设我们有一个分布式系统,包含10个节点:P1、P2是提议者,A1、A2、A3、A4、A5是接受者,L1、L2、L3是学习者。

1、准备阶段:
P1提出一个提议编号,编号为1。
P1向所有接受者A1~A5发送询问,询问P1将发起编号为1提议是否可以。
A1收到提议时,并没有反馈过任何一次提议,于是反馈可以接受,并承诺,后续不会接受编号比1小的提议。A2~A4类似。

几乎同时,P2提出一个提议,编号为5。
P2向所有接受者A1~A5发送询问,询问P2将发起编号为5提议是否可以。
A1收到提议时,承诺的最小编号为1,于是反馈可以接受,并承诺,后续不会接受编号比5小的提议。A2~A4类似。

到这里,P1和P2的提议,前后都被允许提交了。当然,也有情况可能是,部分节点先收到了P2,这种情况下,P1的提议编号就无效了,需要重新拟定编号,这个编号必须单调增加。

2、提议阶段
P1收到了过半节点的提议编号反馈,向所有接受者A1~A5发送提议,告知提议编号为1的提议值为V1。
接受者收到提交消息时,已经无法接收比5小的提议,于是就拒绝P1,提议不通过。

P2收到了过半节点的提议编号反馈,向所有接受者A1~A5发送提议,告知提议编号为5的提议值为V5。
接受者收到提交消息时,反馈P2同意了5号提议。

3、决定阶段
当提议5收到过半同意反馈时(5个节点中3个以上同意),认为提议通过,各节点并将V5写入日志。
此时,学习者L1、L2、L3也会学习到V5的结果,并写入日志。

五、Paxos算法的特点
多数同意:在Paxos算法中,只有当提议者收到超过半数接受者的同意时,提议才可能被提交。
唯一性:Paxos算法保证了在任何给定的一轮中,只有一个提议可以被接受。
容错性:即使在一些节点失败的情况下,Paxos算法也能够工作。
Paxos算法的实现和理解都相当复杂,但它是许多现代分布式系统一致性协议的基础,如Raft算法等。

编译guetzli

#1.下载并安装配置vcpkg
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
bootstrap-vcpkg.bat
vcpkg integrate install
vcpkg install libpng

# 如果用CMake,记录下面的输出路径
CMake projects should use:
"-DCMAKE_TOOLCHAIN_FILE=D:/Build/vcpkg/scripts/buildsystems/vcpkg.cmake"

#2.下载guetzli
git clone https://github.com/google/guetzli.git

#3.用VS2017进行编译

#4.测试效果
整体上压缩比率还是很不错的,但这个压缩速度,还是放弃吧。

Update:
如果是图像压缩的话,现在webp格式还是很不错的,可以直接下载工具:
https://developers.google.com/speed/webp/download
https://chromium.googlesource.com/webm/libwebp/

运煤问题

提问:
一个山西煤老板,在矿区开采了3000吨煤需要运送到市场上去卖,从矿区到市场有1000公里。现在有一列烧煤的火车,该火车最多只能装1000吨煤,但其每一公里需要耗一吨煤作为燃料。请问,怎么运送才能运最多的煤到集市?(火车可以调头,火车最终不需要返回煤矿)

约束:
1、火车一定需要调头
2、调头的原则是,消耗的燃料越少越好
3、当煤不足一车时,仍然要当作一车运输

解答:
1、当煤多于2000吨时,火车一定要运三次,同样的路要跑五次
1000吨煤,走五次,可以走1/5路程
2、当煤多余1000吨时,火车一定要运两次,同样的路要跑三次
1000吨煤,走三次,可以走1/3路程
3、最终剩余1000吨时,直接跑到终点
最后剩余路程为1-1/5-1/3=7/15
跑完后剩余的煤为1000-7/15*1000=1000*8/15=533零1/3吨

以上。

二进制数字中1的个数

#include "stdafx.h"
#include <nmmintrin.h>

/*
 *通过移位计算1的个数
 *每一位都要判断和处理
 */
int BitCount(unsigned int n)
{
	unsigned int c = 0;
	while (n >0)
	{
		if ((n & 1) == 1)++c;
		n >>= 1;
	}
	return c;
}

/*
 *通过减法及位运算,保证每次至少消除一个1
 *只处理1的位,0的位不处理
 */
int BitCountWithMinus(unsigned int n)
{
	unsigned int c = 0;
	for (c = 0; n; ++c)
	{
		n &= (n - 1);
	}
	return c;
}

/*
*将32位数字,截为4个8位数
*通过查表,得到每个8位数中1的个数,然后求和
*/
int BitCountLUT8(unsigned int n)
{
	unsigned char BitsSetTable256[256] = { 0 };

	for (int i = 0; i <256; i++)
	{
		BitsSetTable256[i] = (i & 1) + BitsSetTable256[i / 2];
	}

	unsigned int c = 0;

	unsigned char* p = (unsigned char*)&n;

	c = BitsSetTable256[p[0]] +
		BitsSetTable256[p[1]] +
		BitsSetTable256[p[2]] +
		BitsSetTable256[p[3]];

	return c;
}

/*
*将32位数字,截为4个8位数
*通过查表,得到每个8位数中1的个数,然后求和
*LUT表已经计算好
*/
int BitCountLUT8Static(unsigned int n)
{
	unsigned int table[256] =
	{
		0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
		1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
		2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
		3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
		4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
	};

	return table[n & 0xff] +
		table[(n >> 8) & 0xff] +
		table[(n >> 16) & 0xff] +
		table[(n >> 24) & 0xff];
}

/*
*将32位数字,截为8个4位数
*通过查表,得到每个4位数中1的个数,然后求和
*LUT表已经计算好
*/
int BitCountLUT4Static(unsigned int n)
{
	unsigned int table[16] =
	{
		0, 1, 1, 2,
		1, 2, 2, 3,
		1, 2, 2, 3,
		2, 3, 3, 4
	};

	unsigned int count = 0;
	while (n)
	{
		count += table[n & 0xf];
		n >>= 4;
	}
	return count;
}

/*
*将32位数字,相邻的2位求和,然后相邻的4位求和
*然后相邻的8位、16位、32位求和
*/
int BitCountParallel(unsigned int n)
{
	n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
	n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
	n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
	n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
	n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);

	return n;
}

/*
*第一行,计算每三位的1的个数(其实11*3是33位,但最高一位可以假设为0,所以没问题)
*第二行,实际上是先计算了相邻6位中1的个数(不会产生进位,结果最多占到3位),并将前三位置为0
*然后通过取模,相当于将每6位数字做了加法
*/
int BitCountMagic(unsigned int n)
{
	unsigned int tmp = n - ((n >> 1) & 033333333333) - ((n >> 2) & 011111111111);
	return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}

unsigned int n = 127;
unsigned int bitCount = _mm_popcnt_u32(n);


/*
*将8位数字,转换为MY_UNSIGHED_CHAR结构体,然后求和
*/
struct MY_UNSIGHED_CHAR
{
	unsigned a : 1;
	unsigned b : 1;
	unsigned c : 1;
	unsigned d : 1;
	unsigned e : 1;
	unsigned f : 1;
	unsigned g : 1;
	unsigned h : 1;
};

long BitCountStuct(unsigned char b)
{
	struct MY_UNSIGHED_CHAR *by = (struct MY_UNSIGHED_CHAR*)&b;
	return (by->a + by->b + by->c + by->d + by->e + by->f + by->g + by->h);
}

int _tmain(int argc, _TCHAR* argv[])
{
	printf("There are %d 1 in 133\n", BitCount(133));
	printf("There are %d 1 in 133\n", BitCountWithMinus(133));
	printf("There are %d 1 in 133\n", BitCountLUT8(133));
	printf("There are %d 1 in 133\n", BitCountLUT8Static(133));
	printf("There are %d 1 in 133\n", BitCountLUT4Static(133));
	printf("There are %d 1 in 133\n", BitCountParallel(133));
	printf("There are %d 1 in 133\n", BitCountMagic(133));
	printf("There are %d 1 in 133\n", BitCountStuct(133));
	return 0;
}

用位运算实现加减乘除

        //加法
	public static int add(int a, int b)  
	{  
	    int ans;  
	    while(b!=0)  
	    {
	        ans = a^b;
	        b = ((a&b)<<1);
	        a = ans;  
	    }  
	    return a;  
	}  
	
	//减法
	public static int sub(int a, int b)
	{  
	    return add(a, -b);  
	}
	
	//正数乘法
	private static int posMultiply(int a,int b)  
	{  
	    int ans = 0;  
	    while(b>0)  
	    {  
	        if((b&0x1)==1)ans = add(ans, a);  
	        a = (a<<1);  
	        b = (b>>1);  
	    }  
	    return ans;  
	}  
	  
	//乘法
	public static int multiply(int a,int b)  
	{  
	    if(a==0||b==0)return 0;  
	    if(a>0 && b>0)  
	        return posMultiply(a, b);  
	    if(a<0)  
	    {  
	        if(b<0)  
	        {  
	            return posMultiply(-a, -b);  
	        } 
	        else
	        {
	        	return -posMultiply(-a, b ); 
	        }
	    }
	    else
	    {
	    	return -posMultiply(a, -b);
	    }
	}  
	  
	//正数除法
	private static int posDiv(int x,int y)  
	{  
	    int ans=0;  
	    for(int i=31;i>=0;i--)  
	    {
	        if((x>>i)>=y)  
	        {  
	            ans+=(1<<i);  
	            x-=(y<<i);  
	        }  
	    }  
	    return ans;  
	}  
	  
	//除法
	public static int div( int a, int b )  
	{
		assert(b!=0);
	    if(a==0)return 0;

	    if(a>0)  
	    {  
	        if(b>0)
	        {
	            return posDiv(a,b); 
	        }
	        else
	        {
	        	return -posDiv( a,-b);  
	        }
	    }  
	    else
	    {
	    	if(b>0)
	    	{
		        return -posDiv(a, b);  
	    	}
	    	else
	    	{
	    		return -posDiv(-a, -b);  
	    	}
	    }
	    
	}   
	
	//求负数
	public static int negtive(int a)
	{  
	    return add(~a, 1);  
	}
	  
	//比较正数大小
	private static boolean isbigerPos( int a, int b )   
	{
	    int c = 1;  
	    b = (a^b);  
	    if(b==0)return false;
	    
	    while(b>0)  
	    {  
	    	b>>=1;
	        c <<= 1;  
	    }  
	    return (c&a)==0;  
	}   
	  
	//比较大小   
	public static boolean isbiger( int a, int b )   
	{
	    if(a<0)  
	    {  
	        if(b<0)  
	        {  
	            return isbigerPos( negtive(b), negtive(a) );  
	        }
	        else
	        {
	        	return false;  
	        }
	    }
	    else
	    {
		    if(b<0)
		    {
		        return true;  
		    }
		    else
		    {
		    	return isbigerPos(a, b);  
		    }
	    }

	}

	public static int divideby3(int x)  
	{  
	    int sum = 0;  
	    while(x > 3)  
	    {  
	        sum = add(x>>2 , sum);  
	        x = add(x>>2 , x&3);  
	    } 
	    if(x == 3) 
	    {
	        sum = add(sum , 1);
	    }
	    return sum;  
	}  
	
	public static void main(String[] args)
	{
		System.out.println(add(13,19));
		System.out.println(sub(13,19));
		System.out.println(multiply(13,19));
		System.out.println(div(10,5));
		System.out.println(negtive(13));
		System.out.println(isbiger(10,5));
		System.out.println(isbiger(10,11));
		
		System.out.println(divideby3(15));
		System.out.println(divideby3(14));
		System.out.println(divideby3(13));
		System.out.println(divideby3(12));
		System.out.println(divideby3(11));
	}

几种常见求解平方根的方法

        //精度
	private final static double accuracy= 1e-6;

	/**
	 * 暴力求解
	 */
	public static double bruteSqrt(double x)
	{
		assert(x>=0);
		double ans=0.0;
		while (Math.abs(x - ans * ans) > accuracy)ans += accuracy;
		return ans;
	}

	/**
	 * 牛顿法求解
	 */
	public static double newtonSqrt(double x)
	{
		assert(x>=0);
		double avg = x;
		double last_avg = Double.MAX_VALUE;

		while (Math.abs(avg - last_avg) > accuracy)
		{
			last_avg = avg;
			avg = (avg + x / avg) / 2;
		}
		return avg;
	}

	/**
	 * 二分法求解
	 */
	public static double binarySqrt(double x)
	{
		assert(x>=0);

		double low = 0;
		double high = x;
		double mid = Double.MAX_VALUE;
		double last_mid = Double.MIN_VALUE;

		while (Math.abs(mid - last_mid) > accuracy)
		{
			last_mid = mid;
			mid = (low + high)/2;
			if (mid*mid>x)high = mid;
			if (mid*mid<x)low = mid;
		}
		return mid;

	}

	private final static int[] LUT =
	{ 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84,
			86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115,
			116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136,
			137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155,
			155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171,
			172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186,
			187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200,
			201, 201, 202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213,
			214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, 224, 224, 225, 225,
			226, 226, 227, 227, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237,
			237, 238, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248,
			248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 };

	/**
	 * 查表法求解
	 */
	public static int intLutSqrt(int x)
	{
		int xn;

		if (x >= 0x10000)
		{
			if (x >= 0x1000000)
			{
				if (x >= 0x10000000)
				{
					if (x >= 0x40000000)
					{
						xn = LUT[x >> 24] << 8;
					}
					else
					{
						xn = LUT[x >> 22] << 7;
					}
				}
				else
				{
					if (x >= 0x4000000)
					{
						xn = LUT[x >> 20] << 6;
					}
					else
					{
						xn = LUT[x >> 18] << 5;
					}
				}

				xn = (xn + 1 + (x / xn)) >> 1;
				xn = (xn + 1 + (x / xn)) >> 1;
				return ((xn * xn) > x) ? --xn : xn;
			}
			else
			{
				if (x >= 0x100000)
				{
					if (x >= 0x400000)
					{
						xn = LUT[x >> 16] << 4;
					}
					else
					{
						xn = LUT[x >> 14] << 3;
					}
				}
				else
				{
					if (x >= 0x40000)
					{
						xn = LUT[x >> 12] << 2;
					}
					else
					{
						xn = LUT[x >> 10] << 1;
					}
				}

				xn = (xn + 1 + (x / xn)) >> 1;

				return ((xn * xn) > x) ? --xn : xn;
			}
		}
		else
		{
			if (x >= 0x100)
			{
				if (x >= 0x1000)
				{
					if (x >= 0x4000)
					{
						xn = (LUT[x >> 8]) + 1;
					}
					else
					{
						xn = (LUT[x >> 6] >> 1) + 1;
					}
				}
				else
				{
					if (x >= 0x400)
					{
						xn = (LUT[x >> 4] >> 2) + 1;
					}
					else
					{
						xn = (LUT[x >> 2] >> 3) + 1;
					}
				}

				return ((xn * xn) > x) ? --xn : xn;
			}
			else
			{
				if (x >= 0)
				{
					return LUT[x] >> 4;
				}
			}
		}

		return -1;
	}

	/**
	 * Quake III中快速求解平方根倒数的方法
	 */
	public static float fastInvSqrt(float x)  
	{  
	     float xhalf = 0.5f*x;  
	     int f2i = Float.floatToRawIntBits(x);
	     f2i = 0x5f375a86-(f2i>>1);
	     x = Float.intBitsToFloat(f2i);
	     x = x*(1.5f-xhalf*x*x);
	     x = x*(1.5f-xhalf*x*x);
	     return x;  
	}
	
	/**
	 * Quake III中快速求解平方根方法
	 */
	public static float fastSqrt(float x) {  
	    float y=x;
	    float xhalf = 0.5f*x;
	    int f2i = Float.floatToRawIntBits(x);  
	    f2i = 0x5f3759df-(f2i>>1);  
	    x = Float.intBitsToFloat(f2i);  
	    x  = x * (1.5f-(xhalf*x*x));  
	    x  = x * (1.5f-(xhalf*x*x));  
	    return y*x;  
	}

	public static void main(String[] args)
	{
		System.out.println(bruteSqrt(3));
		System.out.println(newtonSqrt(3));
		System.out.println(binarySqrt(3));
		System.out.println(intLutSqrt(64));
		System.out.println(1/fastInvSqrt(3));
		System.out.println(fastSqrt(3));
	}