C/C++代码优化之求两个整型的平均值
在 C/C++ 中, 直接利用 (x + y) >> 1 来计算 \(\left\lfloor {\left( {x + y} \right)/2} \right\rfloor\) (两个整数的平均值并向下取整)以及直接利用 (x + y + 1) >> 1 来计算 \(\left\lceil {\left( {x + y} \right)/2} \right\rceil\) (两个整数的平均值并向上取整)的结果可能有误, 因为 (x + y) >> 1 和 (x + y + 1) >> 1 中的 x + y 可能会发生数值溢出. 而 \(\left\lfloor {\left( {x + y} \right)/2} \right\rfloor\) 和 \(\left\lceil {\left( {x + y} \right)/2} \right\rceil\) 的结果是不可能数值溢出的, 这就引发我们思考可不可能通过某种方式来规避平均值计算中的数值溢出.
方式一
利用如下公式
\(\begin{align}
\left\lfloor {\left( {x + y} \right)/2} \right\rfloor = \left\lfloor {x/2} \right\rfloor + \left\lfloor {y/2} \right\rfloor + \left\lfloor {\left( {x\bmod 2 + y\bmod 2} \right)/2} \right\rfloor \hfill \\
\left\lceil {\left( {x + y} \right)/2} \right\rceil = \left\lfloor {x/2} \right\rfloor + \left\lfloor {y/2} \right\rfloor + \left\lceil {\left( {x\bmod 2 + y\bmod 2} \right)/2} \right\rceil \hfill \\
\end{align}\)
下面是对上述两式的证明:
\(\begin{align}
\left\lfloor {\left( {x + y} \right)/2} \right\rfloor &= \left\{ {\begin{array}{*{20}{c}}
{m + n}&{x = 2m,y = 2n} \\
{m + n}&{x = 2m + 1,y = 2n} \\
{m + n}&{x = 2m,y = 2n + 1} \\
{m + n + 1}&{x = 2m + 1,y = 2n + 1}
\end{array}} \right. \\
&= \left\lfloor {x/2} \right\rfloor + \left\lfloor {y/2} \right\rfloor + \left\lfloor {\left( {x\bmod 2 + y\bmod 2} \right)/2} \right\rfloor \\
\end{align}\)
\(\begin{align}
\left\lceil {\left( {x + y} \right)/2} \right\rceil &= \left\{ {\begin{array}{*{20}{c}}
{m + n}&{x = 2m,y = 2n} \\
{m + n + 1}&{x = 2m + 1,y = 2n} \\
{m + n + 1}&{x = 2m,y = 2n + 1} \\
{m + n + 1}&{x = 2m + 1,y = 2n + 1}
\end{array}} \right. \\
&= \left\lfloor {x/2} \right\rfloor + \left\lfloor {y/2} \right\rfloor + \left\lceil {\left( {x\bmod 2 + y\bmod 2} \right)/2} \right\rceil \\
\end{align}\)
其中 \(m,n\) 均为整数.
借用上面的公式可以 \(\left\lfloor {\left( {x + y} \right)/2} \right\rfloor\) 转化为如下的 C/C++ 代码 (据说这段代码还被申请了专利):
(x >> 1) + (y >> 1) + (x & y & 1);
可以将 \(\left\lceil {\left( {x + y} \right)/2} \right\rceil\) 转化为如下的 C/C++ 代码:
(x >> 1) + (y >> 1) + ((x | y) & 1);
这两段代码都不会发生数值溢出.
方式二
设 x 和 y 只能取 0 和 1 值, 则:
| x | y | x + y | x ^ y | x & y | x | y | 2*(x & y) + (x ^ y) | 2*(x | y) - (x ^ y) |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 + 0 = 0 | 0 - 0 = 0 |
| 0 | 1 | 1 | 1 | 0 | 1 | 0 + 1 = 1 | 10 - 1 = 1 |
| 1 | 0 | 1 | 1 | 0 | 1 | 0 + 1 = 1 | 10 - 1 = 1 |
| 1 | 1 | 10 | 0 | 1 | 1 | 10 + 0 = 10 | 10 - 0 = 10 |
注意上表中的 10 是二进制下的 10, 即十进制下的 2, & 是逻辑与操作, | 是逻辑或运算, ^ 是逻辑异或操作.
由上表可见 x + y = 2*(x & y) + (x ^ y) = 2*(x | y) - (x ^ y).
无符号整型
对于无符号整型, 设 \(x = \sum\nolimits_{i = 0}^{n - 1} {{u_i}{2^i}}\) 和 \(y = \sum\nolimits_{i = 0}^{n - 1} {{v_i}{2^i}}\), 其中 \(u_i,v_i\in\left\{ 0, 1 \right\}\).
\(\begin{align}
x + y &= \sum\nolimits_{i = 0}^{n - 1} {{u_i}{2^i}} {\text{ + }}\sum\nolimits_{i = 0}^{n - 1} {{v_i}{2^i}} \\
&= \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i} + {v_i}} \right){2^i}} \\
&= \sum\nolimits_{i = 0}^{n - 1} {\left( {2 \times \left( {{u_i}\& {v_i}} \right) + \left( {{u_i} \wedge {v_i}} \right)} \right){2^i}} \\
&= 2\sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}\& {v_i}} \right){2^i}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i} \wedge {v_i}} \right){2^i}} \\
\end{align}\)
\(\begin{align}
\left\lfloor {\left( {x + y} \right)/2} \right\rfloor &= \left\lfloor {\sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}\& {v_i}} \right){2^i}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} } \right\rfloor \\
&= \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}\& {v_i}} \right){2^i}} + \sum\nolimits_{i = 1}^{n - 1} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} \\
\end{align}\)
上式用 C/C++语言可以表示为:
(x & y) + ((x ^ y) >> 1);
\(\begin{align}
x + y &= \sum\nolimits_{i = 0}^{n - 1} {{u_i}{2^i}} {\text{ + }}\sum\nolimits_{i = 0}^{n - 1} {{v_i}{2^i}} \\
&= \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i} + {v_i}} \right){2^i}} \\
&= \sum\nolimits_{i = 0}^{n - 1} {\left( {2 \times \left( {{u_i}|{v_i}} \right) - \left( {{u_i} \wedge {v_i}} \right)} \right){2^i}} \\
&= 2\sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}|{v_i}} \right){2^i}} - \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i} \wedge {v_i}} \right){2^i}} \\
\end{align}\)
\(\begin{align}
\left\lceil {\left( {x + y} \right)/2} \right\rceil &= \left\lceil {\sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}|{v_i}} \right){2^i}} - \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} } \right\rceil \\
&= \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}|{v_i}} \right){2^i}} - \sum\nolimits_{i = 1}^{n - 1} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} \\
\end{align}\)
上式用 C/C++ 语言可以表示为:
(x | y) - ((x ^ y) >> 1);
有符号整型
对于有符号整型, 设 \(x = - {u_{n - 1}}{2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {{u_i}{2^i}}\) 和 \(y = - {v_{n - 1}}{2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {{v_i}{2^i}}\), 其中 \(u_i,v_i\in\left\{ 0, 1 \right\}\).
\(\begin{align}
x + y &= - {u_{n - 1}}{2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {{u_i}{2^i}} - {v_{n - 1}}{2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {{v_i}{2^i}} \\
&= - \left( {{u_{n - 1}} + {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {{u_i} + {v_i}} \right){2^i}} \\
&= - \left( {2 \times \left( {{u_{n - 1}}\& {v_{n - 1}}} \right) + \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right)} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {2 \times \left( {{u_i}\& {v_i}} \right) + \left( {{u_i} \wedge {v_i}} \right)} \right){2^i}} \\
&= 2\left( { - \left( {{u_{n - 1}}\& {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}\& {v_i}} \right){2^i}} } \right) + \left( { - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {{u_i} \wedge {v_i}} \right){2^i}} } \right) \\
\end{align}\)
\(\begin{align}
\left\lfloor {\left( {x + y} \right)/2} \right\rfloor &= \left\lfloor {\left( { - \left( {{u_{n - 1}}\& {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}\& {v_i}} \right){2^i}} } \right) + \left( { - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right){2^{n - 2}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} } \right)} \right\rfloor \\
&= \left( { - \left( {{u_{n - 1}}\& {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}\& {v_i}} \right){2^i}} } \right) + \left( { - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right){2^{n - 2}} + \sum\nolimits_{i = 1}^{n - 2} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} } \right) \\
\end{align}\)
上式用 C/C++ 语言可以表示为:
(x & y) + ((x ^ y) >> 1);
\(\begin{align}
x + y &= - {u_{n - 1}}{2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {{u_i}{2^i}} - {v_{n - 1}}{2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {{v_i}{2^i}} \\
&= - \left( {{u_{n - 1}} + {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {{u_i} + {v_i}} \right){2^i}} \\
&= - \left( {2 \times \left( {{u_{n - 1}}|{v_{n - 1}}} \right) - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right)} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {2 \times \left( {{u_i}|{v_i}} \right) - \left( {{u_i} \wedge {v_i}} \right)} \right){2^i}} \\
&= 2\left( { - \left( {{u_{n - 1}}|{v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}|{v_i}} \right){2^i}} } \right) - \left( { - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {{u_i} \wedge {v_i}} \right){2^i}} } \right) \\
\end{align}\)
\(\begin{align}
\left\lceil {\left( {x + y} \right)/2} \right\rceil &= \left\lceil {\left( { - \left( {{u_{n - 1}}|{v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}|{v_i}} \right){2^i}} } \right) - \left( { - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right){2^{n - 2}} + \sum\nolimits_{i = 0}^{n - 2} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} } \right)} \right\rceil \\
&= \left( { - \left( {{u_{n - 1}}|{v_{n - 1}}} \right){2^{n - 1}} + \sum\nolimits_{i = 0}^{n - 1} {\left( {{u_i}|{v_i}} \right){2^i}} } \right) - \left( { - \left( {{u_{n - 1}} \wedge {v_{n - 1}}} \right){2^{n - 2}} + \sum\nolimits_{i = 1}^{n - 2} {\left( {{u_i} \wedge {v_i}} \right){2^{i - 1}}} } \right) \\
\end{align}\)
上式用 C/C++ 语言可以表示为:
(x | y) - ((x ^ y) >> 1);
综合
综合上面的分析, 可见对于有符号整型和无符号整型,
\(\left\lceil {\left( {x + y} \right)/2} \right\rceil\) 都可以用 C/C++ 语言表示为:
(x & y) + ((x ^ y) >> 1);
\(\left\lceil {\left( {x + y} \right)/2} \right\rceil\) 都可以用 C/C++ 语言表示为:
(x | y) - ((x ^ y) >> 1);
参考:
版权声明
版权声明:自由分享,保持署名-非商业用途-非衍生,知识共享3.0协议。
如果你对本文有疑问或建议,欢迎留言!转载请保留版权声明!
如果你觉得本文不错, 也可以用微信赞赏一下哈.

