Time：2020-11-27

# preface

At the beginning of July, we challenged leetcode‘s question 29 (medium difficulty, which seems nothing to boast about). The problem requires that the function of integer division be realized without division, multiplication and modular operation.

Since the divisor and divisor are integers, subtraction can be used to achieve division (what a naive idea). A trivial function written in JavaScript can be as follows (for simplicity, only consider the case where both parameters are positive integers)

``````function divide(n, m) {
let acc = 0;
while (n >= m) {
n -= m;
acc += 1;
}
return acc;
}``````

So simple`divide`Function submission to leetcode is not accepted – it will time out on test cases like 2147483648 divided by 2. You can run it locally and feel how slow it is

``````➜  nodejs time node divide.js
2147483648/2=1073741824
node divide.js  1.14s user 0.01s system 99% cpu 1.161 total``````

Is there a faster algorithm to calculate the quotient of two integers? The answer, of course, is yes.

# Try to optimize

It can be seen at a glance that the most frequent runs are among them`while`Cycle. Take 2147483648 divided by 2,`while`Statements in the loop are executed 1073741824 times. In order to increase the running speed, the number of cycles must be reduced.

Since every time`n`Subtract from`m`It needs to be implemented`n/m`Times, then if you subtract each time`2m`No, it just needs to be executed`(n/m)/2`Have you done that? The number of cycles is reduced by half. I’m excited to think about it. Every time`2m`And the code and running effect of the algorithm are as follows

``````➜  nodejs cat divide2.js
function divide(n, m) {
let acc = 0;
Let M2 = m < < 1; // since multiplication is not allowed for the title, the left shift is used instead of multiplying by 2.
while (n >= m2) {
n -= m2;
acc += 2;
}
while (n >= m) {
n -= m;
acc += 1;
}
return acc;
}

console.log(`2147483648/2=\${divide(2147483648, 2)}`);
➜  nodejs time node divide2.js
2147483648/2=1073741824
node divide2.js  2.65s user 0.01s system 99% cpu 2.674 total``````

Although the time consumption does not decrease but rises, which makes the scene very embarrassing, according to theoretical analysis, the first cycle runs only half of the original number, while the second cycle runs at most once, which shows that the optimization direction is OK.

If the calculation`m2`The number of times to move left is 2`acc`The number of the first cycle will be greatly reduced to 268435456, and the number of the second cycle will not exceed 4; if the number of times of left shift is 3, then`acc`The number of the first cycle decreases to 134217728 and the number of the second cycle does not exceed 8.

Obviously, the left shift cannot go on indefinitely because`m2`Sooner or later, the value of`n`。 It’s easy to calculate that the upper limit of the number of left shifts is

Logarithmic notation means that even for very large`n`And very small ones`m`The result of the above formula is not very large, so it can significantly improve the efficiency of integer division.

Before I start to write the code, let me briefly prove the quotient and direct calculation of this method`n/m`They are equal.

# A simple proof

Count the number to be subtracted`n`, the subtraction is`m`。 Obviously, there is a positive integer`N`, so that

order

, and then

, so`n`divide`m`Equivalent to

The proof is complete.

From the above formula, we can also know that the new algorithm will have the original scale of 0`n`The problem is transformed into a scale of`r`This means that the final code can be written gracefully recursively.

# Complete code

Final`divide`The code for the function is as follows

``````function divide(n, m) {
if (n < m) {
return 0;
}

let n2 = n;
let N = 0;
//Use right shift instead of left shift to avoid overflow.
while ((n2 >> 1) > m) {
N += 1;
n2 = n2 >> 1;
}

//'power' denotes the nth power of 2 in the formula
//'product' represents the product of 'power' and the divisor'm '
let power = 1;
let product = m;
for (let i = 0; i < N; i++) {
power = power << 1;
product = product << 1;
}
return power + divide(n - product, m);
}``````

This is comparable to the beginning`divide`It’s much faster. There’s a picture, there’s a truth

``````➜  nodejs time node divide3.js
2147483648/2=1073741824
node divide3.js  0.03s user 0.01s system 95% cpu 0.044 total``````

# Postscript

If`T(n, m)`Indicates that the dividend is`n`, divisor is`m`Then its recursive formula can be written in the following form

But this thing doesn’t seem to be able to work out the analytic formula directly from the main theorem, so unfortunately, I don’t know what the time complexity of this algorithm is – though I guess it is`N`The calculation formula of.

If there is any kind reader friend who knows about it, I hope you can give me some advice.