Wednesday, February 29, 2012

Falling Behind

I have fallen behind on my blogging. I have been pretty busy since my last post. Have
learned a lot of things and a lot of stuff to talk about. This post is meant to briefly
outline my past activities and what is coming up.

First my job. That has been picking up. Finished my first script and bug fix. Going to be
the "Duty Officer" this week. Should be fun for all.

Next, there was a hackathon hosted by my school's Computer Science Undergraduate Association. In the week leading up to it I gave myself a crash course in Django to create a server as a backend to an iPhone app. Like past hackathons, inexperience and an over-ambitious project prevented us from finishing (although this time we actually had something to present, it just was not nearly finished enough). However, I (and I think my two partners) am excited for this project. We mostly finished and it's a pretty good idea (which is why I have been avoiding saying what exactly it is). So when I am not working, doing homework, or playing the occasional LoL game I have been becoming more familiar with Django and cleaning up the server's code. Hopefully we'll get this done and maybe even released eventually. Here's hoping.

And finally, I wrote my first bash script (wheeeee) to rename some files for my dad. Not
terribly interesting to anyone who has done serious (or even basic) scripting, but I
still think it deserves a quick post.

So these are things. These are things that I have been doing and things that I will
dedicate some time talking about. But not now. Now I have to add multiprogramming to
Nachos for my operating systems class.

Facebook Hacker Cup 2012 - Qualification

The Facebook Hackercup 2, electric boogaloo. Last year Facebook hosted their first
programming competition. Despite a few technical flaws early on, overall it was pretty
good. I think I made it to the third round last year. At the time I had just started
college and programming, so I felt pretty good with how far I got. Hopefully this time
around I'll be able to get at least a bit further. The qualification round ended on
Monday and I'm fairly confident that I got two out of the three problems correct (you
only need one correct answer to move on to the next round). I'll present and talk a bit
about my solutions here.

Alphabet Soup

This problem was probably the easiest of the three. Paraphrasing
the problem statement, essentially you need to determine how many times you can form the
word "HACKERCUP" from a given string. The example input file is given as
5
WELCOME TO FACEBOOK HACKERCUP
CUP WITH LABEL HACKERCUP BELONGS TO HACKER
QUICK CUTE BROWN FOX JUMPS OVER THE LAZY DOG
MOVE FAST BE BOLD
HACK THE HACKERCUP
and your output file should look like:
Case #1: 1
Case #2: 2
Case #3: 1
Case #4: 0
Case #5: 1
For this round I decided to use Python. It's what I've used to solve many Project Euler
questions, so I can comfortable solving problems with it. So first, processing the input
is fairly straightforward.
def process_input(file_path):
    with open(file_path, 'r') as input:
        # first line is number of cases
        num_cases = int(input.readline().rstrip())
        # each remaining line represents a case
        cases = list(case.rstrip() for case in input.readlines())
    return cases
Open the file and grab the cases, stripping off newline characters. Now to actually deal
with a case. Forewarning, my algorithm is terrible. The runtime is fine, but my
implementation is so roundabout that when I read it now I cringe. I'll mention a much
better way to do this after I present my solution. So here's the code:
def eval_case(case):
    word = list('HACKERCUP!')
    word_list = [list('HACKERCUP!')]
    for char in case:
        if char in word:
            for chance in word_list:
                if char in chance:
                    chance.remove(char)
                    flag = True
                    break
                if flag:
                    word_list.append(list('HACKERCUP!'))
    count = 0
    for word in word_list:
        if word == ['!']: 
            count += 1
    return count
word is just a list of the characters in the string
"HACKERCUP!". word_list is a running list of "HACKERCUP!" strings that may have
some characters removed. My algorithm is something like this:
  1. Look at each character in the case.
  2. If that character is in word then look
    at word_list
  3. If that character is in a word in word_list remove it
    from that word and add a new "HACKERCUP!" string to the list
  4. Once all the characters have been looked at, the number of
    "!"'s left in word_list are the number of complete words
    that were formed
Wow. Wow wow wow wow. This is why I'm glad I've started this blog. I knew that this
algorithm was stupid, but I did not realize just how stupid until I just wrote this
out. What I meant to implement was a list of strings that kept track of how many partial
"HACKERCUP" strings could be formed. When a character matched the string but was NOT
found in the list, that's when the list should grow. For some reason I wrote it as
WHENEVER the char is found in the list, the list grows. Luckily this didn't affect the
output of the program, but it did make it way more memory intensive. I really do not want
to talk about this piece of not good stuff anymore. But anyway, here's how I tie it
together:
# import was at top of file, of course
import sys

def solve(file):
    cases = map(lambda case: eval_case(case), process_input(file))
    with open('output.txt', 'w') as out:
        for idx, val in enumerate(cases):
            out.write("Case #%i: %i\n" % (idx+1, val))
        
solve('./' + sys.argv[1])
So to solve my case I just put the input file into the same folder as my script and
run python alphabet_soup.py input.txt and the output file output.txt is
generated for submission. The one thing I had to look up for this snippet was the
line for idx, val in enumerate(cases):, which gets me both the index and the
value for my for loop.


What was the good solution, you ask? Luckily I live with
two other EECS students who are much more experienced. They have and continue to teach me
a lot. Their solution was so much nicer. Essentially you make a dictionary mapping
characters that occur in "HACKERCUP" to the number of times they occur in the case. Scale
the value for 'c' by 1/2 (to account for the fact that there are two 'c's in "HACKERCUP")
and take the min of all of the values in the dictionary. Much nicer. Linear time,
constant memory. Ah well, we all managed to get it right in the end.

Conclusion

I am writing this conclusion on Febuary 29th. More than a month after I meant to post
this. I don't really have anything more to say, I just wanted to wrap it up and post
this. I'll post my upcoming plans for blogging soon.