数值计算

1. Divide Two Integers

思路: 由于不可以用*, /, %运算,我们只能用+, -还有移位运算。首先,如果我们用减法,依次从被除数中减掉除数,那么是O(N)的时间复杂度。那么有没有更快的方法吗?

我们试着用 “基”的思想来试一下:假设我们需要计算63 / 5:

  • 首先,我们找到最大的基:5*1, 5*2, 5*4, 5*8, 5*16(超过),因此我们选5 * 8为最大的基;
  • 我们从最大的基开始依次降低,如果加到和 ( sum ) 里小于等于被除数,则加入进去(8(ok), 4(ok), 2(no), 1(no));
  • 在上步进行的同时,我们记录我们加了多少进去(8 + 4),得到的就是“商”(12)。

这样的时间复杂度是O(logN).
代码如下:

class Solution {
public:
    int divide(int dividend, int divisor) {
        if (dividend == INT_MIN && divisor == -1) return INT_MAX;
        if (divisor < 0) {
            if (divisor == INT_MIN) {
                return dividend == INT_MIN ? 1 : 0;
            }
            return -divide(dividend, -divisor);
        }

        if (dividend < 0) {
            if (dividend == INT_MIN) {
                dividend += divisor;
                return -1 - divide(-dividend, divisor);
            }
            return -divide(-dividend, divisor);
        }

        //both are positives
        int e = 1;
        //while ((divisor << e) <= dividend) {  //find the biggest base
        //    ++e;
        //}
        while (dividend >> e >= divisor) ++e;  //find the biggest base
        --e;
        int rs = 0;
        for ( ; e >= 0; --e) {
            if (dividend - (divisor<<e) >= 0) {
                rs += 1 << e;
                dividend -= (divisor<<e);
            }
        }
        return rs;
    }
};

注意,注释掉的那段

//while ((divisor << e) <= dividend) {  //find the biggest base
        //    ++e;
        //}

这段代码会造成死循环,当然也会越界,因为在向右移位的时候,会突变为INT_MIN。因此我们采用被除数向右移位,知道比除数还小的方法去找最大基。

2. Pow(x, n)

此道题和上题的处理思路类似,但更简单一些。我们把n用二进制表示b[i],同时记录下来相应的x的幂。如x^5, 5的二进制表示为[1, 0, 1], 我们记录下来x^1, x^2, x^4的值,然后根据二进制表示决定其需不需要乘到rs里面去。
用cache记录x的幂次的代码:

class Solution {
public:
    double pow(double x, int n) {
        // get n's binary representation, and spead up. 
        // cache x^0, x^1, x^2, x^4 if binary representation is 1, else cache 1.0
        if (n == INT_MIN) return 1.0 / (pow(x, INT_MAX) * x);
        if (n < 0) return 1.0 / pow(x, -n);
        vector<double> cache;
        cache.push_back(1.0);

        double x_pow = x;
        while (n > 0) {
            int now = n % 2;
            if (now == 0) 
                cache.push_back(1.0);
            else
                cache.push_back(x_pow);
            n /= 2;
            x_pow *= x_pow;
        }

        double rs = 1.0;
        for (int i = 0; i < cache.size(); ++i)
            rs *= cache[i];
        return rs;        
    }
};

其实我们不需要用cache,因为我们只需要记住当前的幂次就好。
没有用cache的代码:

class Solution {
public:
    double pow(double x, int n) {
        // get n's binary representation, and spead up. 
        // record x^0, x^1, x^2, x^4 if binary representation is 1
        if (n == INT_MIN) return 1.0 / (pow(x, INT_MAX) * x);
        if (n < 0) return 1.0 / pow(x, -n);

        double rs = 1.0;
        double x_pow = x;
        while (n > 0) {
            int now = n % 2;
            if (now != 0) 
                rs *= x_pow;
            n /= 2;
            x_pow *= x_pow;
        }

        return rs;
    }
};

还有要注意的就是当负数转为正数的时候,记得考虑INT_MIN的情况。

3. Sqrt(x)

我们可以利用二分法,指定l, r来二分地试出来,但是会超时。
这里我们采用牛顿迭代法。
具体方式为:我们用牛顿迭代法来找到方程x^2 - a = 0的正实根。假如,根(我们想要的结果)为x0,那么此处的导数(切线斜率)为2*x0,切线方程为 y = 2*x0*x - x0^2 - a,与x轴的交点为x = x0 / 2 + a / (2 * x0)。我们就拿这个新的交点作为下次迭代的输入x0,来进一步迭代,直至方程x^2 - a 近似等于0.

class Solution {
public:
    int mySqrt(int x) {
        // 牛顿迭代法
        if (x < 0) return INT_MIN;
        if (x == 0) return 0;

        const int a = x;
        const double threshold = 0.1;
        double x0 = x;  //solution
        double dis = INT_MAX;

        while (dis > threshold) {  // get the zero point of plot: y = x^2 - a; a is passed argument x, 
                                   // which means the solution we want is the solution of x^2 - a = 0
            x0 = (x0 + a / x0) / 2;
            dis = abs(x0*x0 - a);
        }
        return x0;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值