数学计算模拟类问题:加法,除法和幂,注意越界问题。题 剑指Offer,Pow(x, n) ,Divide Two Integers

2023-11-17

数学计算的模拟类题目,往往是要求实现某种计算(比如两数相除),实现的过程中会有所限定,比如不允许乘法等等。

这类题目首先要注意计算过程中本身的特殊情况。比如求相除,则必须首先反映过来除数不能为0。

其次要记得考虑负数的情况,如果计算范围不单单是整数,还要考虑double的比较方式。

最后要注意越界情况,这个是最容易犯错的,只能具体问题具体分析。

例题 1 不用"+ - * / "做加法

 这道题来自于剑指Offer,为了归类,我把它放到了这里。


用位运算是第一反应,a^b可以算出当前位的结果, a&b可以算出是否进位。

但是接下来怎么办呢?

我的方法是,每次从两个数取一位,然后计算,进位。直到两个数都没有位可取了(往左看都只有0),最后做一个进位。

我的代码:

#include <stdio.h>

int Sum(int a, int b){
    int finalRes = 0;
    int count = 0;
    int prevAcc = 0, curAcc= 0;
    int p = 0, q = 0;
    int bitRes = 0;
    while(a | b){
        p = a&1; q = b&1;
        a = a >> 1; b = b >> 1;
        int temp = p ^ q;
        curAcc = p & q;
        bitRes = temp ^ prevAcc;
        prevAcc = curAcc | (temp&prevAcc);
        finalRes |= (bitRes << (count++));
    }
    finalRes |= prevAcc << count;
    return finalRes;
}

// ====================测试代码====================
void Test(int num1, int num2, int expected)
{
    int result = Sum(num1, num2);
    if(result == expected)
        printf("%d + %d is %d. Passed\n", num1, num2, result);
    else
        printf("%d + %d is %d. Failed\n", num1, num2, result);
}

int main()
{
    Test(1, 2, 3);
    Test(111, 899, 1010);

    Test(5, 2, 7);
    Test(1, 100, 101);

    Test(3, 0, 3);
    Test(0, 5, 5);

    Test(2, 8, 10);
}

白板一次error free bug free,但是,写到一半就明白这个解法是不全面的。。

因为没有考虑负数。

补码表示下的负数,并不能通过每次提取一位的方法来倒腾。

这种思路对于上次facebook的面试题倒是可以用,当时的题目是:两个只含有'0' '1'的字符串,计算两个字符串相加的结果。

在这种情况下,答案的解法确实让人佩服。

书上代码:

int Add(int num1, int num2)
{
    int sum, carry;
    do
    {
        sum = num1 ^ num2;
        carry = (num1 & num2) << 1;

        num1 = sum;
        num2 = carry;
    }
    while(num2 != 0);

    return num1;
}

整体计算,carry表示各个位上计算后的进位,将carry左移一位,再加上sum,就是要求的结果。如何算carry和sum相加的结果呢?

同样可以利用上述方法。

循环思路出现。

循环终止条件是carry为0,也就是说,不再有进位了。

 

引申:

顺便提一提怎样在不定义新的变量的情况下交换两个int的方法。假设要交换a和b。

四则运算法,这个比较熟悉了。

a = a + b;

b = a - b;

a = a - b;

位运算法,不常用,但是更简单:

a = a ^ b;

b = a ^ b;

a = a ^ b;



例题 2 Pow(x, n)

我们需要的考虑的边界情况是:

1. 底数为0,指数为负数的情况。

2. 底数为零0,指数为0的情况。

情况2就是求0的0次幂,这个算是没有意义,返回0或者1均可,但是需要告知面试官你考虑到了,最好能打印出来这个情况。

同样,求幂时,我们没有必要使用时间复杂度为O(N)的连乘方式,这里N等于exponent的绝对值。而是可以使用递归的方式。

因为当n为偶数时,a^n = a^(n/2) * a^(n/2);

当n为奇数时,a^n = a^((n-1)/2) * a^((n-1)/2) * a。

时间复杂度为O(logN)

最后,在判断底数是否为0时,不能使用base == 0的判定方式,因为double和float类型在存储时都有误差,判断两个数是否相等,只要两者之差小于一个极小值,即可认为两者相等。

代码分为三个函数,这样更加清晰:

