CSAPP习题思考(位操作)

2023-11-12

CSAPP习题思考(位操作) 

现在发现写技术方面的文章真是不容易,不像写随感文,随便热血一下两三个小时就出来了。这篇文章至少用了5、6个小时,但依然感觉没写到位,很多想说的却写不出来。想和说(写)是两种境界,所以每次看pongba洋洋散散五六千字,看着不累而且有趣,其背后的心血和深厚沉淀都不是一时半会儿练就的。

记得以前哪个牛人说过:“如何把一个问题解释给一个毫无技术背景但同样很聪明的人听,让别人理解,这才代表自己真正理解了问题。”所以Joel把写作列为优秀程序员的必备素养之一。写作的过程就像和心灵对话,不断在找自己的茬(各种论证的弱点、表述的清晰性、思维过程的总结等等),然后慢慢提高。

言归正传,最近在做《Computer Systems: A Programmer’s Perspective》(CSAPP)的习题,有些题目做得还是挺吐血的,但题目总体颇具思考价值,对增加知识的理解很有帮助。举一些有意思的,主题是位操作。

1) 比较两数是否相等

可用操作:! ~ & ^ | + << >>

最多5步

可以想到判断两数相等就是判断两数的每个位是否相等,所以想个方法来检测每个位是关键。又想到位操作中异或操作其实就是判断两个位是否相等的,即0^0=0, 0^1=1, 1^0=1, 1^1=0。于是只要将两个数异或一下,然后如果为0则是相等,反之则不相等。最后要把结果颠倒下,因为函数要求返回为1是相等,0则不相等。于是得到

 
 
int isEqual(int x, int y) 
{
	return !(x ^ y);
}

2) 逻辑右移n位

可用操作:~ & ^ | + << >>

最多16步

可以想到右移有两种情况:1)负数,右移前n位都为1。2)正数,右移前n位都为0。对于负数如何把前n位置为0就成了问题所在。可以想到如果有一个数前n位为0,后32-n位为1,再把它和移位后的x与一下就是结果了。

于是,我想到做这样一个掩码让它的符号位为1,再右移n位,于是得到一个前n位为1,而后32-n位为0的数,再把这个数取反,就能满足条件了。

 
 
int logicalShift(int x, int n) 
{
	int mask = ~((1 << 31) >> n);   int mask = ~((1 << 31) >> (n -1));
	return (x >> n) & mask;
}

3)如果数中含有1的个数为奇数则返回1,否则返回0

事例:bitParity(5)=0,bitParity(7)=1

可用操作:! ~ & ^ + << >>

最多20步

最容易想到的就是线性的方法了,检查每一个位,为1则累加1。最后检查累加值最低位(奇数的判断)是1,则输出1,否则返回0。但可以看到这样的话需要检查的32步,再加上累加操作的32步,总共64步。大于20步。

经过上述思考,可以发现用线性的方法极有可能是要超出20步的。那么最容易降低复杂度的就是O(logN)的算法。鉴于此,我想到可以用二分的方法试一下。那么,怎么用二分呢?再仔细一看,由于检查的是含有1是否为奇数,并没有要求计算总的数量,所以是否可以用一个操作符来判断奇偶。

怎样的操作数可以判断奇偶,于是想到xor由于xor在两数都不相同时为1,所以多个位作xor操作,得出为1则说明有奇数个1。并且利用二分的思想分解成两个16位->两个8位……->两个1位,如此不断xor。就得出了最后答案。

 
 
int bitParity(int x)
{
	int ret = (x >> 16) ^ x;
	ret = (ret >> 8) ^ ret;
	ret = (ret >> 4) ^ ret;
	ret = (ret >> 2) ^ ret;
	ret = (ret >> 1) ^ ret;
 
 
	return ret & 1;
}

4)完成!x运算,且不使用!运算符

事例:bang(3)=0,bang(0)=1

可用操作:~ & ^ | + << >>

最多12步

首先,这里的!运算其实就是判断这个数是否为0,!0=1其余都为0由于只能使用位操作,所以不能直接x==0来输出。而且最多12步,所以线性的32步的位判断是没出路的。这里最初的想法还是用二分来减少运算。

想到因为0的编码位都为0,所以只要检测出有一位1的就可以得出结果了。那么判断是否有一个位为1就成了解决问题的关键。怎么解决呢?想到了或运算可以保持1的存在。所以似乎觉得可以用或,那么如何结合二分呢?想到了这样一幅图,解决32位中是否有1,其实可以分解成两个16位是否有1,一个16位又可以分解成两个8位……如此不断,就到了两个1位,那最后不就只剩下1位了嘛。