C/C++代码优化之求两个整型的平均值的更多相关文章
- swap 用指针交换两个整型数值
- c++作业:输入两个整数,用函数求两数之和。函数外部声明有什么作用?
#include <iostream> using namespace std; int main(){ //求两数的和? int a,b,s; cout<<"请你输 ...
- Php数据类型之整型详解
php中支持的数据类型 在php中主要支持8种数据类型.和3中伪类型的一个形式.8种数据类型分为以下三3大类,第一个就是我们的标量类型,标量类型它只能存储单一数据,那第二大类就是我们的复合类型,第三个 ...
- java 整型相除得到浮点型
public class TestFloatOrDouble { public static void main(String[] args) { Point num1 = new Point(84, ...
- python 函数求两个数的最大公约数和最小公倍数
1. 求最小公倍数的算法: 最小公倍数 = 两个整数的乘积 / 最大公约数 所以我们首先要求出两个整数的最大公约数, 求两个数的最大公约数思路如下: 2. 求最大公约数算法: 1. 整数A对整数 ...
- JavaScript求两个数字之间所有数字的和
这是在fcc上的中级算法中的第一题,拉出来的原因并不是因为有什么好说的,而是我刚看时以为是求两个数字的和, 很显然错了.我感觉自己的文字理解能力被严重鄙视了- -.故拉出来折腾折腾. 要求: 给你一个 ...
- [LeetCode] Intersection of Two Linked Lists 求两个链表的交点
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- 求两圆相交部分面积(C++)
已知两圆圆心坐标和半径,求相交部分面积: #include <iostream> using namespace std; #include<cmath> #include&l ...
- 面试问题2:给一个5G的大文件,保存的数据为32位的整型,找到所有出现次数超过两次的数字
问题描述:给一个5G的大文件,保存的数据为32位的整型,找到所有出现次数超过两次的数字 大数据操作: 解决方法一: 依次遍历文件数据, 开始32二进制清0 每次读取一个数,先和二进制位与,如果为0 则 ...
随机推荐
- zabbix分布式安装全过程
项目规划 软件 版本 IP zabbix-server 3.4.15 10.1.10.128 zabbix-proxy 3.4.15 10.1.10.129 zabbix-agent 3.4.15 1 ...
- Envoy 基础教程:使用 Unix Domain Socket(UDS) 与上游集群通信
Envoy Proxy 在大多数情况下都是作为 Sidecar 与应用部署在同一网络环境中,每个应用只需要与 Envoy(localhost)交互,不需要知道其他服务的地址.然而这并不是 Envoy ...
- centos8-django项目部署 nginx+uwsgi
1.虚拟环境virtualenv安装 1.安装virtualenv pip3 install virtualenv 2.创建目录,把项目文件传过来 mkdir My cd My 3.创建独立运行环境- ...
- Linux系统上LNMP服务器的搭建
一.确保登录用户权限为root 如果没有root权限: su root 切换到root用户,但不切换环境变量: 或者 su - root 完整地切换到root用户环境. 二.开始下载并安装LNMP( ...
- 【Hadoop离线基础总结】网站流量日志数据分析系统
目录 点击流数据模型 概述 点击流模型 网站流量分析 网站流量模型分析 网站流量来源 网站流量多维度细分 网站内容及导航分析 网站转化及漏斗分析 流量常见分析角度和指标分类 指标概述 指标分类 分析角 ...
- Mysql常用sql语句(13)- having 过滤分组结果集
测试必备的Mysql常用sql语句,每天敲一篇,每次敲三遍,每月一循环,全都可记住!! https://www.cnblogs.com/poloyy/category/1683347.html 前言 ...
- 在微服务框架Demo.MicroServer中添加SkyWalking+SkyApm-dotnet分布式链路追踪系统
1.APM工具的选取 Apm监测工具很多,这里选用网上比较火的一款Skywalking. Skywalking是一个应用性能监控(APM)系统,Skywalking分为服务端Oap.管理界面UI.以及 ...
- 透过面试题掌握HashMap【持续更新中】
本文主要是自己阅读了HashMap和ConcurrentHashMap源码及一些Java容器类相关的博客后,找了一些很多面经中涉及到的Java容器相关的面试题,自己全部手写的解答,也花了一些流程图,之 ...
- java套打
1:套打可能是以后软件开发可能会涉及到的功能,主要麻烦地方就是需要精确定位,光是打印发票还好,要是打印那种协议类型的特别麻烦,不仅长而且需要的数据多 ,定位麻烦. 2:而且大多数情况是需要去除页眉页脚 ...
- quartz 定时任务调度管理器
本项目使用的是spring-quartz 以下配置可以开启多个已知定时任务 <?xml version="1.0" encoding="UTF-8"?&g ...