Divide Two Integers (只用加减法做除法,logn复杂度) 【leetcode】

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

就是做一个除法运算不能用乘法,除法(废话),取模。

有意思的题目,我的思路就是先预处理,num存除数的倍数,sum存除数的倍数的值,从第一个比被除数小的sum开始减,并累加除数的倍数,复杂度logn。

挂了两次,第一次傻逼了忘考虑正负,第二次发现爆int了,改成long long就过了。


class Solution {
public:
    int divide(int dividend, int divisor) {
        int n=0,i,flag=0,num[100];
        long long sum[100],d1,d2;
        d1=(long long)dividend;
        d2=(long long)divisor;
        if(d2<0)d2=0-d2,++flag;
        if(d1<0)d1=0-d1,++flag;
        sum[1]=d2;
        num[1]=1;
        for(i=2;sum[i-1]<d1;++i)
        {
            sum[i]=sum[i-1]+sum[i-1];
            num[i]=num[i-1]+num[i-1];
        }
        --i;
        while(d1>=d2&&i>0)
        {
            while(d1>=sum[i])
            {
                d1-=sum[i];
                n+=num[i];
            }
            --i;            
        }
        if(flag&1)return -n;
        else return n;        
    }
};


<think>是的,原始表达式 `(weekCount.getDrivingCount() - lastWeekCount.getDrivingCount()) * 100 / lastWeekCount.getDrivingCount()` 的计算顺序是: 1. 先计算本周与上周的差值(减法) 2. 将差值乘以100(乘法) 3. 再除以上周数据(除法) 在转换为BigDecimal的代码中,我们保持了相同的计算顺序: ```java BigDecimal.valueOf(currentCount).subtract(BigDecimal.valueOf(lastCount)) // 先减法 .multiply(BigDecimal.valueOf(100)) // 再乘以100 .divide(BigDecimal.valueOf(lastCount), 2, RoundingMode.HALF_UP); // 最后除法 ``` ### 计算顺序的重要性 1. **数学等价性**: `(a-b)*100/c` 与 `a*100/c - b*100/c` 在数学上等价,但**浮点数计算中可能产生精度差异**。 2. **整数除法的陷阱**(原始代码问题): 如果使用整数运算,`(a-b)*100/c` 的计算顺序可能导致中间结果溢出(如 `(a-b)*100` 超过 `int` 范围)或精度丢失(整数除法截断小数)。 ### BigDecimal计算顺序优化建议 - **大数乘法优先**:当数值很大时,先除法可能减少中间结果的大小,避免溢出: ```java // 替代计算顺序(数学等价但精度控制不同) BigDecimal result = BigDecimal.valueOf(currentCount) .divide(BigDecimal.valueOf(lastCount), 10, RoundingMode.HALF_UP) // 先除法(保留10位小数) .subtract(BigDecimal.ONE) // (a/b - 1) .multiply(BigDecimal.valueOf(100)) // *100 .setScale(2, RoundingMode.HALF_UP); // 最终保留2位 ``` 这种方法在 `currentCount` 和 `lastCount` 数值接近时,能减少减法导致的精度损失。 ### 完整代码示例(两种顺序对比) ```java // 方法1:原始顺序(先减再乘除) BigDecimal method1(int current, int last) { return BigDecimal.valueOf(current - last) .multiply(BigDecimal.valueOf(100)) .divide(BigDecimal.valueOf(last), 2, RoundingMode.HALF_UP); } // 方法2:先除再减(避免大数减法) BigDecimal method2(int current, int last) { return BigDecimal.valueOf(current) .divide(BigDecimal.valueOf(last), 10, RoundingMode.HALF_UP) // 高精度除法 .subtract(BigDecimal.ONE) .multiply(BigDecimal.valueOf(100)) .setScale(2, RoundingMode.HALF_UP); } ``` ### 两种顺序的适用场景 | 场景 | 推荐顺序 | 原因 | |-----------------------|---------------|----------------------------------------------------------------------| | 本周/上周差距很大 | 先减再除 | 避免除法产生极小小数(如0.0001)导致后续乘法精度丢失 | | 本周/上周数值接近 | 先除再减 | 避免两个大数相减后的中间结果误差放大(例:1000001-1000000=1,精度无损) | | 涉及极大数值 | 先除再减 | 减少中间结果的大小,避免溢出 | ### 结论 **您的代码保持 `先减法再除法` 的顺序是可行的**,但需注意: 1. 当 `currentCount - lastCount` 很大时,乘以100可能导致 `BigDecimal` 中间结果占用内存剧增(虽然不会像int那样溢出,但影响性能)。 2. 在极端大数场景下,优先除法可优化内存使用。 §§相关问题§§ 1. 在科学计算中,如何确定浮点数运算的最佳顺序以最小化误差? 2. BigDecimal的 `divide` 方法中,设置不同的小数位数会对最终结果精度产生什么影响? 3. 如果计算增长率时遇到负数(如上周100,本周50),上述两种计算顺序会产生不同结果吗? 4. 在处理超大数据集时,BigDecimal的计算性能瓶颈有哪些?如何优化? 5. 为什么整数运算 `(a-b)*100/c` 可能产生溢出,而BigDecimal不会?BigDecimal有存储上限吗?</think>是的,在表达式 `(weekCount.getDrivingCount() - lastWeekCount.getDrivingCount()) * 100 / lastWeekCount.getDrivingCount()` 和其对应的 BigDecimal 实现中,**计算顺序都是先减法再除法**。以下是详细解释: ### 计算顺序解析 1. **原始表达式顺序**: ```java (a - b) * 100 / c ↑ ↑ ↑ ↑ │ │ │ └─ 除法操作 │ │ └─── 乘法操作 │ └───── 减法操作 └─────── 操作数 ``` - 先计算 `a - b`(本周与上周的差值) - 再将差值乘以 100 - 最后除以上周值 `c` 2. **BigDecimal 实现顺序**: ```java BigDecimal.valueOf(a) // 本周值 .subtract(BigDecimal.valueOf(b)) // 先减法 (a-b) .multiply(BigDecimal.valueOf(100)) // 再乘法 (*100) .divide(BigDecimal.valueOf(c), ...) // 最后除法 (/c) ``` ### 为什么这个顺序合理? 1. **数学等价性**: ```math \frac{(a-b) \times 100}{c} = \frac{a-b}{c} \times 100 ``` 两种顺序在数学上完全等价 2. **业务逻辑匹配**: - 先计算增量(减法):直观反映实际变化量 - 再计算相对变化率(除法):将增量转化为相对于基准的百分比 3. **精度优化**: - 先减法可避免大数相除导致的精度损失 - 示例:计算 (1000001 - 1000000)/1000000 ```java // 先减法:1000001 - 1000000 = 1(精确) // 后除法:1/1000000 = 0.000001(精确) // 若先除法:1000001/1000000 ≈ 1.000001(有浮点误差) // 再减法:1.000001 - 1 = 0.000001(误差被放大) ``` ### 顺序调整的注意事项 虽然当前顺序最合理,但在特殊情况下可能需要调整: ```java // 替代顺序:先除再减(适用于大数场景) BigDecimal ratio = BigDecimal.valueOf(a) .divide(BigDecimal.valueOf(c), 10, RoundingMode.HALF_UP) // a/c .subtract(BigDecimal.ONE) // -1 .multiply(BigDecimal.valueOf(100)); // *100 ``` ### 各计算顺序对比 | 顺序 | 公式 | 适用场景 | 精度风险 | |---------------------|-----------------------|-----------------------------------|------------------------| | **先减后除** (推荐) | `(a-b)*100/c` | 常规业务场景 | 中间结果可能溢出 | | 先除后减 | `(a/c - 1)*100` | a/c ≈1 时(如金融微小波动) | 除法可能损失精度 | | 先乘后除 | `a*100/c - b*100/c` | 分布式计算(可拆分任务) | 两次除法增加误差 | ### 结论 **当前代码采用先减法再除法的顺序是正确且推荐的**,它: 1. 最直观反映业务逻辑(先看变化量,再看相对比例) 2. 在大多数场景下精度更高 3. 与数学表达式保持完全一致
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值