1

这里的核心关键是只要有一位存在1,我们就找出答案了,所以可以使用或操作。于是,想到可以把每个位都互相或一下,答案是1的话就说明有一位存在1。这里就用到了二分思想,把32位分成两部分或一下,再把结果分成8位或一下,最后只剩下一位。可以看出每位的互相或一下和这边的二分之后或其实是等价的。

另外最开始,我设定了一个掩码(mask)把A前部分的不需要的去掉,mask = ~((1 << 31) >> 15),即mask = 0000….11111,A = (x >> 16) & mask,这样A的前16位为0,对B=x & mask。而后发现,这样做会超过步数。仔细分析,其实我们关注的只是那16位,对于不需要的部分其实也不用去掉,于是得到以下代码:

 
 
int bang(int x) 
{
	int ret = (x >> 16) | x;
	ret = (ret >> 8) | ret;
	ret = (ret >> 4) | ret;
	ret = (ret >> 2) | ret;
	ret = (ret >> 1) | ret;
 
 
	return (~ret) & 1;
}

用了正好12步,还是很惊险。

5)判断加法是否溢出

事例:addOK(0x80000000, 0x80000000)=0, addOK(0x80000000, 0x70000000)=1

可用操作:! ~ & ^ | + << >>

最多20步

想破头的一题,开始的对溢出的理解错误。如下:

以下为初始的错误想法:

所谓溢出,我想到的就是溢出到了第33位,那么问题就变成了如何判断相加后第33位是否为1的问题。这里,我再次想到了二分的问题。考虑一个小的问题,比如两个16位的数相加怎么判断溢出,我想只要把它们放到两个32位的数中然后,判断第17位就行了,这个问题很容易。那么现在问题扩大了一倍,而且我手头不能用64位去存储32位数的变量,怎么办呢?所谓二分就是减小问题,不如把32位的数拆成两半,如图:

2

根据加法原理,数从低位加到高位,那么把x和y拆成两部分,判断低16位是很容易的,在把第17位放到高16位(sx2和sy2)的加法运算中,判断是否溢出就可以解决了。

代码如下:

 
 
int addOK(int x, int y) 
{
	int mask = ~((1 << 31) >> 15); //0000....1111
 
 
	int sx = x & mask; //sx1
	int sy = y & mask; //sy1
	int sum = sx + sy;
 
 
	sum >>= 16; // get the 16th bit, sum = 0 or 1, check wethter sx1 + sy1 is overflow(16bit)
 
 
	sx = ((x >> 16) & mask); //sx2
	sy = ((y >> 16) & mask); //sy2
	
	sum += sx + sy;
 
 
	return (sum >> 16);
}
 
 

正解:

而后发现一个简单的反例:0xFF,FF,FF,FF(-1) + 0xFF,FF,FF,FF(-1)=-2,显然没有溢出,但按我的算法是溢出的。错误的想法其实对原码是正确的,但补码的机制不是这样。于是找了好几个例子来看,终于算是搞清了溢出的机制。观察两个正负数相加是肯定不会溢出的,溢出的情况有两种:1)两个负数得正数。2)两个正数得负数。也就是问题可以转换为检查初始两个加数的符号位和运算结果的符号位。

  

于是,我得出两种情况是非法的x和y的符号位1,1得出的符号位0。以及x和y的符号位0,0得出的符号位1。问题是怎么判断,这个时候想念到if的好了,没有if的日子真是非常难过啊,还有那些个||和&&运算符。经过无数的尝试,终于想到一个关键的地方:用两个加数的符号位作为开关。也就是这样一个形式:? & 开关。?代表的应该是加数的符号位和结果的符号位的一些运算。说实话,这道题我是在不断地尝试,至于其背后的机制为什么会想到,现在都感觉像是暴力搜索一样,只是感觉上应该是那样。代码如下:

 
 
int addOK(int x, int y) 
{
	int sign = ~((x ^ y) >> 31); //提取符号位,相同为1,不同为0
	int result = x + y; 
	int xSign = (x >> 31) ^ (result >> 31); //用某一个加数的符号位去xor结果的符号位,为什么只用一个加数而不考虑另一个,因为开关在这里起了一个重要的作用可以忽略这样的情况而保证答案的正确性
	return !(xSign & sign & 1); // 取出最后一位且取反
}
 
 

