C++ 在O(log2(amount_bits))中使用二进制搜索查找下限(log2(int))时遇到问题
在我们的算法课上,教授在实验课上提出了一个额外的问题。在log2(n)步中查找n位整数的下限(log2(x))(例如,当T=uint64\u T时,则n=64) 我们已经发现,我们应该能够用二进制搜索来解决这个问题,但在某些边缘情况下,我们会得到一个1比1的结果或一个无休止的循环。我们挠头已经有一段时间了,但似乎无法正确处理。我们如何最好地处理这个问题?我们已经试着用讨论过的不变技巧进行推理,但它似乎比我们想象的要复杂一些。例如,对于十进制数,在第7位或第6位之间进行选择很困难,因为128大于100,但64小于100。不幸的是,在缓解这种情况时,我们打破了一些边缘案例 编辑:如下所述,这纯粹是一个学术问题,在现实场景中可用性很低甚至没有 以下是我们目前的代码:C++ 在O(log2(amount_bits))中使用二进制搜索查找下限(log2(int))时遇到问题,c++,algorithm,binary,C++,Algorithm,Binary,在我们的算法课上,教授在实验课上提出了一个额外的问题。在log2(n)步中查找n位整数的下限(log2(x))(例如,当T=uint64\u T时,则n=64) 我们已经发现,我们应该能够用二进制搜索来解决这个问题,但在某些边缘情况下,我们会得到一个1比1的结果或一个无休止的循环。我们挠头已经有一段时间了,但似乎无法正确处理。我们如何最好地处理这个问题?我们已经试着用讨论过的不变技巧进行推理,但它似乎比我们想象的要复杂一些。例如,对于十进制数,在第7位或第6位之间进行选择很困难,因为128大于1
//
// h l
// 76543210
// 0b01000001 = 65
//
using T = unsigned char;
int lgfloor(T value)
{
assert(value > 0);
int high = ((sizeof(value) * 8) - 1);
int low = 0;
int mid = 0;
T guess = 0;
while (high > low)
{
mid = (low + ((high - low) / 2));
guess = static_cast<T>(1) << mid;
printf("high: %d, mid: %d, low: %d\n", high, mid, low);
if (value < guess)
{
high = mid - 1;
}
else
{
low = mid;
}
}
return low;
}
提前感谢,,
致以亲切的问候
Marten无休止的循环是由这条线造成的:
mid = (low + ((high - low) / 2));
如果
high
和low
相差1,则结果可能是mid==low
,然后在while循环中导致low=mid
的条件下,您将永远检查相同的条件。我的建议是,如果循环中有low=mid
,则必须确保mid!=在这种情况下为低
。因此,只需在分配之前检查此项,如果出现这种情况,请执行low=mid+1
。您需要一个适当的退出条件。比如说y=floor(lg2(x))
。当2^low似乎只需将if移到正确的“log”时间,直到出现“1”时,您应该退出循环
using T = unsigned char;
int lgfloor(T value)
{
assert(value > 0);
int log = 0;
while(value != 1) {
value >> 1;
log++;
}
return log;
}
必须在lg(n)
步骤中找到解决方案,这意味着像low=0
,high=32
这样的初始化将不起作用,因为它在每种情况下都需要5
步骤,并且在x
大于2^32
的情况下不起作用。正确的解决方案必须结合第一次几何搜索(指数加倍),然后是标准的二分法搜索
# Geometric search
low= 0
high= 1
while (1 << high) <= x:
low= high
high+= high
# Dichotomic search
while high - low > 1:
mid= (high + low) >> 1
if x < mid:
high= mid
else:
low= mid
#几何搜索
低=0
高=1
而(1>1
如果x
在您的平台上是char
有符号还是无符号?如果是无符号的,您的断言会很有趣。由于建议只对无符号量执行移位,但您需要使用有符号输入进行测试,因此您需要确定要使用哪一个。@Max Langhof该算法对于使用T=unsigned long long int
,这是我们最初开发此方法时使用的类型。我只是想帮助您改进此问题。我知道这些挑剔不是您主要关心的问题,但当您不关心这些问题时,其他人被困在这些问题上会很恼火。在任何情况下,您发现了什么调试时?哪个测试用例失败,您的搜索采取了哪些步骤?if(value
导致lgfloor(1)失败。if(value
导致lgfloor(2)中的无休止循环:high:2,mid:1,low:1
。修复一个案例会导致其他案例失败。我们怀疑这不是一般的二进制搜索应用程序,但有一个我们经常忽略的额外困难。我们调试了此方法的各种变体,但似乎总是在一个或另一个测试中失败。@MartenBE在这种情况下,只需添加一个检查f或者guess==value
并返回mid
。请注意low
在您的实现中永远无法达到high
,因此如果在任何时候high
是正确的猜测,那么您当前无法返回high
(您只返回low
)。或者,你试过high=mid
和low=mid
吗?同样,如果你坐下来拿一张纸,用手做一次,正确的解决方案应该非常明显(我承认我自己没有做过)。我们试过,但这样的解决方案失败了(100):if(value==guess){return mid;}else if(value
。另外:如果(value
也给出了一个无休止的循环:高:7,中:6,低:6
。这真的很棘手:pOkvalue==guess
当然是错误的。你需要检查的是你是否在最左边。也就是说,如果((guess^value)
。还请注意,有一个内在因素可以为您完成整个作业说明(本质上是O(1)):。不符合分配条件(我的意思是,您可以争辩说,加法、除法移位和比较的内置操作都是O(numBits)完全相同),但万一你在实践中不得不这样做,你知道。这个解决方案是O(amount\u bits),而不是O(log2(amount\u bits))。
if (value < guess)
high = mid;
else
low = mid;
int lgfloor(T value)
{
assert(value > 0);
int high = (sizeof(value) * 8);
int low = 0;
while (high > low+1)
{
int mid = (low + ((high - low) / 2));
T guess = static_cast<T>(1) << mid;
printf("high: %d, mid: %d, low: %d\n", high, mid, low);
if (value < guess)
high = mid;
else
low = mid;
}
return low;
}
using T = unsigned char;
int lgfloor(T value)
{
assert(value > 0);
int log = 0;
while(value != 1) {
value >> 1;
log++;
}
return log;
}
# Geometric search
low= 0
high= 1
while (1 << high) <= x:
low= high
high+= high
# Dichotomic search
while high - low > 1:
mid= (high + low) >> 1
if x < mid:
high= mid
else:
low= mid