bool equalJudgeDouble(const double a, const double b){
    if((a - b) < 0.00001 && (a - b) > -0.00001)
        return true;
    else
        return false;
}

double PowerWithUnsignedExponent(const double base, const unsigned int exp);
    if(exp == 0){
        return 1;
    }
    double temp = 0.0;
    if(exp & 1 == 0){
        temp = power(base, exp >> 1);
        return temp * temp;
    }else{
        temp = power(base, (exp-1) >> 1);
        return temp * temp * base;
    }
}

double Power(const double base, const int exponent){
    if(equalJudgeDouble(base, 0.0)){
        if(exponent < 0);
            throw new std::Exception("Can not work when exponent is negative and base is 0.");
        return 0;
    }
    if(exponent < 0){
        return 1.0 / PowerWithUnsignedExponent(base, (unsigned int)(-exponent));
    }else if(exponent == 0){
        return 1;
    }else{
        return PowerWithUnsignedExponent(base, exponent);
    }
}


例题 3 Divide Two Integers

Divide two integers without using multiplication, division and mod operator.

class Solution {
public:
    int divide(int dividend, int divisor) {
    }
};

这道题相较于前一道稍复杂些。首先考虑divisor为0的情况,再考虑负数的情况。

接着考虑解体方法,由于乘除都不能用,只能用加法,而如果直接累加自然会超时。我的思路是定义一个长32的数组path[32],path[0] = divisor, path[i] = path[i-1] + path[i-1]。path[32]不一定全被填满,当计算出path[i] > dividend时,path[i] 就不会被记录。由于path[i] 有大于 dividend的可能,因此临时存储计算结果的数定义为long long。

然后用这个path[32] 去凑成dividend,相除结果其实就是凑得过程中 1 << i 的相加(i 是 path[] 的 index)。

第一版代码如下:


class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor == 0) return 0;
        if(dividend == 0) return 0;
        
        bool minus1 = false, minus2 = false, minus = false;
        if(divisor < 0){
            divisor = (0 - divisor);
            minus1 = true;
        }
        if(dividend < 0){
            dividend = (0 - dividend);
            minus2 = true;
        }
        minus = (minus1 ^ minus2); //结果的正负号, minus若为true,结果就添加负号。
        
        if(dividend < divisor) return 0;
        long long cache = divisor;
        int* path = new int[32];
        int ind = 0;
        for(; cache <= dividend; path[ind] = (int)cache, ++ind, cache += cache); //填充path[]
        cache = path[--ind]; int res = 1 << ind; //从path的最末尾开始凑dividend
        while(ind >= 0){
            if(cache == dividend) return minus ? (0-res) : res;
            if(cache > dividend){
                cache -= path[ind];
                res -= 1 << ind;
            }
            for(--ind; ind >= 0 && cache < dividend; cache += path[ind], res += 1 << ind);
        }
        return minus ? (0-res) : res;
    }
};

这版代码在执行 sln.divide(-1010369383, -2147483648) 出现错误。

原因在于 开始的

dividend = (0 - dividend);

补码表示下,int下限绝对值比上限绝对值大1。dividend = -2147483648时,0-dividend 结果并非是2147483648。因此dividend 和 divisor的绝对值应该用unsigned int 表示。

考虑到这一点,当dividend = -2147483648 时,res 和 path 也有越过 int上限的可能,因此它们应该定义为 unisgned int。

改进后的代码,改动了一些参数的 格式,这次AC了。


class Solution {
public:
    int divide(int dividend, int divisor) {
        if(divisor == 0) return 0;
        if(dividend == 0) return 0;
        
        bool minus1 = false, minus2 = false, minus = false;
        unsigned int divd, divr;
        if(divisor < 0){
            divr = (0 - divisor);
            minus1 = true;
        }else{
            divr = divisor;
        }
        if(dividend < 0){
            divd = (0 - dividend);
            minus2 = true;
        }else{
            divd = dividend;
        }
        minus = (minus1 ^ minus2); //结果的正负号, minus若为true,结果就添加负号。
        
        if(divd < divr) return 0;
        long long cache = divr;
        unsigned int* path = new unsigned int[32];
        int ind = 0;
        for(; cache <= divd; path[ind] = (unsigned int)cache, ++ind, cache += cache); //填充path[]
        cache = path[--ind]; unsigned int res = 1 << ind; //从path的最末尾开始凑dividend
        while(ind >= 0){
            if(cache == divd) return minus ? (0-res) : res;
            if(cache > divd){
                cache -= path[ind];
                res -= 1 << ind;
            }
            for(--ind; ind >= 0 && cache < divd; cache += path[ind], res += 1 << ind);
        }
        return minus ? (0-res) : res;
    }
};


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

