Richard Heathfield said:
Keith Thompson said:
A genetic algorithm, at its simplest, is one for which you express a
candidate solution to a problem as a bit-string, and have some way to
evaluate the fitness of that solution (for example, if it's TSP, the
fitness is the total distance travelled, where low is good). You
generate lots of random solutions, sort them by fitness, eliminate the
weakest, and breed the remainder using standard genetic crossover and
mutation techniques (crossover basically means "take so many bits from
the father solution, then yay many from the mother solution, switching
back and forth at random points until you have a complete child
solution", and mutation means "flip N randomly-selected bits").
Iterate until you have a solution that you consider "good enough".
GAs are not generally a great idea if you're after an exact solution,
but they can home in on "good enough" solutions surprisingly quickly.
Thanks, that's helpful.
So it seems that it would work best if two solutions whose bit-strings
differ by only a few bits are in some sense "close" to each other, or
at least are likely to have some meaningful relationship to each
other.
For example, if the bit-strings are MD5 checksums, and "fitness" is
determined by whether the bit-string is the MD5 checksum of a
particular input, then a GA isn't going to do much good; two very
similar inputs will have completely dissimilar checksums.
Representations of floating-point numbers can vary from one system to
another, though the IEEE standard representation is very common these
days. Trying GA on floating-point numbers, viewing their
representations as bit-strings, sounds interesting; at the very least,
discovering that it doesn't work could be useful.
Getting back to the OP's question, the language simply doesn't define
bitwise operations on floating-point types. But you can do it
indirectly.
If you don't care about absolute portability, you can choose an
unsigned integer type that's the same size as your floating-point
type. (The language doesn't guarantee that there is such a type.)
For example, on many systems, types double and unsigned long long are
both 64 bits. You can define a typedef, to be modified for different
implementations, for whichever unsigned type is the same size as
double. (C doesn't provide such a typedef because there's not much
use for it in general.)
With some risk of non-portability, you can declare a union:
typedef unsigned long long double_unsigned; /* this may vary */
union kludge {
double d;
double_unsigned u;
};
You can then store a double value in the "d" member, then perform your
bitwise operations on the "u" member, and read the modified value of
the "d" member.
Strictly speaking, storing a value in one member of a union and
reading another member is not permitted. In practice, it's likely to
work, and what you're doing is pretty much inherently non-portable
anyway.
An alternative to a union would be to use memcpy() to copy the
representation of a floating-point object to an unsigned integer
object and vice versa.