不使用'/'的分部

时间:2011-03-22 03:21:32

标签: algorithm division

任何人都可以告诉我一种有效的方法来执行除法操作而不使用'/'。我可以使用类似于二分搜索的方法在log(n)步骤中计算整数值。

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

但还有其他更有效的方法吗?

16 个答案:

答案 0 :(得分:109)

典型的方法是移位和减法。这与我们在学校学到的长分工作基本相似。最大的区别在于,在十进制除法中,您需要估计结果的下一个数字。在二进制文件中,这是微不足道的。下一个数字始终为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。在十进制长除法中,我们执行以下操作:

 ____ 
5)972

然后我们分别计算每个数字。 5进入9一次,所以我们在答案的那个数字上写下1,并从被除数的那个数字中减去1 * 5,然后“降低”被除数的下一个数字:

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

我们继续这样做,直到我们填写了所有数字:

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

所以,我们的回答是194余数2。

现在让我们考虑相同的事情,但是在二进制中。二进制972是11 1100 1100,5是101。现在,在二进制与十进制之间进行除法之间存在一个根本区别:在十进制中,特定数字可以是从0到9的任何数字,因此我们必须乘以找到我们将从被除数中减去的中间结果。在二进制中,数字只会是0或1.我们永远不需要乘以因为我们只会乘以0或1(我们通常在if语句中处理 - 要么减去要么我们不减)。

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

因此,我们的第一步是找出结果中的第一个数字。我们通过比较101到1111001100来做到这一点,并将其向左移动直到它更大。这给了我们:

  |
 1111001100
10100000000

当我们这样做时,我们会计算我们移动的地方数量,以便我们知道在任何给定时间我们填写的结果的哪个数字。我已经通过上面的垂直条显示出来了。然后我们将中间结果右移一个位置,并将垂直条向右移动以表示我们在填写结果数字的位置:

    |
  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.将那些转换为十进制,我们分别得到预期的194和2。

让我们考虑一下如何与上面的代码相关联。我们首先将除数向左移动,直到它超过红利。然后我们反复右移并且每次右移检查该值是否小于我们在最后一次减法后得到的中间值。如果它更少,我们再次减去并在结果中填入1该数字。如果它更大,我们“减去0”(不做任何事情)并在结果中填入该数字的'0'(同样,这不需要我们做任何事情,因为这些数字已经设置为0的)。

当我们填写所有数字时,这就是我们的结果,剩下的还有我们尚未减去的数额是我们的余数。

有些人问我为什么在代码中使用|=代替+=。我希望这有助于解释原因。虽然在这种情况下它们会产生相同的结果,但我不认为将每个数字添加到现有的部分答案中。相反,我认为它在答案中的位置是空的,而or只是填充它。

答案 1 :(得分:11)

选项:

  • 根据您在小学时学到的长分算法编码您自己的分区算法。
  • 取分母的-1次幂,乘以分子
  • 取分子和分母的日志,减去,然后将日志的基数提升到相同的幂

我不是特别喜欢这样的问题,因为我们基本上都在寻找愚蠢的技巧,但我们就是这样。

答案 2 :(得分:5)

以下是不使用除法运算符来划分数字的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;
}

答案 3 :(得分:5)

使用基本高中数学的简单Python实现。分母只是负数1的数字。

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

答案 4 :(得分:4)

这是解决您不允许使用乘法的问题)。

我喜欢这个解决方案:https://stackoverflow.com/a/5387432/1008519,但我觉得有点难以推理(特别是| - 部分)。这个解决方案在我的头脑中更有意义:

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. 分母加倍(按位移)直到它大于被除数
  3. 既然我们知道我们的分母比我们的股息更大,我们可以减去除数,直到它小于股息
  4. 使用除数
  5. 返回所记录的操作以尽可能接近分母

    以下是一些测试运行:

    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

答案 5 :(得分:3)

由于OP说这是一个面试问题,我认为面试官除了你的编码技巧外还希望看到以下内容。 (假设您使用的是Java)

  1. 如何处理负数?将被除数和除数转换为正数是很常见的。但是,您可能会忘记Math.abs(Integer.MIN_VALUE)仍然是Integer.MIN_VALUE。因此,当被除数为Integer.MIN_VALUE时,您应该单独计算它。

  2. “Integer.MIN_VALUE / -1”的结果是什么? Integer中没有这样的值。你应该和面试官讨论一下。你可以为这种情况抛出异常。

  3. 以下是此问题的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;
    
    }
    

答案 6 :(得分:3)

主要概念:

假设我们是计算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;
}

答案 7 :(得分:3)

不使用/

划分两个数字
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;

}

答案 8 :(得分:2)

以下是不使用'/'运算符的简单除法方法: -

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

答案 9 :(得分:1)

这是JavaScript中的一个:

&#13;
&#13;
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))
&#13;
&#13;
&#13;

可以通过在精度的最后一个小数位后舍入来进一步改进。

答案 10 :(得分:0)

也许您可以使用&gt;&gt;序列设计一种方法。 (位移)与其他位运算符。 Wikipedia: Bitwise Operator文章中的psuedo-code中有一个示例。

答案 11 :(得分:0)

好吧,如果这只是整数/整数= int类型除法,通过添加n + n + n + n直到n大于x,很容易得到x / n = int.dec的整数部分,然后从你的'n'中减去一个。

要在不使用*,/,%或其他数学函数的情况下获得int / int = real,您可以做几件事。例如,您可以将余数作为理性返回。这具有精确的优点。您还可以使用字符串修改将您的r转换为r0 ...(您选择精度)然后重复相同的添加技巧,然后连接结果。

当然,你可以尝试通过比特转换获得乐趣。

我不知道这是不是一个'愚蠢的伎俩',因为它是一个测试你可以使用简单的东西(加法,减法)来构建一个复杂的东西(除法)。这是您的潜在雇主可能需要的技能,因为没有任何操作员。像这样的问题应该(理论上)清除那些无法设计算法的人。

我认为这是一个问题,答案在互联网上很容易获得,但这是一个实施问题。

答案 12 :(得分:0)

这是解决我问题的功能:

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

答案 13 :(得分:0)

如果你把除法作为一个减法,它基本上是什么,你可以使用一种方法&#34;减少&#34;什么允许你根本不使用任何运算符,除了〜最后,将结果反转为正整数或任何其他值。

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

答案 14 :(得分:-1)

好吧,让我们看看... x / y = e ^(ln(x)-ln(y))

答案 15 :(得分:-4)

    private int divideBy2(int number){

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

        if(count*2==number){

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

}