以上的一些题都用到了二分的方法,虽然二分的具体实现方法不同,但本质的思想。想到用二分的原因有两点。一者,之前看《编程珠玑》时,作者用二分很优美的解决了一些问题。于是二分在印象中比较深刻。二者,这些问题最初的考虑都是用线性方法,也就是最简单的方法,但由于步数的限制必须降低算法复杂度。由O(n)自然会考虑用O(logn)来降低,于是考虑二分就比较自然了。以上只是练习的一部分,还有一些比较类似的就不说了。

再者,列出一些至今还没有想到办法解决的,当然暴力的线性总可以解决,但始终觉得不够优美。

1) 找出最低位1的位置。

事例:leastBitPos(96)=0x20

可用操作:! ~ & ^ | + << >>

最多30步

只想到了暴力方法,完全没头绪。

2) 计算数中为1的位的个数

事例:bitCount(5)=2, bitCount(7)=3

可用操作:! ~ & ^ | + << >>

最多40步

考虑用xor结合and的方法,但细节方面的二分或者其他具体实现还是想不明白。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CSAPP习题思考(位操作) 的相关文章

  • NAT穿透解决方案介绍

    最近公司要实现在各种网络环境下面的多屏互动 机顶盒 android phone iphone及PC端 的需求 由于IP地址资源有限的原因 目前我们使用的各种终端设备都位于局域网后面也就是多台设备共享同一个公网IP 例如 如果位于局域网里面的
  • Html源代码加密?

    什么是Html源代码加密 使用JavaScript加密转化技术将Html变为密文 以此保护html源代码 这便是Html源码加密 同时 这种加密技术还可实现网页反调试 防复制 链接加密等功能 应用场景 什么情况下需要Html源代码加密 Ht
  • SpringBoot下swagger3.0的配置

    SpringBoot下swagger3 0的配置 1 swagger3 0依赖 2 swagger配置类 3 我的application yml配置 4 访问地址 5 Swagger注解说明 1 swagger3 0依赖
  • 初学者必看!我的第一个Invideo人工智能文字生成视频

    这是一个使用人工智能生成视频的在线平台 主要功能包括 视频脚本自动生成 可以通过输入主题 由AI自动生成视频故事剧本 人声合成 支持上传脚本 AI会合成自然的人声进行朗读 视频制作 有多种视频模板可选择 支持上传自己的素材 一键生成完整视频
  • 三十多岁的我,为了生活转行Java,开始我的小白之路!

    看你35岁要从体制里出来学java 而且看样子已经下定决心了 我真的替你感到悲哀 我也是java培训出来 转行到互联网的 所以我觉得我可以回答这个问题 跟我一起培训的同学大部分也还在做 我们这些人有的是24岁刚毕业出来的 有的是毕业两年三年
  • C#程序用Settings读取和保存参数

    C 程序用Settings读取和保存参数 通常比较大型的程序开发时 需要读取和保存许多用户设置的参数 比如数据文件夹路径 程序界面的颜色 字体名称 大小等 这些信息怎么能够方便的进行设置和保存呢 在C 开发程序时 可以用系统自带的Setti
  • 智能化工作流程,工作效率开始“狂飙”!|Parabola

    随着 AI 功能的日益强大 能够帮忙人们解决的工作问题越来越多 这也不可避免地引发了一场工作效率革命 尤其助力智能化工作流程的建立 Parabola Parabola是一款强大的自动化处理工具 能够帮助用户轻松地进行数据处理 转换和分析而无
  • Java有哪些自定义异常处理方式

    在Java中 异常是一种常见的处理机制 当程序运行出现错误时 Java会默认抛出一个异常 并通过栈回溯信息提供错误详情 从而让开发人员知道程序何时 为什么以及在哪里发生异常 然而 这仅仅是Java内置异常处理的一部分 Java也提供了许多自
  • Unity使用Xcode将项目打包成IPA

    Unity是个开放性的平台 打包时也可以选择多种打包类型 几乎包含了所有的平台 目前主流Android iOS平台 Android平台可以直接使用Unity自行打包 但iOS平台需要借助Mac电脑进行打包 本博客就iOS打包进行一个简单的说
  • C++知识点总结(三)

    1 什么是二叉搜索树 二叉搜索树又称二叉排序树 它或者是一棵空树 或者是具有以下性质的二叉树 若它的左子树不为空 则左子树上所有节点的值都小于根节点的值 若它的右子树不为空 则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜
  • [转] Hyper-V Cluster Shared Volume(叢集共用磁碟區)原理初探

    原文地址 http www dotblogs com tw daniel07793 archive 2012 05 19 72265 aspx 這篇是要來解說Hyepr V在運用Cluster Shared Volume的一些原理 可以先參
  • 【牛客讨论区】第三章:开发社区核心功能

    目录 1 过滤敏感词 2 发布帖子 2 1 dao 2 2 service 2 3 controller 3 帖子详情 4 事务管理 5 显示评论 5 1 实体类 5 2 dao 5 3 service 5 4 controller 6 添
  • 详细讲解SpringBoot快速入门

    https blog csdn net m0 37106742 article details 64438892
  • Open3D(C++) 读取、可视化并保存点云

    目录 一 主要函数 1 读取点云 1 1 从文件中读取点云 1 2 从扩展名中读取点云 2 保存点云 2 1 直接保存 2 2 根据扩展名保存 3 显示点云 二 代码实现 包括读取txt格式 1 读取常见点云 2 读取txt格式的点云 三
  • CRMEB Pro2.0多门店版商城系统源码

    CRMEB Pro2 0多门店版商城系统源码 支持公众号 H5 小程序 PC 模板需另行购买 APP 首页DIY id 665343205554
  • 云服务器怎么弄mac系统,mac系统在云服务器

    mac系统在云服务器 内容精选 换一换 本地Windows操作系统主机 推荐使用 方法1 使用RDP文件登录在控制台单击 远程登录 下载RDP文件至本地 运行RDP文件 输入密码 密钥鉴权方式请先获取登录密码 登录远程桌面 详细操作请参考使
  • 扫盲系列(4):数据仓库ETL流程和ETL工具推荐

    目录 1 数据抽取 2 数据转换 3 数据加载 4 数据仓库ETL工具推荐 结构化数据ETL工具 非结构化 半结构化数据ETL工具 1 数据抽取 数据源是指存储数据的源头 包括结构化数据 半结构化数据 非结构化数据等 1 结构化数据 可以采
  • 一款经典的ThinkPhp6开发的CMS内容管理系统

    项目介绍 一款 PHP 语言基于 ThinkPhp6 x Layui MySQL等框架精心打造的一款模块化 插件化 高性能的前后端分离架构敏捷开发框架 可用于快速搭建前后端分离后台管理系统 本着简化开发 提升开发效率的初衷 框架自研了一套个
  • Oracle下载安装:

    一 Oracle老版本11g下载地址 https www oracle com cn database technologies microsoft windows html 二 安装 解压到同一目录 在解压文件夹中找到可执行文件 setu
  • angular快速入门教程

    angular 安装 1 安装node js 2 安装angular npm i g angular cli 3 创建项目 ng new 项目名 4 文件结构 e2e 测试目录 src index html 网站主页面 main ts 应用

