开发者

Division without using '/'

开发者 https://www.devze.com 2023-02-19 07:56 出处:网络
Can anyone tell me an efficient approach to perform the divisio开发者_JAVA百科n operation without using \'/\'. I can calculate the integer value in log(n) steps using a method similar to binary search

Can anyone tell me an efficient approach to perform the divisio开发者_JAVA百科n operation without using '/'. I can calculate the integer value in log(n) steps using a method similar to binary search.

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

But is there any other more efficient method?


The typical way is to shift and subtract. This is basically pretty similar to long division as we learned it in school. The big difference is that in decimal division you need to estimate the next digit of the result. In binary, that's trivial. The next digit is always either 0 or 1. If the (left-shifted) divisor is less than or equal to the current dividend value, you subtract it, and the current bit of the result is a 1. If it's greater, then the current bit of the result is a 0. Code looks like this:

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;
}

This works pretty much like when we do long division by hand. For example, let's consider 972/5. In decimal long division, we do something like this:

 ____ 
5)972

Then we figure each digit individually. 5 goes into 9 once, so we write down a 1 in that digit of the answer, and subtract 1*5 from (that digit) of the dividend, then "bring down" the next digit of the dividend:

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

We continue doing the same until we've filled in all the digits:

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

So, our answer is 194 remainder 2.

Now let's consider the same thing, but in binary. 972 in binary is 11 1100 1100, and 5 is 101. Now there is one fundamental difference between doing the division in binary vs. decimal: in decimal a particular digit could be anything from 0 to 9, so we had to multiply to find the intermediate result we were going to subtract from the dividend. In binary the digit is only ever going to be a 0 or a 1. We never need to multiply because we would only ever multiply by 0 or 1 (which we normally handle in an if statement--either we subtract or we don't).

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

So, our first step is to figure out which will be the first digit in the result. We do that by comparing 101 to 1111001100, and shifting it left until it's greater. That gives us:

  |
 1111001100
10100000000

As we do that shifting, we count the number of places we've shifted so we know which digit of the result we're filling in at any given time. I've shown that with the vertical bar above. Then we shift the intermediate result right one place, and shift the vertical bar right with it to signify where we're doing to fill in a result digit:

    |
  1111001100
  1010000000

From there we check if the shifted divisor is less than the dividend. If it is, we fill in a 1 in the proper place in the answer, and subtract the shifted divisor from the intermediate result [and to help keep columns straight, I'm going to insert some spaces]:

            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

We continue the same way, filling in digits of the result, and subtracting the shifted divisor from the intermediate result until we've filled in all the digits. In a further attempt at helping keep things straight, I'm going to write in each digit of the result at the far right next to the subtrahend:

            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

So, we get a result of 11000010, remainder 10. Converting those to decimal, we get the expected 194 and 2 respectively.

Let's consider how that relates to the code above. We start by shifting the divisor left until it's greater than the dividend. We then repeatedly shift it right and for each right shift check whether that value is less than the intermediate we got after the last subtraction. If it's less, we subtract again and fill in a 1 for that digit in our result. If it's greater, we "subtract 0" (don't do anything) and fill in a '0' for that digit in the result (which, again, doesn't require us to do anything, since those digits are already set to 0's).

When we've filled in all the digits, that's our result, and any amount left that we haven't subtracted yet is our remainder.

Some have asked why I used |= instead of += in the code. I hope this helps explain why. Although in this case they produce the same result, I don't think of adding each digit to the existing partial answer. Rather, I think of it that spot in the answer as being empty, and the or just fills it in.


Options:

  • Code your own division algorithm based on the long division algorithm you learned in grade school.
  • Take the -1 power of the denominator, and multiply onto the numerator
  • Take the logs of the numerator and denominator, subtract, and then raise the base of the log to that same power


Simple Python implementation using basic high school math. A denominator is simply a number to the power of negative 1.

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


Following is the Java code for dividing number without using division operator.

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;
}


(This is a solution to the problem where you are not allowed to use multiplication either).

I like this solution: https://stackoverflow.com/a/5387432/1008519, but I find it somewhat hard to reason about (especially the |-part). This solution makes a little more sense in my head:

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. Initialize the result to 1 (since we are going to double our denominator until it is bigger than the dividend)
  2. Double the denominator (with bitwise shifts) until it is bigger than the dividend
  3. Since we know our denominator is bigger than our dividend, we can subtract the divisor until it is less than the dividend
  4. Return the recorded actions it took to get as close to the denominator as possible using the divisor

Here are some test runs:

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

Here is a gist handling both the 0 divisor case and negative dividend and/or divisor: https://gist.github.com/mlunoe/e34f14cff4d5c57dd90a5626266c4130


Since the OP said it's an interview question, I think the interviewer wants to see the following things in addition to your coding skills. (Suppose you are using Java)

  1. How to deal with negative numbers? It's common to convert both the dividend and the divisor to positive numbers. However, you may forget that Math.abs(Integer.MIN_VALUE) is still Integer.MIN_VALUE. Therefore, when the dividend is Integer.MIN_VALUE, you should calculate it separately.

  2. What's the result of "Integer.MIN_VALUE/-1"? There is no such value in Integer. You should discuss it with the interviewer. You can throw an exception for this condition.

Here is the Java code for this question and you can validate it 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;

}


The main concept :

Let's say we are calc 20/4, so

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 

The code:

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;
}


Division of two numbers without using /

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;

}


Here is a simple divide method for ints without using a '/' operator:-

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;
}


Here's one in JavaScript:

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))

It could be further improved by rounding after the last decimal place of the precision.


Perhaps you can devise a way to do it using sequences of >> (bit shifts) with other bitwise operators. There's an example in psuedo-code in the Wikipedia: Bitwise Operator article.


Well, if this is only integer/integer = int type division, it's pretty easy to get the integer part of x / n = int.dec by adding n+n+n+n until n is greater than x, then subtracting one from your 'n' count.

To get int/int = real without using *, /, %, or other math functions, you could do several things. You could return the remainder as a rational, for example. That has the advantage of being exact. You could also use string modification to turn your r into r0... (you pick the precision) and then repeat the same addition trick, then concatenate the results.

And of course, you could try having fun with bit shifting.

I don't know if this is so much a 'silly trick' as it is a test of how well you can use simple things (addition, subtraction) to build a complex thing (division). This is a skill that your potential employer might need, because there isn't an operator for everything. A question like this should (theoretically) weed out people who can't design algorithms from people who can.

I do think it's a problem that the answer is so readily available on the internet, but that's an implementation issue.


This is the function that solved my problem:

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)")
}


If you take the division as a subtraction, what it basically is, you could use a method "decrement" what allows you to not use any operator at all, except for ~ at the end, to invert the result later into a positive integer or any other value.

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;
}


well, let's see... x/y = e^(ln(x)-ln(y))


    private int divideBy2(int number){

    int count = 1;
    while(count<=number){

        if(count*2==number){

            return count;
        }
        count++;
    }
    return count;

}
0

精彩评论

暂无评论...
验证码 换一张
取 消