Divide Two Integers不使用乘除法來計算兩個數相除

DoWhatIWant_comeon發表於2017-06-04
  • 題目描述
    這裡寫圖片描述

  • 分析
    不使用乘除法來計算兩個數相除,那就從除法的定義出發:商表示的是被除數裡包含了多少個除數。那最直接的一種辦法,就是迴圈減除數,累加次數,直到不能再減。這麼粗暴的辦法,很容易會超時,舉個極端的例子,若被除數是INT_MAX即2147483647,除數是1,那麼就得減2147483647次,程式才能執行結束。

    所以,我們應該每次減多一點,我第一想到的便是位操作。因為,位操作可以看成是特殊的乘除法,如向左移一位,便是翻倍(即乘2);向右移一位,便是減半(即除2)。舉個簡單的例子來對照看,3的二進位制是11,6的二進位制是110,12的二進位制是1100,…

    所以,

    核心思想
    被除數
    =除數商*
    =除數(2^0 * q0 + 2^1 q1 + 2^2 *q2 + 2^3*q3 + ….. + 2^n*qn)
    其中,q0~qn的取值要麼為1,要麼為0.

    舉例1,
    75
    =5*15
    =5*(2^0 *1 + 2^1 *1 + 2^2 *1 + 2^3*1)

    舉例2,
    15
    =3*5
    =3*(2^0 *1 + 2^1 *0 + 2^2 *1)

    程式碼的編寫思路如下,
    1、確定n值。
    每次翻倍的累加除數(1,2,4,8,16,…倍),看直到多少倍(n),除數會大於被除數,同時記下除數被累加的次數和當前的移位數(即倍數)。
    2、確定q0~q(n-1)的值,確認是0還是1,同時更新累加次數
    3、根據除數和被除數的符號,確定商的符號

  • 程式碼實現

int divide(int dividend, int divisor) {
    //溢位判定
    if(divisor==0 || (dividend==INT_MIN && divisor==-1))
        return INT_MAX;

    //符號確定
    bool sign=0;
    if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
       sign=1;
    }

    long ldividend=labs(dividend);
    long div=labs(divisor);
    long count=1;
    int bits=0;  //bits為移的位數

    //確定指數級翻倍的最大翻倍數(多項式的次數)
    while(div<=ldividend){
       count=count<<1;
       div=div<<1;
       bits++;
    }
    div=div>>1;
    bits--;
    count=count>>1;

    //確定多項式形式的其他位的值(是1還是0)
    int i;
    long copyd;
    for(i=bits-1;i>=0;i--){
        copyd=labs(divisor);
        div=div+(copyd<<i);    //<<的優先順序小於+,要加括號
        //除數可以再加上copyd<<i,也即可以再累加上1<<i次
        if(div<=ldividend){
            count=count+(1<<i);
        }
        //不能加,嘗試失敗,除數的值還原到剛才未加時候的值
        else{
            copyd=labs(divisor);
            div=div-(copyd<<i);
        }
    }

    if(sign)
        return count;
    else
        return -count;
}
  • 遺留現象
    1、上述程式碼是在以C++的形式下通過的,若以C的形式是過不了的。

    2、另外一個奇怪的現象是,上述程式碼在eclipse上執行,有些測試集的結果會和leetcode執行的不一樣。
    這裡寫圖片描述圖1:leetcode程式設計環境下的測試截圖

    這裡寫圖片描述
    圖2:eclipse C++的測試截圖

    圖1和圖2使用的是相同的程式碼,見上文。然而測試結果不一樣。在eclipse中debug會發現,即使是使用labs或者fabs先強制轉換型別後來取絕對值(long temp=(long)dividend; long ldividend=labs(temp);),並使用long或double型來儲存,-2147483648的絕對值依然是-2147483648,這也就是造成結果不對的原因。那為什麼在leetcode的程式設計環境下執行就不會出現這種問題呢?我暫時還不清楚,知道的親歡迎留言告訴我。

  • 後續
    在文章的開頭,我說到直接單純的一次一次的累加,可能會超時。那如果減少一半的累加次數,還會超時嗎?於是我便寫了下面的程式碼,與上文程式碼不同的地方是在確定指數級翻倍的最大翻倍數之後,採取的是簡單的逐次迴圈累加統計次數。

    下面的程式碼提交,就有時候會Accept,有時候會提示Time limit Exceeded。所以,最好還是用上文程式碼的方法,一定不會超時。

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

    bool sign=0;
    if((dividend>0&&divisor>0)||(dividend<0&&divisor<0)){
       sign=1;
    }

    long ldividend=labs(dividend);
    long div=labs(divisor);
    long count=1;
    int bits=0;  //bits為移的位數

    if(div>ldividend)
        return 0;

    while(div<=ldividend){
       count=count<<1;
       div=div<<1;
       bits++;
    }

    div=div>>1;
    bits--;
    count=count>>1;

    long copyd=labs(divisor);;
    while(div<=ldividend){
       div=div+copyd;
       count++;
    }
    if(div!=ldividend)
        count--;

    if(sign)
        return count;
    else
        return -count;
}

};

相關文章