随机推荐

  • React:主题切换

    效果图 useTheme import useState useEffect useRef from react const useTheme active theme gt const LOCAL KEY theme 如果浏览器存有 LO
  • VBA的常见语法整理

    1 for循环 2 字符串连接符 Dim i For i 1 To 10 Cells i 2 第 i 行 Next i 3 设定必须显式声明变量 Option Explicit 4 while循环 Dim j j 1 While Cells
  • GD32单片机USB HID模式连续发送多包数据,出现丢包现象

    项目背景 产品使用GD32F103单片机实现USB HID模式 周期发送数据包 在特定情况下 需要连续发送三包数据 测试发现 只接收到了最后一包数据 前面的数据丢失了 故障排查 原发送函数如下 再调用custom hid report se
  • volatile、ReentrantLock和synchronized保证线程可见性原理

    主存 工作内存 在了解什么是线程可见性前 我们先来简单了解下 Java内存模型 的主存 工作内存抽象概念 主存 存储的是一些共享资源的存储位置 例如静态变量等 工作内存 每个线程对应的栈内存对应的私有局部资源的存储位置 我们来分析一个小案例
  • 浅识:元组、字典和集合

    目录 一 轻量性列表 元组 一 了解 元组 二 简单的元组操作 三 可哈希对象 二 映射类型 字典 一 了解 字典 二 字典的操作 三 无序可变序列 集合 一 了解集合 二 集合操作与运算 一 轻量性列表 元组 一 了解 元组 列表的功能十
  • Mac下jdk的安装路径

    http hi baidu com liouyan9 blog item 78fdc009b97bdac63ac76377 html Mac下jdk的安装路径 2009 08 11 15 39 苹果系统已经包含完整的J2SE 其中就有JDK
  • Spring Cloud微服务技术栈学习(导读)

    目录标题 前言 微服务架构解决方案 什么是spring cloud 技术组件 概念区分 1 spring cloud alibaba与spring cloud netflix 2 微服务技术之间的关系 3 springcloud是通过htt
  • 单调栈理解

    文章目录 单调栈 什么是单调栈 模拟实现一个单调栈 一些例题 视野总和 下一个最大元素 单调栈 什么是单调栈 从名字上就听的出来 单调栈中存放的数据应该是有序的 所以单调栈也分为单调递增栈和单调递减栈 单调递增栈 单调递增栈就是从栈底到栈顶
  • 常见猫咪种类

    文章目录 中华田园猫 猫图秀 概况 产地血统 毛色特征 形态特征 性格特征 近种区别 饲养特点 适养人群 英短 猫图秀 概况 产地血统 毛色特征 形态特征 性格特征 近种区别 饲养特点 适养人群 美短 猫图秀 概况 产地血统 毛色特征 形态
  • 双目标定(一)单目标定与矫正的基本介绍

    1 单目相机标定 首先 任何标定都是用基于小孔模型的数学模型去近似相机模型 我们需要用fx f dx fy f dy 图像坐标系中的光心原点坐标 和可能的缩放因子ks 这5个相机内参数 切向畸变参数和径向畸变参数 共5 N个参数来 近似 整
  • iOS 图片处理学习: 实现点九切图

    先来一个例子 一张图片 保留中间 拉伸两边 看效果 原始图片 easy 处理后 调用代码 view backgroundColor UIColor white let imgViewWidth CGFloat 300 let imgView
  • Gvim插件

    plugin 编程相关 公共 taglist 相信无人不知其大名 用来提供单个源代码文件的函数列表之类的功能 最近在使用一个针对面向对象语言的类似插件 tagbar vim 也很不错 NERD commenter 提供快速注释 反注释代码块
  • DeepChem预测小分子溶解度

    在药开发中 根据化学式预测小分子的溶解度 是开发药物小分子时要考虑的非常重要的性质 如果一种药物的溶解度不够 你可能无法将足够的药物输送到患者的血液中产生治疗效果 我们需要的第一件事是一个真实分子的测量溶解度的数据集 DeepChem的核心
  • [ctfshow]入门2

    目录 web14 默认配置 web15 社会工程学 web16 探针泄露 web17 sql备份 wed18 js小游戏 web19 数据库泄露 web20 mbd文件 杂项5 杂项6 杂项6 杂项7 杂项8 杂项10 杂项11 隐写1 隐
  • feign 序列化_自定义 feign 反序列化时间字符格式

    feign client 默认配置类 默认的配置类为FeignClientsConfiguration 配置了解码和编码 当请求Feign Client的方法执行时会被 SynchronousMethodHandler 类中的 invoke
  • 网络带宽单位转换 — MB/s、Mb/s、Mbps、Mbit/s、Kbps

    1 bit s 和 bps 的区别 bit s 和 bps 都是一样的意思 bit per second 2 KB s 和 Kb s 的区别 大写 B 和小写 b KB s 和 Kb s 的意思不一样 KB s 中的 大 B 表示 Byte
  • 通配符的应用

    我们使用通配符描述切入点 主要的目的就是简化之前的配置 具体都有哪些通配符可以使用 单个独立的任意符号 可以独立出现 也可以作为前缀或者后缀的匹配符出现 execution public com itheima UserService fi
  • Wireshark 解密https 数据

    默认情况下 wireshark 抓到的https 数据包都是加密后的 无法展示明文内容 方式一 SSLKEYLOGFILE 变量方式 推荐 适用各种情况 配置环境变量 浏览器在访问https 站点的时候会检测这个SSLKEYLOGFILE
  • java反射field.setAccessible()方法作用

    Accessable属性是继承自AccessibleObject 类 功能是启用或禁用安全检查 JDK API中的解释 引用AccessibleObject 类是 Field Method 和 Constructor 对象的基类 它提供了将
  • CSAPP习题思考(位操作)

    CSAPP习题思考 位操作 现在发现写技术方面的文章真是不容易 不像写随感文 随便热血一下两三个小时就出来了 这篇文章至少用了5 6个小时 但依然感觉没写到位 很多想说的却写不出来 想和说 写 是两种境界 所以每次看pongba洋洋散散五六