This is the exercice:
Given a string (of bytes), is the number of bits set to 1 odd or even?
Let's now find some possible solutions.
Well, the most naive way is to count them all, and then see if the number is even or odd. But it's probably better not to use an integer to count them all and then see if that counter is odd or even, since we can instead use a Boolean: each time we see a 1, we can simply flip the Boolean value!
let has_an_odd_number_of_1 s = let res = ref false in for i = 0 to String.length s - 1 do let x = int_of_char s.[i] in for j = 0 to 7 do if (x lsr j) land 1 = 1 then res.contents <- not res.contents; done done; res.contents [try]
Moreover, we can see that using a Boolean means it will not limit the number of bits we may read, whereas an integer... well, actually, when we reach the maximum value of integers (of type int, or Int32.t or Int64.t, which — in OCaml — are respectively Pervasives.max_int, Int32.max_int and Int64.max_int), adding one will make them even negative numbers (max_int, from Pervasives, Int32 or Int64, are all odd numbers), so we should still get the odd/even outcome right. But then, we would have to be sure that we have correctly designed the code that tells us if an integer is odd or even. For instance, we can write
Well, let's start with a very classic mathematically-elegant implementation of two mutually recursive functions:
let rec is_odd = function | 0 -> false | n -> is_even (n-1) and is_even = function | 0 -> true | n -> is_odd (n-1) [try]
Note that it needs a lot of computation when the integer is "big"...
And this is probably the shortest version:
let is_odd n = n mod 2 <> 0 [try]
Now note that we cannot write this and expect it to work properly in all cases:
let is_odd n = n mod 2 = 1 [try]
and that's because if n is negative and odd, then n mod 2 is -1, not 1.
A tricky version that uses properties on integers:
let is_odd n = n <> 0 && ((n / 2) * 2) <> n [try]
Another tricky version that uses properties on machine integers:
let is_odd n = n land 1 = 1 [try]
Well, there is the same number of characters as the shortest version, and actually this version is probably the most efficient because we know that the
landoperation is easy for the computer, whereas the
modoperation implies division, which is not an easy one. Then
modcould be optimised in some cases, such as
mod 2because it's basically the same as
land 1except that the sign has to be preserved! So, it's likely to still cost more...
But there's way more efficient than that! Indeed, unless we really don't have a choice, we shouldn't read bits one by one, because it's slow!
So what can we do instead?
Well, there's a very very convenient logical operation on integers.
It's called XOR (and the OCaml operator for that is
lxor), and yes
we can use it!
Note that 1 XOR 1 = 0; 0 XOR 0 = 0; but 1 XOR 0 or 0 XOR 1 = 1. It's very convenient because it's basically giving us the answer! When we have two 1s, we can eliminate them, that's what happens with XOR. When we have a 1 and a 0, we keep the 1. So if we take those bytes byte by byte, and XOR them with each other, at the end we will have one remaining byte that will tell us if the whole string had an odd number of 1 or not.
let has_an_odd_number_of_1__efficient s = let l = String.length s in let rec loop c i = if i = l then let res = ref false in for j = 0 to 7 do if (c lsr j) land 1 = 1 then res.contents <- not res.contents; done; res.contents else loop (c lxor int_of_char s.[i]) (succ i) in loop 0 0 [try]
And... There we go! :-)
int_of_char is a zero-cost operation (but
not, because it has to check that the value of the given integer is
between 0 and 255 inclusive).
started on 2013-09-26 20:15:24+00:00, (re)generated on 2013-10-08 23:18:41+01:00