Quirky without being annoying. Amazing acting brought to bear in depicting the struggle of life in the teens. Seeing Jason Bateman and Michael Cera together made it hard to keep Arrested Development out of my head but that wasn’t a bad thing.
Thirty three essays by thirty three authors describing code they consider to be beautiful. A few were inane, a few were exceptional and the majority were generally good. I’ve long wanted to start a blog or magazine that consisted solely of engineers describing interesting complex systems they developed or encountered in the real world. Many of these essays fit that criterion and were, as I suspected, very interesting for that reason alone.
Surprise surprise. Another excellent Zelda title. I particularly dug the control scheme, poking the screen with the stylus, slashing monsters and drawing little circles around Link were all nicely tactile and intuitive. Even driving the boat around was pretty fun, and anyone reading this blog will know how much I like to have my own boat to drive around.
import scala.collection.mutable.Set;
object Euler23 extends Application {
def sumdiv (x :Int) :Int = (1 :: List.flatten(for {
divis <- List.range(2, Math.sqrt(x)+1)
if x % divis == 0
} yield List(divis, x / divis).removeDuplicates)).foldLeft(0)(_+_);
val max = 28123;
val abundant = List.range(1, max+1).filter(a => (a < sumdiv(a))).toArray;
def filter (abund :Array[Int], ints :Set[Int], a :Int, b :Int) :Set[Int] = {
ints -= (abund(a) + abund(b));
if (b == abund.length-1)
if (a == abund.length-1) return ints;
else return filter(abund, ints, a+1, 0);
else return filter(abund, ints, a, b+1);
}
println(filter(abundant, Set() ++ List.range(1, max+1), 0, 0).foldLeft(0)(_+_));
}
This was an annoying excercise in trying to get Scala not be idiotic about performance. Instead of that cumbersome recursive function, I wanted to simply write:
var integers = Set() ++ List.range(1, max+1);
for (a <- List.range(0, abundant.length);
b <- List.range(a, abundant.length))
integers -= (abundant(a) + abundant(b));
println(integers.foldLeft(0)(_+_));
However, for some reason that took 164 seconds to execute. I wrote the same algorithm in Java which took about 450ms to execute. Clearly Scala was doing something ridiculous.
Looking at the decompiled byte codes we see where Scala gets into trouble. The for-comprehension approach generates code that looks plus or minus like so:
public Euler23 () {
// ... compute abundant, etc.
List.range(0, abundant().length).foreach(new AnonFunc() {
public void apply (Object value) { apply(unbox(val)); }
public void apply (int a) {
List.range(a, abundant().length).foreach(new AnonFunc() {
public void apply (Object value) { apply(unbox(val)); }
public void apply (int b) {
integers().remove(box(abundant()[a] + abundant()[b]));
}
});
}
});
// ... foldLeft, print sum, etc.
}
public int[] abundant () { return abundant; }
public Set integers () { return integers; }
private int[] abundant;
private Set integers;
So each time through the outer loop, we’re creating a new anonymous function (probably not a huge deal, abundant.length is only ~6600) and each time through the inner loop we’re accessing “integers” and “abundant” through non-final public method calls.
After seeing this, I suspected that if I passed “abundant” and “integers” to a recursive function call, it would pass those references directly rather than access them through a public method call. This would reduce the inner loop to a single recursive function call. As it turns out, Scala did me one better as you can see below:
public Set filter (int[] abund, Set ints, int a, int b) {
do {
ints.remove(box(abund[a] + abund[b]));
if (b == abund.length - 1) {
if (a == abund.length - 1)
return ints;
b = 0;
a = a + 1;
} else {
b++;
}
} while(true);
}
Now it’s exceedingly clear why this code runs in ~2400ms compared to the ~164000ms of the for-comprehension-based code. Not only does Scala indeed pass the array and set directly, but it determined that my function was tail-recursive and turned it into a while loop.
The moral of the story here is perhaps not to map in your head “val something :Int” in Scala to “final int value” in Java because that’s clearly not what the compiler is doing. Perhaps using Scala in a more object oriented way by declaring classes and members would avoid this funny business. Using Application and writing my solution directly therein may result in semantics I neither need nor expect.
A peculiar film about a woman beating her head against the hierarchy and rigidity of corporate Japan. Strangely French and strangely Japanese at the same time. There are no doubt parallels to be discovered in comparing the two cultures.
object Euler21 extends Application {
def sumdiv (n :Int) = List.range(1, n/2+1).filter(div => n % div == 0).foldLeft(0)(_+_)
println(List.range(1, 10000).filter(n => {
val sdn = sumdiv(n); (n == sumdiv(sdn) && n != sdn && sdn < 10000)
}).foldLeft(0)(_+_));
}
This one is almost readable — if you’ve been staring at a lot of Scala recently. It also uses a less efficient method for computing divisors because it fits on one line.
The above runs in 1176ms but the version using:
def sumdiv (x :Int) = (1 :: List.flatten(for {
divis < - List.range(2, Math.sqrt(x)+1)
if x % divis == 0
} yield List(divis, x / divis).removeDuplicates)).foldLeft(0)(_+_)
runs in 157ms. Oh the sacrifices we make for brevity.
Then I decided to be clever and replace that with an array of functions each of which returned the number of days in the month in question when called with a particular year. This points toward the reason I suspect Odersky uses parenthesis for array dereference:
length(cur._2)(cur._1)
That’s actually an array dereference followed by a function call.
But then I went overboard and replaced my perfectly reasonable iterative solution:
var days = 0;
var firstSundays = 0;
for (year < - List.range(1900, 2001)) {
for (month <- List.range(0, 12)) {
if (year > 1900 && days % 7 == 6) {
firstSundays = firstSundays + 1;
}
days += length(month)(year);
}
}
println(firstSundays);
with the single expression monstrosity you see above.
Back when I was learning functional programming with SML, we used to joke that you could implement any algorithm in one line with foldl. That may indeed be true, but readability ends up sacrificed and bleeding on the floor as a result.
The interesting thing about this one (other than my excellent triangle array formatting) is that we can simply fold the triangle up on itself computing the larger of each pair of elements in the bottom row and adding that larger value to the row above it.
I first started thinking about this as a caching problem, having just finished problem 14 which was reduced to a reasonable runtime by caching. At any coordinate in the grid, there are some number of unique paths from that position to the destination, so one could start in the bottom right and traverse the graph breadth first from the end to the start, caching the number of paths at each intersection and using that to efficiently compute the number of paths as you made your way to the start. I implemented this solution in Java and indeed 841 calls to a recursive function later I had my solution.
Then I realized that every path from the start to the finish is the same length and a series of an equal number of horizontal and vertical segments (20 horizontal and 20 vertical). Thus each unique path was a unique arrangement of those horizontal and vertical segments. If we consider the 40 positions in our path to be distinct elements then the problem becomes how many ways can we choose 20 of our positions in the path to contain horizontal segments (or vertical segments if you swing that way), which any good computer scientist knows is 40 choose 20: 40! / 20! (40 – 20)!.
The funny thing is that the caching solution in Java runs in less than 1 millisecond whereas computing the factorial in Scala above requires 2 milliseconds. It isn’t a tail recursive factorial function though, so that and the BigInt math probably take up all the time.
Because any given number will always converge to 1 in the same number of steps, once you reach a number for which you’ve already calculated the distance to 1 you can simply tack the previously calculated distance onto your current solution and Bob is your proverbial uncle.
For this one, I simply summed the tens column of all the numbers and then tacked that sum onto the list of numbers and lopped off the tens column of all the numbers in the list. When I got down to 8 digit numbers, I simply summed them to get the final result.