web-dev-qa-db-ja.com

「/」を使用しない除算

「/」を使用せずに除算操作を実行するための効率的なアプローチを教えてもらえますか。バイナリ検索に似た方法を使用して、log(n)ステップで整数値を計算できます。

115/3 
57 * 3 > 115
28 * 3 < 115
47 * 3 > 115
.
.
.
38 * 3 is quotient value .....

しかし、他のより効率的な方法はありますか?

46
broun

典型的な方法は、シフトして減算することです。これは基本的に、学校で学んだように、長い区分にかなり似ています。大きな違いは、10進数の除算では、結果の次の桁を推定する必要があることです。バイナリでは、それは簡単です。次の桁は常に0または1のいずれかです。(左シフト)除数が現在の被除数値以下である場合、それを減算し、結果の現在のビットは1です。それより大きい場合、結果の現在のビットは0です。コードは次のようになります。

unsigned divide(unsigned dividend, unsigned divisor) { 

    unsigned denom=divisor;
    unsigned current = 1;
    unsigned answer=0;

    if ( denom > dividend) 
        return 0;

    if ( denom == dividend)
        return 1;

    while (denom <= dividend) {
        denom <<= 1;
        current <<= 1;
    }

    denom >>= 1;
    current >>= 1;

    while (current!=0) {
        if ( dividend >= denom) {
            dividend -= denom;
            answer |= current;
        }
        current >>= 1;
        denom >>= 1;
    }    
    return answer;
}

これは、手で長い分割を行う場合とほとんど同じように機能します。たとえば、972/5を考えてみましょう。 10進数の長い除算では、次のようなことを行います。

 ____ 
5)972

次に、各桁を個別に計算します。 5は9になるので、答えのその桁に1を書き留め、配当の(その桁)から1 * 5を引き、次に配当の次の桁を「引き下げ」ます。

  1
 ----
5)972
  5
  ---
  47

すべての数字を入力するまで同じことを続けます。

   194
  ----
 5)972
   5
   ---
   47
   45
   ---
    22
    20
   ---
     2

したがって、答えは残り194です。

ここで、同じことを考えますが、バイナリで行います。 972のバイナリは11 1100 1100で、5は101です。現在、2進数と10進数の除算の間に1つの基本的な違いがあります。10進数では、特定の数字は0〜9のいずれかになります。バイナリでは、数字は0または1になります。0または1だけを乗算するため、乗算する必要はありません(通常ifステートメントで処理します-減算するかしないか)。

   -----------
101)1111001100

したがって、最初のステップは、結果の最初の桁がどれかを判断することです。 101と1111001100を比較し、大きくなるまで左にシフトします。それは私たちに与えます:

  |
 1111001100
10100000000

そのシフトを行う際に、シフトした場所の数を数えるので、結果のどの桁をいつでも埋めることができます。上記の垂直バーでそれを示しました。次に、中間結果を1桁右に移動し、垂直バーを右に移動して、結果の数字を入力する場所を示します。

    |
  1111001100
  1010000000

そこから、シフトされた除数が配当よりも小さいかどうかを確認します。そうであれば、答えの適切な場所に1を入力し、中間結果からシフトされた除数を引きます[そして、列をまっすぐに保つために、スペースを挿入します]:

            1  
     -----------------------------
  101)1  1  1  1  0  0  1  1  0  0
      1  0  1  0  0  0  0  0  0  0
      ----------------------------
         1  0  1

同じ方法で結果の桁を埋め、すべての桁を埋めるまで、シフトした除数を中間結果から減算します。物事を真っ直ぐにするためのさらなる試みとして、結果の各桁を減数のすぐ右に書きます。

            1  1  0  0  0  0  1  0
     -----------------------------
  101)1  1  1  1  0  0  1  1  0  0
      1  0  1                             1
      -----------------------------
         1  0  1
         1  0  1                           1
      -----------------------------
            0  0  0                          0
         --------------------------
               0  0  0                        0
          -------------------------
                  0  0  1                      0
          -------------------------
                     0  1  1                    0
          -------------------------
                        1  1  0   
                        1  0  1                   1
           ------------------------
                           0  1  0                 0

