Sometimes I think it's a curse to be in high-tech. Not because of the job itself but because it basically ruins movies for me. Any movie that has anything to do with computers. Any time a movie throws some hackery on the screen, it winds up looking incredibly amateurish. Imagine a medical show throwing around dialog about freezing peoples' heads. Utterly ridiculous. And I bet I know why this technobabble is wildly inaccurate: it's boring. Code is boring. And not just kind of boring, it's amazingly boring. Public television-boring.
Which is why I gotta warn you: these Queens entries will have a substantial amount of code. But don't worry, I'll be your tour guide. And all the code will be in snippets and clearly marked, so you can gawk at them like you're looking at animals in a zoo. Or inmates in an insane asylum. And we're walking down a corridor, lit only by bare light bulbs, that leads to Dr. Lecter himself.
But don't worry. I'll make it interesting. When I have a pen and paper in my hands, I can make anything interesting.
Part I. Growing pains, or discovery
Software is really malleable. It doesn't exist anywhere except in the minds of its creators. There's nothing substantial about it. That's both a blessing and a curse, though, as many companies are guilty of releasing things that are half-baked. Kind of like the time I got an egg tart from Pacific Mall that was half-cooked. I tried to return it but only got half my money back. But I digest...
Sorry, hungry. Anyways, the best part of software is how easily you can change your mind about things. There are no parts to assemble, no chemicals to dispose. But all that flexibility comes at a cost. You're basically afloat in a vast ocean of possibilities on a tiny little raft. It's night and you're navigating using a deft combination of your wits and the stars. And each trick you know is another constellation you can use for guidance.
I've solved a few problems like this before, so I knew a few tricks. I essentially took what I knew and applied it cookie-cutter to this problem. The solution was, shall we say, competent but uninspired. In case you're curious:
private static long findSolutions(
int index,
int row,
long down,
long ld,
long rd) {
int startIndex = index;
long solutions = 0;
long mask = 0;
// Main (critical) loop.
for (;; index >>= 1) {
mask = index;
if ((down & mask) == 0L &&
(ld & (mask << row)) == 0L &&
(rd & (mask << (32 - row))) == 0L) {
if (row != 0) {
solutions += findSolutions(startIndex, row - 1,
down | mask,
ld | mask << row,
rd | mask << (32 - row));
} else {
// This optimization disables rectangular boards.
return 1L;
}
}
if (index == 1) {
return solutions;
}
} // End critical for loop
/* NOT REACHED */
} // end of function
If you're not a programmer, that was kind of a Keanu-"whoa" moment. What does any of it mean? Doesn't matter. All you really need to know is that it's basically the standard, plain-vanilla version of the solution. No one would fault you for a solution like this, but you probably wouldn't garnish much praise either.1
The only thing really noticeable about this solution is how normal it is. It sets everything up, then goes off into a loop. Ya gotta loop, cuz it's, you know, how you get stuff done on computers. Whenever you use a computer it's essentially in a giant loop waiting for you to do stuff. After the loop you get your "if" statements that check to see if we have a solution. Read one of my other entries for the wacky wigged-out fun that is "if" statements. Then the solution calls itself. Not like a schizophrenic with two cell phones; this kind of "calling yourself" actually makes sense. It's called recursion, and in order to define recursion, we must first define recursion. (If you got that, send me a resume.)
For the programming-minded, I have some basic optimizations in place. I'm using longs as bit fields2 and looping backwards to avoid a comparison to a variable through each iteration of the loop.3 Real Mickey Mouse stuff. In fact, in any other project, I probably wouldn't suggest looking into such trivial matters. But in this case, trying to optimize this loop was kind of like squeezing blood from an onion.
So there you go. At this point, we're like a kid out of high school. We know what we're doing and where we want to go. We can accomplish things, but we're neither noteworthy nor especially fast. But we're past the first hurdle.
Timing: For my baseline, I'm using the current Java world-record holder.4 It solves the 16-queen problem5 in 70 seconds on my computer. This version of my solver does it in 70.48. I actually timed it. Yes, I have no life. But I'm just about as fast as a world-record holder, and that's a good start.
Footnotes.
(1) Actually, my first attempt separated the representation of the chessboard into a separate class. Well, since method invocations on classes are much slower than accessing variables, that approach was initially much slower, taking over 180 seconds to solve the 16 x 16 chessboard case. That was embarrassingly slow to mention, except as footnote material.
(2) This is as opposed to using boolean arrays. Array accesses in Java are automatically bounds-checked. This means that each time an array access occurs, the VM checks to see if the index is in bounds. This incurs a significant overhead.
(3) This is a really esoteric form of optimization, and I'd only use in cases where there's a very frequently-called loop and precious little room to improve elsewhere. Such as a tight loop like this. It essentially involves looping backwards from N to 0 instead of forwards from 0 to N in cases where you don't know what N is beforehand. The normal way of iterating forwards requires a comparison between two variables (the loop index and N), which requires two loads from main memory. Looping backwards requires only a comparison between a variable and a constant (the index and 0), which only requires one load from main memory. Loads from main memory are slow and best avoided if possible.
(5) This is the problem of finding the total number of solutions on a 16 x 16 chessboard. Incidentally, there are 14,772,512. Timing was done on a single computer.