我正在阅读算法和数据结构教科书,并提出了这个问题:
1-28。编写一个函数来执行整数除法而不使用 或者是/或*运算符。找到一种快速的方法。
我们怎样才能想出一个快速的方法呢?
答案 0 :(得分:8)
我喜欢这个解决方案:https://stackoverflow.com/a/34506599/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;
}
以下是一些测试运行:
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
以下是解决方案的要点:https://gist.github.com/mlunoe/e34f14cff4d5c57dd90a5626266c4130
答案 1 :(得分:2)
通常,当算法教科书说 fast 时,它们意味着计算复杂性。也就是说,每位输入的操作次数。一般来说,它们不关心常量,所以如果输入 n 位,无论每位需要两次操作还是每位操作一百次,我们都说算法需要O(< em> n )时间。这是因为如果我们有一个在O( n ^ 2 )时间运行的算法(多项式......在这种情况下, square 时间),我们想象一个O( n )算法,与我们的算法相比,每位执行100次运算,每次运算可执行1次运算,一旦输入大小为100位,多项式算法开始运行的速度非常快(与我们相比)其他算法)。从本质上讲,您可以设想两行,y=100x
和y=x^2
。你的老师可能会让你在代数中练习(也许是微积分?),你必须说明哪一个更大,因为 x 接近无穷大。这实际上是微积分中分歧/收敛的一个关键概念,如果你已经在数学中已经到了那里。无论如何,使用小代数,您可以想象我们的图表在x=100
处交叉,而y=x^2
对于 x 大于100的所有点都更大。
就大多数教科书而言,O( nlgn )或更好被认为是“快”。解决这个问题的一个非常糟糕的算法的例子如下:
crappyMultiplicationAlg(int a, int b)
int product = 0
for (b>0)
product = product + a
b = b-1
return product
该算法基本上使用“b”作为计数器,并且每次b倒计时只是在某个变量上添加“a”。为了计算算法的“快速”程度(根据算法复杂度),我们计算不同组件将采用多少次运行。在这种情况下,我们只有一个for
循环和一些初始化(在这种情况下可忽略不计,忽略它)。 for
循环运行了多少次?你可能会说“嘿,伙计!它只运行'b'次!这甚至可能不是一半输入。那是比O更好 n )时间!“
这里的诀窍是,我们关注存储方面输入的大小 ...我们都(应该)知道要存储一个 n 位整数,我们需要lg n 位。换句话说,如果我们有 x 位,我们可以将任何(无符号)数存储到(2 ^ x)-1。因此,如果我们使用标准的4字节整数,那么这个数字最多可以达到2 ^ 32 - 1,如果我的记忆能够正常使用,这个数字可以达到数十亿。如果您不相信我,请使用10,000,000这样的数字运行此算法,看看需要多长时间。还是不相信?使用long
使用1,000,000,000之类的数字。
由于你没有请求算法的帮助,我会把它留给你作为一个家庭作业练习(不是试图成为一个混蛋,我是一个极端的极客和爱算法问题)。如果您需要帮助,请随时提出!我已经意外地输入了一些提示,因为我最初没有正确地阅读你的问题。
编辑:我偶然做了一个糟糕的乘法算法。 真正可怕的划分算法(我被骗)的一个例子是:AbsolutelyTerribleDivisionAlg(int a, int b)
int quotient = 0
while crappyMultiplicationAlg(int b, int quotient) < a
quotient = quotient + 1
return quotient
这个算法很糟糕,原因很多,其中最重要的是使用我糟糕的乘法算法(即使在相对“驯服”的运行中,它也会被称为不止一次 )。即使我们被允许使用*
运算符,这仍然是一个非常糟糕的算法,很大程度上是由于我糟糕的算法中使用了相同的机制。
PS我的两个algs中可能存在一个或两个围栏错误...我发布它们的概念清晰度而不是正确性。然而,无论它们在进行乘法或除法方面有多准确,都不要使用它们。他们会给你的笔记本电脑疱疹,然后让它燃烧起来,让你感到悲伤。
答案 2 :(得分:1)
我不知道你的意思是什么......这似乎是测试你的思维过程的一个基本问题。
一个简单的函数可以使用一个计数器,并从除数中减去除数,直到它变为0.这是O(n)
过程。
int divide(int n, int d){
int c = 0;
while(1){
n -= d;
if(n >= 0)
c++;
else
break;
}
return c;
}
另一种方法是使用shift
运算符,它应该在log(n)
步骤中执行。
int divide(int n, int d){
if(d <= 0)
return -1;
int k = d;
int i, c, index=1;
c = 0;
while(n > d){
d <<= 1;
index <<= 1;
}
while(1){
if(k > n)
return c;
if(n >= d){
c |= index;
n -= d;
}
index >>= 1;
d >>= 1;
}
return c;
}
这就像我们在高中数学中所做的那样整数除法。
PS:如果你需要更好的解释,我会的。请在评论中发帖。编辑:编辑了Erobrere评论的代码。
答案 3 :(得分:1)
执行除法的最简单方法是连续减法:只要b
保持正数,就从a
减去a
。商是执行的减法数。
这可能非常慢,因为您将执行q
减法和测试。
使用a=28
和b=3
,
28-3-3-3-3-3-3-3-3-3=1
商为9
,其余为1
。
想到的下一个想法是一次性减去b
几次。我们可以尝试2b
或4b
或8b
...因为这些数字很容易通过添加进行计算。只要b
的倍数不超过a
,我们就可以尽可能使用。
在该示例中,2³.3是可能的最大倍数
28>=2³.3
所以我们一次性减去8次3,得到
28-2³.3=4
现在我们继续使用较低的倍数2²
,2
和1
来减少剩余部分
4-2².3<0
4-2.3 <0
4-1.3 =1
然后我们的商为2³+1=9
,其余为1
。
您可以检查,b
的每个倍数仅尝试一次,并且总尝试次数等于达到a
所需的倍数。这个数字只是写q
所需的位数,它比q
本身小得多。
答案 4 :(得分:0)
这不是最快的解决方案,但我认为它足够可读并且可以正常工作:
print(df1)
names ids P1 P2 t
0 1 0.349 0.12 0.008
1 2 0.349 0.12 0.008
2 3 0.349 0.20 0.209
3 4 0.349 0.20 0.209
print(ids)
[[1, 2], [3, 4]]
它显示以下结果:
def weird_div(dividend, divisor):
if divisor == 0:
return None
dend = abs(dividend)
dsor = abs(divisor)
result = 0
# This is the core algorithm, the rest is just for ensuring it works with negatives and 0
while dend >= dsor:
dend -= dsor
result += 1
# Let's handle negative numbers too
if (dividend < 0 and divisor > 0) or (dividend > 0 and divisor < 0):
return -result
else:
return result
# Let's test it:
print("49 divided by 7 is {}".format(weird_div(49,7)))
print("100 divided by 7 is {} (Discards the remainder) ".format(weird_div(100,7)))
print("-49 divided by 7 is {}".format(weird_div(-49,7)))
print("49 divided by -7 is {}".format(weird_div(49,-7)))
print("-49 divided by -7 is {}".format(weird_div(-49,-7)))
print("0 divided by 7 is {}".format(weird_div(0,7)))
print("49 divided by 0 is {}".format(weird_div(49,0)))
答案 5 :(得分:-1)
unsigned bitdiv (unsigned a, unsigned d)
{
unsigned res,c;
for (c=d; c <= a; c <<=1) {;}
for (res=0;(c>>=1) >= d; ) {
res <<= 1;
if ( a >= c) { res++; a -= c; }
}
return res;
}
答案 6 :(得分:-2)
伪代码:
count = 0
while (dividend >= divisor)
dividend -= divisor
count++
//Get count, your answer