Quelqu'un peut-il me dire une approche efficace pour effectuer l'opération de division sans utiliser '/'. Je peux calculer la valeur entière en étapes log(n)
en utilisant une méthode similaire à la recherche binaire.
115/3
57 * 3 > 115
28 * 3 < 115
47 * 3 > 115
.
.
.
38 * 3 is quotient value .....
Mais existe-t-il une autre méthode plus efficace?
La manière typique est de décaler et de soustraire. Ceci est fondamentalement assez similaire à la division longue telle que nous l’avons apprise à l’école. La grande différence est que dans la division décimale, vous devez estimer le prochain chiffre du résultat. En binaire, c'est trivial. Le chiffre suivant est toujours égal à 0 ou 1. Si le diviseur (décalé à gauche) est inférieur ou égal à la valeur du dividende actuel, vous le soustrayez et le bit actuel du résultat est 1. Si le diviseur est supérieur à 1, alors le Le bit actuel du résultat est un 0. Le code ressemble à ceci:
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;
}
Cela fonctionne à peu près comme lorsque nous effectuons une longue division à la main. Par exemple, considérons 972/5. Dans la division longue décimale, nous faisons quelque chose comme ceci:
____
5)972
Ensuite, nous figurons chaque chiffre individuellement. 5 entre en 9 une fois, nous écrivons donc un 1 dans ce chiffre de la réponse et soustrayons 1 * 5 à (ce chiffre) du dividende, puis "réduisons" le chiffre suivant du dividende:
1
----
5)972
5
---
47
Nous continuons à faire la même chose jusqu'à ce que nous ayons rempli tous les chiffres:
194
----
5)972
5
---
47
45
---
22
20
---
2
Donc, notre réponse est 194 reste 2.
Considérons maintenant la même chose, mais en binaire. 972 en binaire est 11 1100 1100
et 5 est 101
. Il existe maintenant une différence fondamentale entre la division binaire et décimale: en décimal, un chiffre donné peut être compris entre 0 et 9, nous avons donc dû nous multiplier pour trouver le résultat intermédiaire que nous allions soustraire du dividende. En binaire, le chiffre ne sera jamais égal à 0 ou 1. Nous n'avons jamais besoin de multiplier, car nous ne multiplions jamais que par 0 ou 1 (que nous traitons normalement dans une instruction if - nous soustrayons ou nous ne le faisons pas. ).
-----------
101)1111001100
Notre première étape consiste donc à déterminer quel sera le premier chiffre du résultat. Nous le faisons en comparant 101 à 1111001100 et en le décalant vers la gauche. Cela nous donne:
|
1111001100
10100000000
Lorsque nous effectuons ce changement, nous comptons le nombre de places que nous avons déplacées afin de savoir quel chiffre du résultat que nous remplissons à un moment donné. J'ai montré cela avec la barre verticale ci-dessus. Ensuite, nous décalons le résultat intermédiaire d’une place vers la droite, puis nous déplaçons la barre verticale vers la droite pour indiquer où nous sommes en train de saisir un résultat:
|
1111001100
1010000000
À partir de là, nous vérifions si le diviseur décalé est inférieur au dividende. Si c'est le cas, nous remplissons un 1 à la place appropriée dans la réponse et soustrayons le diviseur décalé du résultat intermédiaire [et pour aider à garder les colonnes droites, je vais insérer quelques espaces]:
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
Nous continuons de la même manière, en remplissant les chiffres du résultat et en soustrayant le diviseur décalé du résultat intermédiaire jusqu'à ce que tous les chiffres soient complétés. Pour tenter de maintenir les choses au clair, je vais écrire chaque chiffre du résultat à l'extrême droite, à côté du sous-titre:
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
Nous obtenons donc un résultat de 11000010, le reste 10. En convertissant ceux-ci en nombres décimaux, nous obtenons respectivement les valeurs 194 et 2 attendues.
Voyons comment cela se rapporte au code ci-dessus. Nous commençons par déplacer le diviseur vers la gauche jusqu'à ce qu'il soit supérieur au dividende. Ensuite, nous le décalons de manière répétée vers la droite et, pour chaque décalage vers la droite, vérifions si cette valeur est inférieure à l’intermédiaire obtenu après la dernière soustraction. Si c'est moins, nous soustrayons à nouveau et remplissons un 1
pour ce chiffre dans notre résultat. Si c'est plus grand, on "soustrait 0" (ne fait rien) et on remplit un "0" pour le chiffre obtenu dans le résultat (ce qui, encore une fois, ne nécessite aucune action, car ces chiffres sont déjà définis sur 0).
Lorsque nous avons rempli tous les chiffres, c’est notre résultat, et tout montant restant que nous n’avons pas encore soustrait est notre reste.
Certains ont demandé pourquoi j'ai utilisé |=
au lieu de +=
dans le code. J'espère que cela aide à expliquer pourquoi. Bien que dans ce cas ils produisent le même résultat, je ne pense pas ajouter chaque chiffre à la réponse partielle existante. Je pense plutôt que cet endroit de la réponse est vide et que or
le remplit simplement.
Options:
Je n'aime pas particulièrement les questions comme celle-ci, car nous cherchons essentiellement des astuces idiotes, mais nous y sommes.
Voici le code Java pour diviser le nombre sans utiliser d'opérateur de division.
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;
}
(Ceci est une solution au problème où vous n'êtes pas autorisé à utiliser la multiplication non plus).
J'aime cette solution: https://stackoverflow.com/a/5387432/1008519 , mais je trouve difficile de raisonner (en particulier la partie |
-). Cette solution a un peu plus de sens dans ma tête:
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;
}
Voici quelques essais:
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
Voici un résumé contenant le diviseur à 0 diviseur et/ou le dividende négatif: https://Gist.github.com/mlunoe/e34f14cff4d5c57dd90a5626266c4130
Implémentation simple de Python utilisant les mathématiques de base au lycée. Un dénominateur est simplement un nombre à la puissance de négatif 1.
def divide(a, b):
return a * b ** -1
Division de deux nombres sans utiliser /
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;
}
Puisque le PO a dit que c'était une question d'entrevue, je pense que l'intervieweur veut voir les choses suivantes en plus de vos compétences de codage. (Supposons que vous utilisez Java)
Comment traiter les nombres négatifs? Il est courant de convertir le dividende et le diviseur en nombres positifs. Cependant, vous pouvez oublier que Math.abs(Integer.MIN_VALUE)
est toujours Integer.MIN_VALUE
. Par conséquent, lorsque le dividende est Integer.MIN_VALUE, vous devez le calculer séparément.
Quel est le résultat de "Integer.MIN_VALUE/-1"? Il n'y a pas une telle valeur dans Integer. Vous devriez en discuter avec l'intervieweur. Vous pouvez lancer une exception pour cette condition.
Voici le code Java pour cette question et vous pouvez le valider leetcode: divisez deux entiers :
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;
}
Le concept principal:
Disons que nous sommes calc 20/4
, donc
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
Le 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;
}
Voici une méthode simple de division pour les ints sans utiliser l'opérateur '/': -
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;
}
En voici un en 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))
Elle pourrait être encore améliorée en arrondissant après la dernière décimale de la précision.
Vous pouvez peut-être trouver un moyen de le faire en utilisant des séquences de >> (décalages de bits) avec d'autres opérateurs au niveau des bits. Il existe un exemple dans psuedo-code dans Wikipedia: Bitwise Operator article.
Si vous prenez la division comme une soustraction, vous pouvez utiliser une méthode "décrémenter" qui vous permet de ne pas utiliser d'opérateur du tout, sauf pour ~ à la fin, pour inverser le résultat ultérieurement en un entier positif toute autre valeur.
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;
}
C'est la fonction qui a résolu mon problème:
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)")
}
Eh bien, s’il ne s’agit que d’une division de type entier/entier = int, il est assez facile d’obtenir la partie entière de x/n = int.dec en ajoutant n + n + n + n jusqu’à ce que n soit supérieur à x, puis en soustrayant un de votre 'n' compte.
Pour obtenir int/int = real sans utiliser *, /,% ou d'autres fonctions mathématiques, vous pouvez effectuer plusieurs opérations. Vous pouvez retourner le reste sous forme de rationnel, par exemple. Cela a l'avantage d'être exact. Vous pouvez également utiliser la modification de chaîne pour transformer votre r en r0 ... (vous choisissez la précision), puis répéter la même astuce d’ajout, puis concaténer les résultats.
Et bien sûr, vous pouvez essayer de vous amuser avec le transfert de bits.
Je ne sais pas si c'est vraiment un "truc stupide", mais plutôt un test sur la capacité d'utiliser des choses simples (addition, soustraction) pour construire une chose complexe (division). Votre employeur potentiel pourrait avoir besoin de cette compétence, car il n'y a pas d'opérateur pour tout. Une question comme celle-ci devrait (théoriquement) éliminer les personnes qui ne peuvent pas concevoir des algorithmes à partir de personnes qui le peuvent.
Je pense que le fait que la réponse soit si facilement disponible sur Internet est un problème, mais c'est un problème de mise en œuvre.