I may be at Oxford, but I haven't lost my passion for taking online courses at Coursera.org. Last spring, I finished the Computer Networks, which was great preparation for my summer internship at Akamai. Recently, I completed Foundations of Business Strategy, which was the furthest departure from computer science I have taken thus far. It was a fascinating class that showed me a totally new way of thinking about the companies I see every day. I also just finished Functional Programming Principles in Scala, which nicely complements my study of functional programming at Oxford.
Currently, I'm taking the Automata class from Stanford, Algorithms Part II from Princeton, and Human-Computer Interaction from UCSD. The Automata class is very mathematically rigorous, and it reminds me of studying cryptography last year. The classes I'm taking at Oxford have a similar level of rigor, and the proofs are good practice for the sort of thing I'm doing for my degree. I took Algorithms Part I last fall, and really enjoyed it, so I'm taking the second half now. I tried to take it last spring, but ended up not having enough time to finish it. Hopefully, I'll be able to stick it out this time. Human-Computer Interaction is a really fascinating class because it covers a really crucial part of computer science that isn't really discussed. Given that almost every program is written with a user in mind, we actually spend far less time on interface design than we could. HCI provides a scientific process for designing interfaces and testing their usability. More generally, it provides helpful tips for anyone designing an application. Overall, I'm still enjoying everything Coursera has to offer and I'm excited to keep augmenting my studies with its content.
Friday, November 22, 2013
Sunday, February 17, 2013
February USACO Results
I regularly compete in the US Computing Olympiad online challenges. There are three levels of competition, bronze, silver, and gold, each with more difficult problems than the last. In order to move up a level, a competitor must achieve a set score in the current level. This week, I advanced from silver to gold by getting a score of 867 out of 1000. I also placed 16th out of 193 high school competitors.
The problems in this competition were really interesting because I was able to apply some of the knowledge I've gained from my Coursera classes. I solved the first problem with a recursive flood fill approach. The recursive, divide-and-conquer paradigm is something the Stanford Design and Analysis of Algorithms class covers extensively. The second problem involved finding the minimum variable that would result in a set of points being connected beyond a certain threshold. I used an optimized union-find data structure to connect the points and check how many were connected in total. This is a technique I learned in my Princeton Algorithms class.
The last problem was really interesting. It required finding the minimum time a set of tasks could be accomplished in, given a set of dependencies. The hard part is that the dependencies had dependencies, and one task might be a dependency for several other tasks. Since the solution has to run in a very short time in order to be judged correct, I couldn't simply calculate the time a task would take each time it was listed as a dependency. Instead, I used a dynamic programming approach, and cached the times of tasks, taking its dependencies into account, when it was first calculated. That way, my program never did the same work twice. I got full points for my solution.
The problems in this competition were really interesting because I was able to apply some of the knowledge I've gained from my Coursera classes. I solved the first problem with a recursive flood fill approach. The recursive, divide-and-conquer paradigm is something the Stanford Design and Analysis of Algorithms class covers extensively. The second problem involved finding the minimum variable that would result in a set of points being connected beyond a certain threshold. I used an optimized union-find data structure to connect the points and check how many were connected in total. This is a technique I learned in my Princeton Algorithms class.
The last problem was really interesting. It required finding the minimum time a set of tasks could be accomplished in, given a set of dependencies. The hard part is that the dependencies had dependencies, and one task might be a dependency for several other tasks. Since the solution has to run in a very short time in order to be judged correct, I couldn't simply calculate the time a task would take each time it was listed as a dependency. Instead, I used a dynamic programming approach, and cached the times of tasks, taking its dependencies into account, when it was first calculated. That way, my program never did the same work twice. I got full points for my solution.
Wednesday, January 23, 2013
Tuesday, January 22, 2013
Coursera
Completed Courses:
Cryptography I: Dan Boneh, Stanford University
Machine Learning: Andrew Ng, Stanford University
Computing for Data Analysis: Roger Peng, Johns Hopkins
Algorithms, Part I: Robert Sedgewick and Kevin Wayne, Princeton University
Current Courses:
Game Theory: Matthew O. Jackson, Kevin Leyton-Brown and Yoav Shoham, Stanford University and the University of British Columbia
Introduction to Computer Networks: Arvind Krishnamurthy, David Wetherall and John Zahorjan, the University of Washington
My Experience:
Over
the past few months, I have had the amazing opportunity of taking
online college courses at Coursera.org. This site offers education
for the sake of education alone. Actual professors post videos of
lectures along with automatically graded homework assignments that
anyone can access. I particularly enjoy it because it allows me to
study subjects that simply are not available at my high school. Some
courses are intended for college freshmen. Others are graduate level.
I've tried several so far, and enjoyed every second. In some ways,
even though the material is harder, I find it easier to learn when I
don't have to worry about credits, prerequisites, or grades. I'm not
ashamed to admit that I failed miserably in some classes that were
too advanced for my experience, but I rose to the challenge in
others.
Cryptography
has fascinated me for years, and Coursera made it possible for me to
taste the field, with a Stanford class taught by Professor Dan Boneh.
For six weeks, I watched in-depth lectures and completed challenging
problem sets. It was difficult to find the time on top of my typical
high school coursework, but I finished successfully. I gained a new
understanding of the subject matter and an incomparable sense of
accomplishment. I'm incredibly grateful to Coursera and all the
professors and universities involved for providing the courses I've
completed so far and the ones I have yet to take. My curiosity is
piqued and I'm ready to study such subjects for real as an
undergraduate.
Statements of Accomplishment:
Algorithms, Part I did not issue a certificate.
CS50 Problem Set Example
Specification:
Essentially, the goal is to see if a word is in a dictionary in the fastest possible real-world time. When actually tested for speed, my program finished in 10th place out of 700 Harvard students; 20th if you count the teaching fellows' submissions.
My Solution:
/**************************************************************************** * dictionary.c *
* Victor Porras* Computer Science 50* Problem Set 5 * * Implements a dictionary's functionality.* This is not the fastest version of this program, but it is the most * extensible and elegant. I hardcoded several parts of this to make the * BigBoard version faster, at the expense of readability and robust design.** Some distribution code is necessary to compile and run this. See spec.***************************************************************************/ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "dictionary.h" #define LEVELS 4 // LEVEL dependent int dict[28][28][28][28][2]; char* raw; char** words; char w[46]; unsigned int sz = 0; /** * Returns true if word is in dictionary else false. */ bool check(const char* word) { if (sz == 0) return false; // preprocessing // since I need to mess around with the characters in word, w is a non-const copy // char* w = (char *) malloc(sizeof(char) * (LENGTH + 1)); // preparing the word size for later int wsz; // this makes EvRytHing lowercase for (wsz = 0; word[wsz] != '\0'; wsz++) w[wsz] = tolower(word[wsz]); // gotta null terminate! w[wsz] = '\0'; // the first LEVELS values of w must be sequential, ie a-z then z+1 z+2, so they can be used to access arrays bool finished = false; for (int x = 0; x < LEVELS; x++) { // check for corner cases if (w[x] == '\'') w[x] = 'z' + 1; if (w[x] == '\0' || finished) { w[x] = 'z' + 2; finished = true; } w[x] -= 'a'; } if (finished) w[LEVELS] = '\0'; // LEVEL dependent // time for binary search int bottom = dict[(int) w[0]][(int) w[1]][(int) w[2]][(int) w[3]][0]; int top = dict[(int) w[0]][(int) w[1]][(int) w[2]][(int) w[3]][1]; int result = 0; // top is -1 when dict[...] has no pointer while (top >= bottom) { result = strcmp(&(w[LEVELS]), words[(top + bottom)/2]); if (result == 0) return true; if (result > 0) bottom = (top + bottom)/2 + 1; else top = (top + bottom)/2 - 1; } return false; } /** * Loads dictionary into memory. Returns true if successful else false. */ bool load(const char* dictionary) { FILE* f = fopen(dictionary, "r"); if (f == NULL) return false; // LEVEL dependent // sets up all the tops with -1 for (int i = 0; i < 28; i++) for (int j = 0; j < 28; j++) for (int k = 0; k < 28; k++) for (int l = 0; l < 28; l++) dict[i][j][k][l][1] = -1; // gets the file size then allocates memory for it struct stat st; stat(dictionary, &st); unsigned long fsz = st.st_size; raw = (char*) malloc(fsz); // it's much faster to read the data in one huge block than in many separate pieces fread(raw, sizeof(char), fsz, f); // sets up the data structures to hold the words themselves int capacity = 262144; words = malloc(sizeof(char*) * capacity); // breaks the raw data into words char* word = strtok(raw, "\n"); // LEVEL dependent // This will store the first LEVELS letters of the previous word so the indices can be quickly found for similar words char old[LEVELS*2] = {27, 27, 27, 27}; // makes sure the file is not over while (word != NULL) { // check if words[] is too big // this runs in amortized linear time if (sz == capacity) { capacity *= 2; words = realloc(words, sizeof(char*) * capacity); } if (strncmp(word, old, LEVELS)) { strncpy(old, word, LEVELS); bool finished = false; for (int x = 0; x < LEVELS; x++) { // check for corner cases if (word[x] == '\0' || finished) { old[LEVELS + x] = 27; finished = true; } else if (word[x] == '\'') old[LEVELS + x] = 26; else // put the indices in old old[LEVELS + x] = word[x] - 'a'; } // set the bottom of the binary search range dict[(int) old[LEVELS]][(int) old[LEVELS+1]][(int) old[LEVELS+2]][(int) old[LEVELS+3]][0] = sz; } // truncate the word to the part after the indices int wsz = strlen(word) - LEVELS; if (wsz <= 0) *word = '\0'; else word = &(word[LEVELS]); // put word into the master array words[sz] = word; // set the top of the binary search range dict[(int) old[LEVELS]][(int) old[LEVELS+1]][(int) old[LEVELS+2]][(int) old[LEVELS+3]][1] = sz; sz++; // get the next word word = strtok(NULL, "\n"); } fclose(f); return true; } /** * Returns number of words in dictionary if loaded else 0 if not yet loaded. */ unsigned int size(void) { return sz; } /** * Unloads dictionary from memory. Returns true if successful else false. */ bool unload(void) { // makes sure that the memory has been malloc'd if (raw == NULL) return false; // frees the big arrays that actually hold the values free(words); free(raw); return true; }
Subscribe to:
Posts (Atom)