したがって、結果は11000010、剰余10になります。これらを10進数に変換すると、それぞれ194と2が期待されます。

それが上記のコードにどのように関係するかを考えてみましょう。除数が配当より大きくなるまで除数を左にシフトすることから始めます。次に、それを繰り返し右にシフトし、右シフトごとに、その値が最後の減算後に得た中間値よりも小さいかどうかをチェックします。少ない場合は、再度減算して、結果のその桁に1を入力します。大きい場合は、「0を減算」(何もしない)し、結果のその数字に「0」を入力します(これらの数字は既に設定されているため、何もする必要はありません) 0's)。

すべての数字を入力すると、それが結果になり、まだ減算していない残りの金額が残りになります。

コードで|=の代わりに+=を使用した理由を尋ねた人もいます。これが理由の説明に役立つことを願っています。この場合、同じ結果が得られますが、既存の部分的な回答に各桁を追加することは考えていません。むしろ、答えの中のスポットは空であると考えており、orはそれを埋めるだけです。

116
Jerry Coffin

オプション:

  • 小学校で学んだ長い除算アルゴリズムに基づいて、独自の除算アルゴリズムをコーディングします。
  • 分母の-1乗を取り、分子に乗算します
  • 分子と分母のログを取り、減算してから、ログの底を同じパワーに上げます

私は基本的に愚かなトリックを探しているので、このような質問は特に好きではありませんが、私たちはそこにいます。

13
Rob Lachlan

簡単なPython基本的な高校の数学を使用した実装。分母は単純に負の1のべき乗の数です。

def divide(a, b):
    return a * b ** -1
6
msbodw001

以下は、除算演算子を使用せずに数値を除算するためのJavaコードです。

private static int binaryDivide(int dividend, int divisor) {
    int current = 1;
    int denom = divisor;
    // This step is required to find the biggest current number which can be
    // divided with the number safely.
    while (denom <= dividend) {
        current <<= 1;
        denom <<= 1;
    }
    // Since we may have increased the denomitor more than dividend
    // thus we need to go back one shift, and same would apply for current.
    denom >>= 1;
    current >>= 1;
    int answer = 0;
    // Now deal with the smaller number.
    while (current != 0) {
        if (dividend >= denom) {
            dividend -= denom;
            answer |= current;
        }
        current >>= 1;
        denom >>= 1;
    }
    return answer;
}
5
Hemant

これは、乗算の使用も許可されていない問題の解決策です)。

私はこのソリューションが好きです: https://stackoverflow.com/a/5387432/1008519 、しかし、私はそれを推論するのが少し難しいと思います(特に|- part )。この解決策は私の頭の中でもう少し理にかなっています:

var divide = function (dividend, divisor) {
  // Handle 0 divisor
  if (divisor === 0) {
    return NaN;
  }

  // Handle negative numbers
  var isNegative = false;
  if (dividend < 0) {
    // Change sign
    dividend = ~dividend+1;
    isNegative = !isNegative;
  }

  if (divisor < 0) {
    // Change sign
    divisor = ~divisor+1;
    isNegative = !isNegative;
  }

  /**
   * Main algorithm
   */

  var result = 1;
  var denominator = divisor;
  // Double denominator value with bitwise shift until bigger than dividend
  while (dividend > denominator) {
    denominator <<= 1;
    result <<= 1;
  }

  // Subtract divisor value until denominator is smaller than dividend
  while (denominator > dividend) {
    denominator -= divisor;
    result -= 1;
  }

  // If one of dividend or divisor was negative, change sign of result
  if (isNegative) {
    result = ~result+1;
  }

  return result;
}
  1. 結果を1に初期化します(分母が配当よりも大きくなるまで分母を2倍にするため)
  2. 配当よりも大きくなるまで分母を(ビット単位のシフトで)2倍にします
  3. 分母が配当よりも大きいことがわかっているため、除数が配当よりも小さくなるまで除数を減算できます。
  4. 除数を使用して、分母にできるだけ近づけるためにとった記録されたアクションを返します