数学计算模拟类问题:加法,除法和幂,注意越界问题。题 剑指Offer,Pow(x, n) ,Divide Two Integers 的相关文章

  • 计算机如何根据人脸估计年龄,人脸图像算法研究(1)

    今天给大家带来一篇 人脸识别中的年龄估计技术 年龄特征作为人类的一种重要生物特征 计算机要如何基于人脸图像估计年龄呢 概述 简单地说 基于人脸图像的年龄估计是指机器根据面部图像推测出人的大概年龄或所属的年龄范围 年龄段 基于人脸图像的年龄估
  • 4.4.5 密码验证(2)

    4 当且仅当含数字和字母的密码验证 如果密码当且仅当包含数字和字母 那么该密码的强度是中等强度 当然 它的安全性一般 以下正则表达式能够验证当且仅当包含数字和字母的密码 da zA Z d a zA Z da zA Z 74 正则表达式 7
  • C++ 的四种类型转换

    背景 C语言中强制类型转换可以随意转换我们想要的类型 格式如下 类型 变量名 那么为什么C 还要引入新的4种类型转换呢 1 新的类型转换控制符可以很好的控制类型转换的过程 允许控制各种类型不同的转换 2 C 的类型转换控制服能告诉程序员或读
  • 【毕业设计】Python_学生校园消费行为

    资源下载 https download csdn net download wouderw 87357462 1 分析学校校园消费行为的目的 分析学生的消费行为和食堂的运营状况 为食堂运营提供建议 构建学生消费细分模型 为学校判定学生的经济
  • VT是什么?怎么打开教程

    装过虚拟机的朋友都知道 要想虚拟出cpu 就必须电脑打开VT VT指的是CPU的虚拟化技术 有了它就可以单CPU模拟多CPU并行 这样才可以虚拟出电脑出来 而如果你的bios没有打开VT的话 是不能创建虚拟机的 下面就教大家怎么打开VT 1
  • 华为员工自曝,工作四年,每天都哭想裸辞!

    架构师大咖 架构师大咖 打造有价值的架构师交流平台 分享架构师干货 教程 课程 资讯 架构师大咖 每日推送 公众号 该公众号已被封禁 进入华为是一项令人向往的机会 但它并不适合每个人 许多人都希望能够进入这家公司 但实际上 它要求员工具备卓
  • 加权回归估计_比率估计与回归估计

    本章讨论简单随机抽样和分层随机抽样下比率估计和回归估 计的构造及性质 要求 掌握总体比率 比率估计量及回归估计量的概念 了解比率估计量 回归估计量的偏倚 方差及方差的估计量 掌握应用比率估计量及回归估计量的条件 抽样调查从本质上看是利用不完
  • 蓝桥杯:拉马车

    目录 题目描述 输入描述 输入为 2 行 2 个串 分别表示 A B 双方初始手里的牌序列 我们约定 输入的串的长度不超过 30 输入输出样例 输入 输出 题目分析 列表 递归 AC代码 Java 题目描述 小的时候 你玩过纸牌游戏吗 有一
  • Linux最基础

    软件包安装的时候 会经常用到 tar zxvf Japan tar gz 如果要做LAMP环境的编译 建立一个LAMP源代码包构建的PHP生产环境 下载的包都是 tar gz 这样的源代码包 创建目录 mkdir 创建文件 touch 命令
  • Apache Commons Compress介绍-Zip压缩解压

    Apache Commons Compress介绍 Zip压缩解压 简述 为什么使用Apache Commons Compress 在使用java自带的ZipFile处理zip文件时报如下错误java lang IllegalArgumen
  • 安装virtualbox时安装程序出现严重错误

    问题 windows 8 1 下virtualbox安装到一半跳出错误窗口被迫终止 窗口如下 virtualbox install fail https img blog csdn net 20160622163138051 解决方法 在服
  • 从头走前端-百度前端技术学院(1)

    记录自己在网上自学加复习的前端笔记 当然还有一些其他涉及的相关知识 问题 在web建站技术中 HTML HTML5 XHTML CSS JavaScript PHP SQL web services是什么 答 首先知道网站的访问过程 1 输
  • navicat 绿化版

    navicat自带注册码 已经绿化 解压到任意目录就可运行 Navicat Premium 是一个可多重连接的数据库管理工具 它可让你以单一程序同时连接到 MySQL Oracle PostgreSQL SQLite 及 SQL Serve
  • 高性能IO模型浅析

    高性能IO模型浅析 服务器端编程经常需要构造高性能的IO模型 常见的IO模型有四种 1 同步阻塞IO Blocking IO 即传统的IO模型 2 同步非阻塞IO Non blocking IO 默认创建的socket都是阻塞的 非阻塞IO
  • Python retrying模块

    参见 http segmentfault com a 1190000004085023 github源码 https github com rholder retrying
  • QT信号槽连接方式

    1 QT信号槽主要分两个连接方式 手动和自动 1 1 使用 connect 函数手动连接信号和槽 QObject connect sender SIGNAL signal receiver SLOT slot 自动 1 2 使用 lambd
  • 模板类与函数

    模板类与函数 普通函数 参数和返回值是模板类的实例化版本 函数模板 参数和返回值是某种的模板类 函数模板 参数和返回值是任意类型 支持普通类和模板类和其它类型 模板类可以用于函数的参数和返回值 有三种形式 普通函数 参数和返回值是模板类的实
  • 运用Cookie技术,统计访问次数以及上次访问时间。

    package servlet import java io IOException import java io PrintWriter import java text SimpleDateFormat import java util
  • mysql已经配置且正确_mysql8 参考手册-Connector/J应用程序进行故障排除-1

    16 1 当我尝试使用MySQL Connector J连接到数据库时 出现以下异常 SQLException Server configuration denies access to data source SQLState 08001
  • 【Web自动化测试——代码篇】常用方法——切换

    方法总览 多表单切换 当一个页面存在frame iframe表单嵌套时 WebDriver却只能在一个页面上对元素识别定位 但是对于表单上的嵌套元素无法直接定位 这时候该怎么办呢 Java 1 package JavaTest 2 3 im

