Trouble with finding floor(log2(int)) using binary search in O(log2(amount_bits))Skip List vs. Binary Search TreeFind kth smallest element in a binary search tree in Optimum wayConverting an int to a binary string representation in Java?Cast to int vs floorhow to calculate binary search complexityComputing the floor log of a binary numberHaving trouble with basic Binary SearchFloor implementation in binary search treetrouble with binary search recursionTrouble converting from int in decimal to int in binary
Number of matrices with bounded products of rows and columns
Representing an indicator function: binary variables and "indicator constraints"
Pocket Clarketech
Can anybody tell me who this Pokemon is?
From where do electrons gain kinetic energy through a circuit?
What is the best way to use errors in mathematica M12?
Would getting a natural 20 with a penalty still count as a critical hit?
How does the illumination of the sky from the sun compare to that of the moon?
Why should P.I be willing to write strong LOR even if that means losing a undergraduate from his/her lab?
Do predators tend to have vertical slit pupils versus horizontal for prey animals?
Postdoc interview - somewhat positive reply but no news?
What modifiers are added to the attack and damage rolls of this unique longbow from Waterdeep: Dragon Heist?
How to render "have ideas above his station" into German
What should I do with the stock I own if I anticipate there will be a recession?
What if a restaurant suddenly cannot accept credit cards, and the customer has no cash?
What does a comma signify in inorganic chemistry?
Heyawacky: Ace of Cups
Can I submit a paper computer science conference using an alias if using my real name can cause legal trouble in my original country
Why is su world executable?
Ending a line of dialogue with "?!": Allowed or obnoxious?
Build a mob of suspiciously happy lenny faces ( ͡° ͜ʖ ͡°)
global variant of csname…endcsname
Are there any rules on how characters go from 0th to 1st level in a class?
The Lucky House
Trouble with finding floor(log2(int)) using binary search in O(log2(amount_bits))
Skip List vs. Binary Search TreeFind kth smallest element in a binary search tree in Optimum wayConverting an int to a binary string representation in Java?Cast to int vs floorhow to calculate binary search complexityComputing the floor log of a binary numberHaving trouble with basic Binary SearchFloor implementation in binary search treetrouble with binary search recursionTrouble converting from int in decimal to int in binary
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
In our algorithms class, we've got an extra question in the lab session by the professor. Find the floor(log2(x)) for an int of n bits in log2(n) steps (e.g. when T = uint64_t, then n = 64).
We've found that we should be able to solve this with binary search, but we get an off by 1 result or an endless loop in certain edge cases. We're scratching our heads for some time, but cannot seem to get this right. How do we best deal with this? We've tried to reason with the invariant trick as discussed here, but it seems to be a little more complex than. E.g. for a decimal number, when choosing between bit 7 or 6 is difficult as 128 is larger than 100, but 64 is smaller. Unfortunately, when mitigating this, we break some edge cases.
EDIT: As noted below, this is purely an academic question with low to none usability in real-life scenario's.
Here is our code so far:
//
// 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: %dn", high, mid, low);
if (value < guess)
high = mid - 1;
else
low = mid;
return low;
We have created the following unit tests (using GoogleTest):
TEST(LgFloor, lgfloor)
ASSERT_DEATH(lgfloor(-1), "Assertion `value > 0' failed.");
ASSERT_DEATH(lgfloor(0), "Assertion `value > 0' failed.");
ASSERT_EQ(lgfloor(1), 0);
ASSERT_EQ(lgfloor(2), 1);
ASSERT_EQ(lgfloor(64), 6);
ASSERT_EQ(lgfloor(100), 6);
Thanks in advance,
with kind regards,
Marten
c++ algorithm binary
add a comment |
In our algorithms class, we've got an extra question in the lab session by the professor. Find the floor(log2(x)) for an int of n bits in log2(n) steps (e.g. when T = uint64_t, then n = 64).
We've found that we should be able to solve this with binary search, but we get an off by 1 result or an endless loop in certain edge cases. We're scratching our heads for some time, but cannot seem to get this right. How do we best deal with this? We've tried to reason with the invariant trick as discussed here, but it seems to be a little more complex than. E.g. for a decimal number, when choosing between bit 7 or 6 is difficult as 128 is larger than 100, but 64 is smaller. Unfortunately, when mitigating this, we break some edge cases.
EDIT: As noted below, this is purely an academic question with low to none usability in real-life scenario's.
Here is our code so far:
//
// 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: %dn", high, mid, low);
if (value < guess)
high = mid - 1;
else
low = mid;
return low;
We have created the following unit tests (using GoogleTest):
TEST(LgFloor, lgfloor)
ASSERT_DEATH(lgfloor(-1), "Assertion `value > 0' failed.");
ASSERT_DEATH(lgfloor(0), "Assertion `value > 0' failed.");
ASSERT_EQ(lgfloor(1), 0);
ASSERT_EQ(lgfloor(2), 1);
ASSERT_EQ(lgfloor(64), 6);
ASSERT_EQ(lgfloor(100), 6);
Thanks in advance,
with kind regards,
Marten
c++ algorithm binary
Ischar
signed or unsigned on your platform? If it's unsigned you're in for some fun with your assertions. Since it's recommended to only perform shifts on unsigned quantities but you want tests with signed inputs, you kind of need to figure out which one you want to use.
– Max Langhof
Mar 27 at 13:16
@Max Langhof The algorithm also fails equivalently forusing T = unsigned long long int
, which was the type used when we originally developed this method.
– MartenBE
Mar 27 at 13:19
I'm just trying to help you improve the question. I understand that these nitpicks are not your main concern, but it's irritating for others to get stuck on these problems when they aren't what you care about.
– Max Langhof
Mar 27 at 13:20
In any case, what did you find when debugging this? Which test case fails, and what are the steps your search is taking?
– Max Langhof
Mar 27 at 13:24
add a comment |
In our algorithms class, we've got an extra question in the lab session by the professor. Find the floor(log2(x)) for an int of n bits in log2(n) steps (e.g. when T = uint64_t, then n = 64).
We've found that we should be able to solve this with binary search, but we get an off by 1 result or an endless loop in certain edge cases. We're scratching our heads for some time, but cannot seem to get this right. How do we best deal with this? We've tried to reason with the invariant trick as discussed here, but it seems to be a little more complex than. E.g. for a decimal number, when choosing between bit 7 or 6 is difficult as 128 is larger than 100, but 64 is smaller. Unfortunately, when mitigating this, we break some edge cases.
EDIT: As noted below, this is purely an academic question with low to none usability in real-life scenario's.
Here is our code so far:
//
// 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: %dn", high, mid, low);
if (value < guess)
high = mid - 1;
else
low = mid;
return low;
We have created the following unit tests (using GoogleTest):
TEST(LgFloor, lgfloor)
ASSERT_DEATH(lgfloor(-1), "Assertion `value > 0' failed.");
ASSERT_DEATH(lgfloor(0), "Assertion `value > 0' failed.");
ASSERT_EQ(lgfloor(1), 0);
ASSERT_EQ(lgfloor(2), 1);
ASSERT_EQ(lgfloor(64), 6);
ASSERT_EQ(lgfloor(100), 6);
Thanks in advance,
with kind regards,
Marten
c++ algorithm binary
In our algorithms class, we've got an extra question in the lab session by the professor. Find the floor(log2(x)) for an int of n bits in log2(n) steps (e.g. when T = uint64_t, then n = 64).
We've found that we should be able to solve this with binary search, but we get an off by 1 result or an endless loop in certain edge cases. We're scratching our heads for some time, but cannot seem to get this right. How do we best deal with this? We've tried to reason with the invariant trick as discussed here, but it seems to be a little more complex than. E.g. for a decimal number, when choosing between bit 7 or 6 is difficult as 128 is larger than 100, but 64 is smaller. Unfortunately, when mitigating this, we break some edge cases.
EDIT: As noted below, this is purely an academic question with low to none usability in real-life scenario's.
Here is our code so far:
//
// 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: %dn", high, mid, low);
if (value < guess)
high = mid - 1;
else
low = mid;
return low;
We have created the following unit tests (using GoogleTest):
TEST(LgFloor, lgfloor)
ASSERT_DEATH(lgfloor(-1), "Assertion `value > 0' failed.");
ASSERT_DEATH(lgfloor(0), "Assertion `value > 0' failed.");
ASSERT_EQ(lgfloor(1), 0);
ASSERT_EQ(lgfloor(2), 1);
ASSERT_EQ(lgfloor(64), 6);
ASSERT_EQ(lgfloor(100), 6);
Thanks in advance,
with kind regards,
Marten
c++ algorithm binary
c++ algorithm binary
edited Mar 27 at 23:31
MartenBE
asked Mar 27 at 13:10
MartenBEMartenBE
3631 gold badge3 silver badges14 bronze badges
3631 gold badge3 silver badges14 bronze badges
Ischar
signed or unsigned on your platform? If it's unsigned you're in for some fun with your assertions. Since it's recommended to only perform shifts on unsigned quantities but you want tests with signed inputs, you kind of need to figure out which one you want to use.
– Max Langhof
Mar 27 at 13:16
@Max Langhof The algorithm also fails equivalently forusing T = unsigned long long int
, which was the type used when we originally developed this method.
– MartenBE
Mar 27 at 13:19
I'm just trying to help you improve the question. I understand that these nitpicks are not your main concern, but it's irritating for others to get stuck on these problems when they aren't what you care about.
– Max Langhof
Mar 27 at 13:20
In any case, what did you find when debugging this? Which test case fails, and what are the steps your search is taking?
– Max Langhof
Mar 27 at 13:24
add a comment |
Ischar
signed or unsigned on your platform? If it's unsigned you're in for some fun with your assertions. Since it's recommended to only perform shifts on unsigned quantities but you want tests with signed inputs, you kind of need to figure out which one you want to use.
– Max Langhof
Mar 27 at 13:16
@Max Langhof The algorithm also fails equivalently forusing T = unsigned long long int
, which was the type used when we originally developed this method.
– MartenBE
Mar 27 at 13:19
I'm just trying to help you improve the question. I understand that these nitpicks are not your main concern, but it's irritating for others to get stuck on these problems when they aren't what you care about.
– Max Langhof
Mar 27 at 13:20
In any case, what did you find when debugging this? Which test case fails, and what are the steps your search is taking?
– Max Langhof
Mar 27 at 13:24
Is
char
signed or unsigned on your platform? If it's unsigned you're in for some fun with your assertions. Since it's recommended to only perform shifts on unsigned quantities but you want tests with signed inputs, you kind of need to figure out which one you want to use.– Max Langhof
Mar 27 at 13:16
Is
char
signed or unsigned on your platform? If it's unsigned you're in for some fun with your assertions. Since it's recommended to only perform shifts on unsigned quantities but you want tests with signed inputs, you kind of need to figure out which one you want to use.– Max Langhof
Mar 27 at 13:16
@Max Langhof The algorithm also fails equivalently for
using T = unsigned long long int
, which was the type used when we originally developed this method.– MartenBE
Mar 27 at 13:19
@Max Langhof The algorithm also fails equivalently for
using T = unsigned long long int
, which was the type used when we originally developed this method.– MartenBE
Mar 27 at 13:19
I'm just trying to help you improve the question. I understand that these nitpicks are not your main concern, but it's irritating for others to get stuck on these problems when they aren't what you care about.
– Max Langhof
Mar 27 at 13:20
I'm just trying to help you improve the question. I understand that these nitpicks are not your main concern, but it's irritating for others to get stuck on these problems when they aren't what you care about.
– Max Langhof
Mar 27 at 13:20
In any case, what did you find when debugging this? Which test case fails, and what are the steps your search is taking?
– Max Langhof
Mar 27 at 13:24
In any case, what did you find when debugging this? Which test case fails, and what are the steps your search is taking?
– Max Langhof
Mar 27 at 13:24
add a comment |
4 Answers
4
active
oldest
votes
You need a proper exit condition. Let's say y = floor(lg2(x))
. You should exit the loop when 2^low <= x
and x < 2^(low+1)
. But if high == low+1
then this is fulfilled, yet you do not currently exit. Just do:
while (high > low+1)
{
It is good to look at invariants in your loop. For example, we could try to maintain x < 2^high
(that would require starting at sizeof(T)*8
, not sizeof(T)*8 - 1
). Then all you need to do is bisecting until low == high-1
and you are done.
We can maintain this invariant by only changing high
to mid
if x < 2^mid
, i.e. if value < guess
. That's the first case:
if (value < guess)
high = mid;
We further must maintain 2^low <= x = value
. So, in the else branch (which requires 2^mid == guess < value
, we can safely set low = mid
.
else
low = mid;
All that is left is to prove that the loop always progresses. Since high > low+1
, we have high - low >= 2
and thus mid != low
and mid != high
. Clearly, we are reducing the interval (by half) each iteration.
So there you go:
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: %dn", high, mid, low);
if (value < guess)
high = mid;
else
low = mid;
return low;
I should of course note that there are dedicated intrinsics for this exact purpose in modern hardware. For example, search Intel's intrinsics guide for _BitScanReverse
which will complete in a fraction of the cycles the above code would take.
One way or another, asymptotic runtimes that depend on bit-width are pretty meaningless when dealing with fixed-width types such as C++' integral ones (although the question has educational value still).
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail.if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2):high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.
– MartenBE
Mar 27 at 13:40
@MartenBE In that case just add a check forguess == value
and returnmid
. Note thatlow
can never reachhigh
in your implementation, so if at any pointhigh
is the correct guess then you currently have no way of returninghigh
(you only ever returnlow
). Alternatively, did you tryhigh = mid
andlow = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).
– Max Langhof
Mar 27 at 13:44
We've tried that, but such a solution fails lgfloor(100):if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally:if (value < guess) high = mid; else low = mid;
also gives an endless loop:high: 7, mid: 6, low: 6
. It's really tricky :p
– MartenBE
Mar 27 at 13:47
1
Okvalue == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is,if ((guess ^ value) < guess) return mid;
.
– Max Langhof
Mar 27 at 13:58
1
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.
– Max Langhof
Mar 27 at 14:01
|
show 6 more comments
Endless loop is due to this line:
mid = (low + ((high - low) / 2));
if high
and low
is differ by 1, the result could be mid == low
and then at the condition that cause low = mid
inside the while loop, you resulted in checking the same condition forever. My suggestion would be, if you have low = mid
in the loop, you have to make sure your mid != low
in that case. So just check this before the assignment and do low = mid+1
instead if that happens.
add a comment |
The solution must be found in lg(n)
steps, which means that an initialisation such as low= 0
, high= 32
won't work, because it would take 5
steps in every case and wouldn't work for x
larger than 2^32
. A correct solution must combine a first geometric search where you double the exponent, then a standard dichotomic search.
# 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
add a comment |
Seems like you just have to shift if to the right 'log' times, until you have a '1'.
using T = unsigned char;
int lgfloor(T value)
assert(value > 0);
int log = 0;
while(value != 1)
value >> 1;
log++;
return log;
2
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55378046%2ftrouble-with-finding-floorlog2int-using-binary-search-in-olog2amount-bits%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
You need a proper exit condition. Let's say y = floor(lg2(x))
. You should exit the loop when 2^low <= x
and x < 2^(low+1)
. But if high == low+1
then this is fulfilled, yet you do not currently exit. Just do:
while (high > low+1)
{
It is good to look at invariants in your loop. For example, we could try to maintain x < 2^high
(that would require starting at sizeof(T)*8
, not sizeof(T)*8 - 1
). Then all you need to do is bisecting until low == high-1
and you are done.
We can maintain this invariant by only changing high
to mid
if x < 2^mid
, i.e. if value < guess
. That's the first case:
if (value < guess)
high = mid;
We further must maintain 2^low <= x = value
. So, in the else branch (which requires 2^mid == guess < value
, we can safely set low = mid
.
else
low = mid;
All that is left is to prove that the loop always progresses. Since high > low+1
, we have high - low >= 2
and thus mid != low
and mid != high
. Clearly, we are reducing the interval (by half) each iteration.
So there you go:
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: %dn", high, mid, low);
if (value < guess)
high = mid;
else
low = mid;
return low;
I should of course note that there are dedicated intrinsics for this exact purpose in modern hardware. For example, search Intel's intrinsics guide for _BitScanReverse
which will complete in a fraction of the cycles the above code would take.
One way or another, asymptotic runtimes that depend on bit-width are pretty meaningless when dealing with fixed-width types such as C++' integral ones (although the question has educational value still).
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail.if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2):high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.
– MartenBE
Mar 27 at 13:40
@MartenBE In that case just add a check forguess == value
and returnmid
. Note thatlow
can never reachhigh
in your implementation, so if at any pointhigh
is the correct guess then you currently have no way of returninghigh
(you only ever returnlow
). Alternatively, did you tryhigh = mid
andlow = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).
– Max Langhof
Mar 27 at 13:44
We've tried that, but such a solution fails lgfloor(100):if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally:if (value < guess) high = mid; else low = mid;
also gives an endless loop:high: 7, mid: 6, low: 6
. It's really tricky :p
– MartenBE
Mar 27 at 13:47
1
Okvalue == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is,if ((guess ^ value) < guess) return mid;
.
– Max Langhof
Mar 27 at 13:58
1
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.
– Max Langhof
Mar 27 at 14:01
|
show 6 more comments
You need a proper exit condition. Let's say y = floor(lg2(x))
. You should exit the loop when 2^low <= x
and x < 2^(low+1)
. But if high == low+1
then this is fulfilled, yet you do not currently exit. Just do:
while (high > low+1)
{
It is good to look at invariants in your loop. For example, we could try to maintain x < 2^high
(that would require starting at sizeof(T)*8
, not sizeof(T)*8 - 1
). Then all you need to do is bisecting until low == high-1
and you are done.
We can maintain this invariant by only changing high
to mid
if x < 2^mid
, i.e. if value < guess
. That's the first case:
if (value < guess)
high = mid;
We further must maintain 2^low <= x = value
. So, in the else branch (which requires 2^mid == guess < value
, we can safely set low = mid
.
else
low = mid;
All that is left is to prove that the loop always progresses. Since high > low+1
, we have high - low >= 2
and thus mid != low
and mid != high
. Clearly, we are reducing the interval (by half) each iteration.
So there you go:
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: %dn", high, mid, low);
if (value < guess)
high = mid;
else
low = mid;
return low;
I should of course note that there are dedicated intrinsics for this exact purpose in modern hardware. For example, search Intel's intrinsics guide for _BitScanReverse
which will complete in a fraction of the cycles the above code would take.
One way or another, asymptotic runtimes that depend on bit-width are pretty meaningless when dealing with fixed-width types such as C++' integral ones (although the question has educational value still).
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail.if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2):high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.
– MartenBE
Mar 27 at 13:40
@MartenBE In that case just add a check forguess == value
and returnmid
. Note thatlow
can never reachhigh
in your implementation, so if at any pointhigh
is the correct guess then you currently have no way of returninghigh
(you only ever returnlow
). Alternatively, did you tryhigh = mid
andlow = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).
– Max Langhof
Mar 27 at 13:44
We've tried that, but such a solution fails lgfloor(100):if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally:if (value < guess) high = mid; else low = mid;
also gives an endless loop:high: 7, mid: 6, low: 6
. It's really tricky :p
– MartenBE
Mar 27 at 13:47
1
Okvalue == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is,if ((guess ^ value) < guess) return mid;
.
– Max Langhof
Mar 27 at 13:58
1
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.
– Max Langhof
Mar 27 at 14:01
|
show 6 more comments
You need a proper exit condition. Let's say y = floor(lg2(x))
. You should exit the loop when 2^low <= x
and x < 2^(low+1)
. But if high == low+1
then this is fulfilled, yet you do not currently exit. Just do:
while (high > low+1)
{
It is good to look at invariants in your loop. For example, we could try to maintain x < 2^high
(that would require starting at sizeof(T)*8
, not sizeof(T)*8 - 1
). Then all you need to do is bisecting until low == high-1
and you are done.
We can maintain this invariant by only changing high
to mid
if x < 2^mid
, i.e. if value < guess
. That's the first case:
if (value < guess)
high = mid;
We further must maintain 2^low <= x = value
. So, in the else branch (which requires 2^mid == guess < value
, we can safely set low = mid
.
else
low = mid;
All that is left is to prove that the loop always progresses. Since high > low+1
, we have high - low >= 2
and thus mid != low
and mid != high
. Clearly, we are reducing the interval (by half) each iteration.
So there you go:
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: %dn", high, mid, low);
if (value < guess)
high = mid;
else
low = mid;
return low;
I should of course note that there are dedicated intrinsics for this exact purpose in modern hardware. For example, search Intel's intrinsics guide for _BitScanReverse
which will complete in a fraction of the cycles the above code would take.
One way or another, asymptotic runtimes that depend on bit-width are pretty meaningless when dealing with fixed-width types such as C++' integral ones (although the question has educational value still).
You need a proper exit condition. Let's say y = floor(lg2(x))
. You should exit the loop when 2^low <= x
and x < 2^(low+1)
. But if high == low+1
then this is fulfilled, yet you do not currently exit. Just do:
while (high > low+1)
{
It is good to look at invariants in your loop. For example, we could try to maintain x < 2^high
(that would require starting at sizeof(T)*8
, not sizeof(T)*8 - 1
). Then all you need to do is bisecting until low == high-1
and you are done.
We can maintain this invariant by only changing high
to mid
if x < 2^mid
, i.e. if value < guess
. That's the first case:
if (value < guess)
high = mid;
We further must maintain 2^low <= x = value
. So, in the else branch (which requires 2^mid == guess < value
, we can safely set low = mid
.
else
low = mid;
All that is left is to prove that the loop always progresses. Since high > low+1
, we have high - low >= 2
and thus mid != low
and mid != high
. Clearly, we are reducing the interval (by half) each iteration.
So there you go:
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: %dn", high, mid, low);
if (value < guess)
high = mid;
else
low = mid;
return low;
I should of course note that there are dedicated intrinsics for this exact purpose in modern hardware. For example, search Intel's intrinsics guide for _BitScanReverse
which will complete in a fraction of the cycles the above code would take.
One way or another, asymptotic runtimes that depend on bit-width are pretty meaningless when dealing with fixed-width types such as C++' integral ones (although the question has educational value still).
edited Mar 27 at 15:28
answered Mar 27 at 13:31
Max LanghofMax Langhof
14.9k3 gold badges26 silver badges47 bronze badges
14.9k3 gold badges26 silver badges47 bronze badges
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail.if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2):high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.
– MartenBE
Mar 27 at 13:40
@MartenBE In that case just add a check forguess == value
and returnmid
. Note thatlow
can never reachhigh
in your implementation, so if at any pointhigh
is the correct guess then you currently have no way of returninghigh
(you only ever returnlow
). Alternatively, did you tryhigh = mid
andlow = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).
– Max Langhof
Mar 27 at 13:44
We've tried that, but such a solution fails lgfloor(100):if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally:if (value < guess) high = mid; else low = mid;
also gives an endless loop:high: 7, mid: 6, low: 6
. It's really tricky :p
– MartenBE
Mar 27 at 13:47
1
Okvalue == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is,if ((guess ^ value) < guess) return mid;
.
– Max Langhof
Mar 27 at 13:58
1
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.
– Max Langhof
Mar 27 at 14:01
|
show 6 more comments
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail.if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2):high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.
– MartenBE
Mar 27 at 13:40
@MartenBE In that case just add a check forguess == value
and returnmid
. Note thatlow
can never reachhigh
in your implementation, so if at any pointhigh
is the correct guess then you currently have no way of returninghigh
(you only ever returnlow
). Alternatively, did you tryhigh = mid
andlow = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).
– Max Langhof
Mar 27 at 13:44
We've tried that, but such a solution fails lgfloor(100):if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally:if (value < guess) high = mid; else low = mid;
also gives an endless loop:high: 7, mid: 6, low: 6
. It's really tricky :p
– MartenBE
Mar 27 at 13:47
1
Okvalue == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is,if ((guess ^ value) < guess) return mid;
.
– Max Langhof
Mar 27 at 13:58
1
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.
– Max Langhof
Mar 27 at 14:01
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail. if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2): high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.– MartenBE
Mar 27 at 13:40
if (value < guess) high = mid; else low = mid + 1;
causes lgfloor(1) to fail. if (value < guess) high = mid - 1; else low = mid;
causes an endless loop in lgfloor(2): high: 2, mid: 1, low: 1
. Fixing one case causes others to fail. We suspect that this is not the average binary search application, but has an additional difficulty which we constantly seem to miss. We debugged various variations of this method, but seem to constantly fail one test or another.– MartenBE
Mar 27 at 13:40
@MartenBE In that case just add a check for
guess == value
and return mid
. Note that low
can never reach high
in your implementation, so if at any point high
is the correct guess then you currently have no way of returning high
(you only ever return low
). Alternatively, did you try high = mid
and low = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).– Max Langhof
Mar 27 at 13:44
@MartenBE In that case just add a check for
guess == value
and return mid
. Note that low
can never reach high
in your implementation, so if at any point high
is the correct guess then you currently have no way of returning high
(you only ever return low
). Alternatively, did you try high = mid
and low = mid
yet? Again, the correct solution should be really obvious if you sit down with a piece of paper and do it by hand once (which I admit to not having done myself).– Max Langhof
Mar 27 at 13:44
We've tried that, but such a solution fails lgfloor(100):
if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally: if (value < guess) high = mid; else low = mid;
also gives an endless loop: high: 7, mid: 6, low: 6
. It's really tricky :p– MartenBE
Mar 27 at 13:47
We've tried that, but such a solution fails lgfloor(100):
if (value == guess) return mid; else if (value < guess) high = mid - 1; else low = mid + 1;
. Additionally: if (value < guess) high = mid; else low = mid;
also gives an endless loop: high: 7, mid: 6, low: 6
. It's really tricky :p– MartenBE
Mar 27 at 13:47
1
1
Ok
value == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is, if ((guess ^ value) < guess) return mid;
.– Max Langhof
Mar 27 at 13:58
Ok
value == guess
is of course wrong. What you'd need to check is if you are at the leftmost bit. That is, if ((guess ^ value) < guess) return mid;
.– Max Langhof
Mar 27 at 13:58
1
1
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):
_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.– Max Langhof
Mar 27 at 14:01
Also note that there is an intrinsic that does this whole job instruction for you (in essentially O(1)):
_BitScanReverse
. Doesn't qualify for the assignment (I mean, you could argue that the inbuilt operations for addition, division shifts and comparisons are all O(numBits) just the same), but just in case you ever have to do this in practice, you know.– Max Langhof
Mar 27 at 14:01
|
show 6 more comments
Endless loop is due to this line:
mid = (low + ((high - low) / 2));
if high
and low
is differ by 1, the result could be mid == low
and then at the condition that cause low = mid
inside the while loop, you resulted in checking the same condition forever. My suggestion would be, if you have low = mid
in the loop, you have to make sure your mid != low
in that case. So just check this before the assignment and do low = mid+1
instead if that happens.
add a comment |
Endless loop is due to this line:
mid = (low + ((high - low) / 2));
if high
and low
is differ by 1, the result could be mid == low
and then at the condition that cause low = mid
inside the while loop, you resulted in checking the same condition forever. My suggestion would be, if you have low = mid
in the loop, you have to make sure your mid != low
in that case. So just check this before the assignment and do low = mid+1
instead if that happens.
add a comment |
Endless loop is due to this line:
mid = (low + ((high - low) / 2));
if high
and low
is differ by 1, the result could be mid == low
and then at the condition that cause low = mid
inside the while loop, you resulted in checking the same condition forever. My suggestion would be, if you have low = mid
in the loop, you have to make sure your mid != low
in that case. So just check this before the assignment and do low = mid+1
instead if that happens.
Endless loop is due to this line:
mid = (low + ((high - low) / 2));
if high
and low
is differ by 1, the result could be mid == low
and then at the condition that cause low = mid
inside the while loop, you resulted in checking the same condition forever. My suggestion would be, if you have low = mid
in the loop, you have to make sure your mid != low
in that case. So just check this before the assignment and do low = mid+1
instead if that happens.
answered Mar 27 at 13:26
adrtamadrtam
3,8731 gold badge4 silver badges23 bronze badges
3,8731 gold badge4 silver badges23 bronze badges
add a comment |
add a comment |
The solution must be found in lg(n)
steps, which means that an initialisation such as low= 0
, high= 32
won't work, because it would take 5
steps in every case and wouldn't work for x
larger than 2^32
. A correct solution must combine a first geometric search where you double the exponent, then a standard dichotomic search.
# 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
add a comment |
The solution must be found in lg(n)
steps, which means that an initialisation such as low= 0
, high= 32
won't work, because it would take 5
steps in every case and wouldn't work for x
larger than 2^32
. A correct solution must combine a first geometric search where you double the exponent, then a standard dichotomic search.
# 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
add a comment |
The solution must be found in lg(n)
steps, which means that an initialisation such as low= 0
, high= 32
won't work, because it would take 5
steps in every case and wouldn't work for x
larger than 2^32
. A correct solution must combine a first geometric search where you double the exponent, then a standard dichotomic search.
# 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
The solution must be found in lg(n)
steps, which means that an initialisation such as low= 0
, high= 32
won't work, because it would take 5
steps in every case and wouldn't work for x
larger than 2^32
. A correct solution must combine a first geometric search where you double the exponent, then a standard dichotomic search.
# 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
edited Mar 27 at 15:13
answered Mar 27 at 14:48
Yves DaoustYves Daoust
39.8k8 gold badges30 silver badges64 bronze badges
39.8k8 gold badges30 silver badges64 bronze badges
add a comment |
add a comment |
Seems like you just have to shift if to the right 'log' times, until you have a '1'.
using T = unsigned char;
int lgfloor(T value)
assert(value > 0);
int log = 0;
while(value != 1)
value >> 1;
log++;
return log;
2
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
add a comment |
Seems like you just have to shift if to the right 'log' times, until you have a '1'.
using T = unsigned char;
int lgfloor(T value)
assert(value > 0);
int log = 0;
while(value != 1)
value >> 1;
log++;
return log;
2
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
add a comment |
Seems like you just have to shift if to the right 'log' times, until you have a '1'.
using T = unsigned char;
int lgfloor(T value)
assert(value > 0);
int log = 0;
while(value != 1)
value >> 1;
log++;
return log;
Seems like you just have to shift if to the right 'log' times, until you have a '1'.
using T = unsigned char;
int lgfloor(T value)
assert(value > 0);
int log = 0;
while(value != 1)
value >> 1;
log++;
return log;
answered Mar 27 at 14:32
GardenerGardener
2,0191 gold badge7 silver badges17 bronze badges
2,0191 gold badge7 silver badges17 bronze badges
2
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
add a comment |
2
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
2
2
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
This solution is O(amount_bits) instead of O(log2(amount_bits)).
– MartenBE
Mar 27 at 14:34
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55378046%2ftrouble-with-finding-floorlog2int-using-binary-search-in-olog2amount-bits%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Is
char
signed or unsigned on your platform? If it's unsigned you're in for some fun with your assertions. Since it's recommended to only perform shifts on unsigned quantities but you want tests with signed inputs, you kind of need to figure out which one you want to use.– Max Langhof
Mar 27 at 13:16
@Max Langhof The algorithm also fails equivalently for
using T = unsigned long long int
, which was the type used when we originally developed this method.– MartenBE
Mar 27 at 13:19
I'm just trying to help you improve the question. I understand that these nitpicks are not your main concern, but it's irritating for others to get stuck on these problems when they aren't what you care about.
– Max Langhof
Mar 27 at 13:20
In any case, what did you find when debugging this? Which test case fails, and what are the steps your search is taking?
– Max Langhof
Mar 27 at 13:24