以下にいくつかのテスト実行を示します。

console.log(divide(-16, 3)); // -5
console.log(divide(16, 3)); // 5
console.log(divide(16, 33)); // 0
console.log(divide(16, 0)); // NaN
console.log(divide(384, 15)); // 25

0除数の場合と負の配当および/または除数の両方を処理する要点は次のとおりです: https://Gist.github.com/mlunoe/e34f14cff4d5c57dd90a5626266c4130

4
mlunoe

/を使用しない2つの数値の除算

int div(int a,int b){

    if(b == 0)
         return -1;   //undefined
    else if (b == 1)
         return a;    
    else if(b > 1){

       int count = 0;
       for(int i=b;i<=a;i+=b){
           count++;
       }
    }

    return count;

}
3

OPがインタビューの質問だと言ったので、インタビュアーはあなたのコーディングスキルに加えて次のことを見たいと思います。 (Javaを使用しているとします)

  1. 負の数に対処するには?配当と除数の両方を正の数に変換することは一般的です。ただし、Math.abs(Integer.MIN_VALUE)はまだInteger.MIN_VALUE。したがって、配当がInteger.MIN_VALUEの場合、個別に計算する必要があります。

  2. 「Integer.MIN_VALUE/-1」の結果は何ですか? Integerにはそのような値はありません。面接官と話し合う必要があります。この条件に対して例外をスローできます。

以下に、この質問のJavaコードを示します。検証できます leetcode:divide two integers

public int divide(int dividend, int divisor) {

    if(divisor == 0)
        throw new Exception("Zero as divisor!");

    int a = Math.abs(dividend);
    int b = Math.abs(divisor);

    boolean isPos = true;
    if(dividend < 0) isPos = !isPos;
    if(divisor < 0) isPos = !isPos;

    if(divisor == Integer.MIN_VALUE){

        if(dividend == Integer.MIN_VALUE) return 1;
        else return 0;
    }

    if(dividend == Integer.MIN_VALUE) {

        if(divisor == -1){

            // the result is out of Integer's range.
            throw new Exception("Invalid result.");
        } else {
            // Because Math.abs(Integer.MIN_VALUE) = Integer.MIN_VALUE
            // we avoid it by adding a positive divisor to Integer.MIN_VALUE
            // here I combined two cases: divisor > 0 and divisor < 0
            return divide((dividend + b), divisor) - divisor/b;
        }
    }

    int res = 0;        
    int product = b;

    while(a >= b){

        int multiplier = 1;
        while(a - product >= product){

            product = product << 1;// "product << 1" is actually "product * 2"
            multiplier = multiplier << 1;
        }
        res += multiplier;
        a -= product;
        product = b;
    }

    return isPos?res:-res;

}
3
ChuanRocks

主なコンセプト:

私たちがcalc 20/4、 そう

4*(1+1) = 8 *(1+1) = 16 *(1+1) == 32 (which is bigger) X
so go back to 16 and try 16*(1+0.5) == 24 (bigger) X
so go back to 16 and try 16*(1+0.25) == 20 

コード:

float product=1,multiplier=2,a=1;
int steps=0;

void divCore(float number, float divideBy,float lastDivison)
{
    steps++;
    //epsilon check e.g (10/3) will never ends
    if(number - divideBy < 0.01)
        return;
    else
    {
        lastDivison = divideBy; 
        divideBy *= multiplier;
        if(number >= divideBy)
        {
            product *= multiplier;
            divCore(number,divideBy,lastDivison);
        }
        else
        {
            a *= 0.5;
            multiplier = 1 + a;
            divCore(number,lastDivison,lastDivison);
        }
    }
}

float Divide(float numerator, float denominator)
{
    //init data
    int neg=(numerator<0)?-1:1;
    neg*=(denominator<0)?-1:1;
    product = 1;
    multiplier = 2;
    a = 1;
    steps =0;
    divCore(abs(numerator),abs(denominator),0);
    return product*neg;
}
3
Zaza

「/」演算子を使用しないintの単純な除算方法を次に示します。