随机推荐

  • PAT乙级1042 字符统计 (20 分)

    1042 字符统计 20 分 一 问题描述 请编写程序 找出一段给定文字中出现最频繁的那个英文字母 输入格式 输入在一行中给出一个长度不超过 1000 的字符串 字符串由 ASCII 码表中任意可见字符及空格组成 至少包含 1 个英文字母
  • 23.07.12作业

    思维导图 计算题
  • Provider提供者模式与策略模式的比较

    在这篇文章Provider和Factory的区别 作者提到 可以往工厂里面添加Provider 也就是说Factory里面可能存在着许许多多的Provider 而这些Provider将是最后Factory创建出结果的必要支撑 可以理解为提供
  • 开启硬件加速 导致花屏问题 OpenGlRenderer 0x506 解决办法

    150114 17 08 32 461 I dalvikvm heap 850 Grow heap frag case to 10 342MB for 2457616 byte allocation 150114 17 08 32 542
  • Python实现基于朴素贝叶斯的垃圾邮件分类

    听说朴素贝叶斯在垃圾邮件分类的应用中效果很好 寻思朴素贝叶斯容易实现 就用python写了一个朴素贝叶斯模型下的垃圾邮件分类 在400封邮件 正常邮件与垃圾邮件各一半 的测试集中测试结果为分类准确率95 15 在仅仅统计词频计算概率的情况下
  • 解决Debian系统自动更新软件包的问题

    解决Debian系统自动更新软件包的问题 参考文章 1 解决Debian系统自动更新软件包的问题 2 https www cnblogs com nkqlhqc p 11978565 html 备忘一下
  • android添加依赖出现问题

    出现该问题unspecified on project app resolves to an APK archive which is not supported as a compilation dependency的情形可能是 创建了两
  • hduoj 2010

    水仙花数 Problem Description 春天是鲜花的季节 水仙花就是其中最迷人的代表 数学上有个水仙花数 他是这样定义的 水仙花数 是指一个三位数 它的各位数字的立方和等于其本身 比如 153 1 3 5 3 3 3 现在要求输出
  • 文件系统---代码层次深入分析文件系统

    文件系统对用户来说 最重要的就是创建目录 创建文件 打开文件 和 文件读写 对通常的硬盘文件系统来说 涉及硬盘的读写和硬盘空间管理 从读写文件系统层一直到通用设备层再到硬盘驱动 为了简化 我们给出最简单文件系统 通过这个例子导入文件系统的概
  • uniapp 在app和小程序端使用webview进行数据交互

    结论 app端支持比较好可以做到实时传递 微信小程序支持比较差 小程序向url传参只能通过url url向app传参需要特定时机 后退 组件销毁 分享 复制链接 触发才能收到消息 以下是代码 app端 需要使用nvue
  • 数组建堆(heapify)

    将一个数组调整为最大堆 根据堆的性质 只要保证部分有序即可 即根节点大于左右节点的值 将数组抽象为一个完全二叉树 所以只要从最后一个非叶子节点向前遍历每一个节点即可 如果当前节点比左右子树节点都大 则已经是一个最大堆 否则将当前节点与左右节
  • React中的组件以及组件实例的三大属性(详细类的复习)

    前言 我们为什么要模块化 是因为要复用编码 提高效率 react就是面向组件编程 函数式组件 函数不能中作为react的节点 就跟正常写函数一样 需要注意的是首字母需要大写 把函数封装为组件 所以把组件渲染到页面上时要使用组件的形式 因为开
  • Smart Tools 网站的架构之美

    本文将简要介绍Smart Tools工具箱网站的架构设计 带领大家一起领略架构之美 Smart Tools是一款实用的在线工具箱网站 地址 https smart tools cn 总体架构 Smart Tools工具箱网站是采用前后端分离
  • 为什么无法用数组名输出数组的的首地址

    当我们直接输出其他类型数组的数组名时 打印的都是一串地址 而字符数组打印的是字符串 为什么 因为字符串中 0 这个结束符 计算机可以知道在哪里读取结束 所以打印数组名就代表输出里面存储的字符串 其他类型没有结束符 计算机不知道从哪里停止 所
  • vue组件生命周期有哪些?vue2和vue3的生命周期图牢记于心,附面试题1

    单选题 关于vue组件生命周期说法错误的是 A 在data中的对象的某个属性和input双向绑定 修改input的值 在beforeDestroy中获取的值是修改过后的值 B ajax请求可以放在created钩子函数中 C 在create
  • Flutter每次进界面都刷新数据

    前言 需求 每次进去消息中心需要请求接口刷新数据 点击打开子界面返回也要请求数据改变状态 解决方法一 1 在initState方法加载数据 override void initState super initState 加载数据 loadD
  • git基础介绍与GitKraken操作简记

    刚开始尝试使用git的时候 简直是被弄得生不如死啊 写了半天的代码一下子被删了 那时候的心情简直想SHI 后来没办法 抽了点时间学习了一下git的原理 其实就是你的文件现在到底在什么状态 这么一个问题 由于只是了解 所以不知道具体的操作 具
  • Redis有效时间设置及时间过期处理

    本文对redis的过期处理机制做个简单的概述 让大家有个基本的认识 Redis中有个设置时间过期的功能 即对存储在redis数据库中的值可以设置一个过期时间 作为一个缓存数据库 这是非常实用的 如我们一般项目中的token或者一些登录信息
  • 打开PowerPoint提示:PowerPoint上次起送时失败。以安全模式启动PowperPoint将帮助您纠正或发现启动中的问题

    PowerPoint 无法打开 1 问题 PowerPoint 上次启动时失败 以安全模式启动PowerPoint 将帮助您纠正或发现启动中的问题 以便下一次成功启动应用程序 但是在这种模式下 一些功能被禁用 是否使用 安全模式 启动Pow
  • 数学计算模拟类问题:加法,除法和幂,注意越界问题。题 剑指Offer,Pow(x, n) ,Divide Two Integers

    数学计算的模拟类题目 往往是要求实现某种计算 比如两数相除 实现的过程中会有所限定 比如不允许乘法等等 这类题目首先要注意计算过程中本身的特殊情况 比如求相除 则必须首先反映过来除数不能为0 其次要记得考虑负数的情况 如果计算范围不单单是整