public static int divide(int numerator, int denominator) throws Exception {

    int q = 0;
    boolean isNumPos = (numerator >= 0) ? true : false;
    boolean isDenPos = (denominator >= 0) ? true : false;

    if (denominator == 0) throw new Exception("Divide by 0: not an integer result");

    numerator = Math.abs(numerator);
    denominator = Math.abs(denominator);

    while (denominator <= numerator) {
        numerator -= denominator;
        q++;
    }

    return (isNumPos ^ isDenPos) ? -q : q;
}
2
Mrinal Shukla

JavaScriptの1つを次に示します。

function divideWithoutDivision(a, b, precision) {
    precision = precision > 0 ? precision : 10

    var result = 0
    var decimalPosition = 1
    var A = a*0.1
    var howManyTimes = 0

    while (precision--) {
        A = A * 10
        howManyTimes = 0

        while (A >= b) {
            A = A - b
            howManyTimes += 1
        }

        result = result + howManyTimes*decimalPosition
        decimalPosition = decimalPosition * 0.1
    }

    return result
}

document.write('<br>20/3 = ', divideWithoutDivision(20, 3))
document.write('<br>10/3 = ', divideWithoutDivision(10, 3))
document.write('<br>10/4 = ', divideWithoutDivision(10, 4))
document.write('<br>17/14 = ', divideWithoutDivision(17, 14))
document.write('<br>23/4 = ', divideWithoutDivision(23, 4))

精度の最後の小数点以下を丸めることにより、さらに改善できます。

1
trusktr

おそらく、>>(ビットシフト)のシーケンスを他のビット演算子と組み合わせて使用​​する方法を考案できます。 Wikipedia:Bitwise Operator の記事にpsuedo-codeの例があります。

0
Jim Dennis

除算を減算として取る場合、基本的には、「デクリメント」メソッドを使用して、最後の〜を除き、演算子をまったく使用しないようにして、後で正の整数に結果を反転させることができます。その他の値。

private static int decrement(int i) {

    System.out.println("Value of decrement : ");
    System.out.println(i);
    return i - 1;
}

private static int divide(int n, int d) {

    assert n > 0 && d > 0;
    int counter = 0;
    while (n >= d) {
        for (int i = d; i > 0; i = decrement(i)) {

            n = decrement(n);
        }
        counter = decrement(counter);

    }
    counter  =~decrement(counter);
    System.out.println(counter);
    return counter;
}
0
grinsekatz007

これは私の問題を解決した機能です:

func printRemainderAndQuotient(numerator: Int,divisor: Int) {
  var multiplier = 0
  var differene = numerator - divisor
  var dynamicNumber = 0
  if divisor == 0 {
    print("invalid divisor")
    return
  }
  if divisor == numerator {
    print("quotient : " + "1")
    print("remainder : " + "0")
    return
  }
  while differene >= divisor {
    multiplier = multiplier + 1
    dynamicNumber = divisor * multiplier
    differene = numerator - dynamicNumber
  }
  print("quotient : " + "\(multiplier)")
  print("remainder : " + "\(differene)")
}
0
siva k

さて、これが整数/整数= int型の除算のみの場合、nがxより大きくなるまでn + n + n + nを追加してからx/n = int.decの整数部分を取得するのは非常に簡単ですあなたの「n」カウント。

*、/、%、またはその他の数学関数を使用せずにint/int = realを取得するには、いくつかのことができます。たとえば、残りを有理数として返すことができます。それには正確であるという利点があります。また、文字列変更を使用してrをr0 ...に変換し(精度を選択)、同じ追加トリックを繰り返して、結果を連結することもできます。

そしてもちろん、ビットシフトを楽しんでみることができます。

単純なもの(加算、減算)を使用して複雑なもの(除算)を構築できるかどうかのテストであるため、これが「愚かなトリック」であるかどうかはわかりません。これはあなたの潜在的な雇用者が必要とするかもしれないスキルです、すべてのためのオペレーターがいないので。このような質問は、(理論的には)アルゴリズムを設計できない人をできる人から除外するべきです。

答えがインターネット上ですぐに入手できるのは問題だと思いますが、それは実装の問題です。

0
philosodad