Treat Yourself a Movie Marathon

To fight the stagnation of a dull Sunday afternoon, my friend, Sherlock Holmes decided, instead of resorting to his old bad habits, to go to the movie theatre. 

He had developed this new habit of watching movies continuously for a whole Sunday afternoon, which wouldn't be a pleasant experience for his companion with weaker mental strength. But such brain marathon was just what Mr. Holmes was looking for. 

He opened the morning paper, jotted down some numbers on a piece of paper, sank back in his arm-chair and involved in some calculation. Shortly afterwards he had put on dress-clothes and rushed downstairs. One minute later, a cab left Baker street for the movie theatre.

Left behind, I examined my friend's calculation on that piece of paper with great curiosity. Before revealing what I can make of it, I think it is necessary to explain the difficulty of treating yourself a movie marathon in our movie theatre.

There are multiple movie viewing rooms in the theatre, each of which has its own schedule of showing the same movie over and over. In order to, say, watch all five movies in a single afternoon, one has to switch to different rooms at the precise time such that he can see all of them from the beginning to the end. It sounds like a similar problem to what Mr. Phileas Fogg had to figure out when he traveled around the world in 80 days by catching different trains and steam boats on time.

With the help of Mathematica and a little functional programming tactic, you can also learn to treat yourself a movie marathon in the theatre.

Code the Movie Schedule

Here is a glimpse of the movie schedule in the morning paper. Comparing it with my friend's list, I could tell how he turned the schedule into a list of numbers:

FB = {"Four Brothers",  {1, 
          49}, {{2, 25}, {4, 45}, {7, 20}, {9, 40}}}

The first item (I added) "Four Brothers" is obviously the name of the movie. The second item "{1, 49}" means that this movie lasts 1 hour 49 minutes. The third item is a list of time schedules at which this movie will be shown in the theatre. For example this movie will be shown at four different times in the afternoon,  the latest session being at 9:40PM.

Matching his numbers carefully, I was able to find out the five movies he picked for himself that afternoon.

HV = {"A History of Violence",  {1, 
          35}, {{2, 10}, {4, 35}, {7, 05}, {9, 30}}}; 
    JLH = {"Just Like Heaven", {1, 
          41}, {{2, 35}, {4, 55}, {7, 05}, {9, 15}}}; 
    LW = {"Lord of War", {2, 2}, {{3, 30}, {6, 30}, {9, 15}}}; 
    S = {"Serenity", {1, 59}, {{2, 0}, {4, 45}, {7, 25}, {10, 0}}}; 
    TBCB = {"Tim Burton's Corpse Bride", {1, 
          14}, {{3, 15}, {5, 30}, {7, 45}, {9, 35}}};

To facilitate calculation of "hours" and "minutes", we have to transform them into "seconds". Here are the the Mathematica functions to help us.

toSecond[{hr_, min_}] := 
        FromDigits[{hr, min, 0}, 60];
fromSecond[s_] := 
        Most[IntegerDigits[s, 60, 3]];
transform[{movieName_, movieLength_, schedule_}] := {movieName, 
        toSecond[movieLength], toSecond /@ schedule}

hislist = transform /@ {HV, JLH, LW, S, TBCB}

Make a Choice

So far so good, these numbers match exactly what Mr. Holmes had written next to his movie list. The next logical step, I believe my friend would take, is to decide on which movie to see first, second, third and etc. Since there are only 5 movies to be arranged in order, its permutation has 5!  =  120 possibilities. It won't be a challenge at all for my friend to have considered all these 120 possibilities. But how did he reach his final decision?

Let's imagine the "crime" scene as follows: someone arrives at the theatre at 12:30, his first movie has the following schedule: 12:15, 2:15, 4:15, 6:15. If he picks the 12:15 session, he is already 15 minutes from the beginning of the movie. Otherwise he could wait 1 hour 45 minutes for the next session at 2:15.

For someone who plans to see five movies in one afternoon, 15 minutes late is better than wasting 1 hour 45 minutes. So the my rule of deduction is: pick a movie session that is closest to the theatre clock such that the time wasted is minimum.

This principle also holds true for choosing the second, third and so forth movies. The only difference is that when choosing the second movie, the theatre clock should be updated according to the length of the first movie.To code this into Mathematica function in the functional style.

\(pickEarliest[{clk_, penalty_, result_}, movie_] := 
      Module[{t, pos, movieName, movieLength, 
          schedule}, 
{movieName, movieLength, schedule} = 
          transform[movie]; 
t = 
          Abs[clk - schedule]; 
 (*\ 
          pick\ the\ closest\ movie\ time\ *) 
pos = \(Flatten[Position[t, 
                Min[t]]]\)[\([1]\)]; 
{schedule[\([pos]\)] + movieLength, 
 (*\ 
            no\ penalty\ unless\ one\ is\ late\ for\ the\ movie\ *) 
If[clk < schedule[\([pos]\)], penalty, 
            penalty + t[\([pos]\)]], 
          Append[result, 
            movieName <> " " <> 
              ToString[\(fromSecond[
                    schedule[\([pos]\)]]\)[\([1]\)]] <> ":" <> 
              ToString[\(fromSecond[
                    schedule[\([pos]\)]]\)[\([2]\)]]]}]\)

For each permutation of five movies, there is an accumulated penalty number (i.e. the missing time at the beginning of a movie). It's reasonable to choose the order of movies that has the least penalty. Here is the Mathematica function implementing it.

result = Fold[pickEarliest, {0, 0, {}}, ##] & /@ 
        Permutations[{HV, JLH, LW, S, TBCB}]; 
    Select[result, #[[2]] == 
          Min[result[[All, 2]]] &]

All in One

To put all these functions into one, with the input: a list of movies that you plan to see, each of which being a list of: {"movie name", movieLength, {schedule1, schedule2, ...}}

movieMarathon[movielist_] := 
      Module[{result, temp}, 
        result = Fold[pickEarliest, {0, 0, {}}, ##] & /@ 
            Permutations[movielist]; 
        temp = Select[
            result, #[[2]] == 
                Min[result[[All, 
                      2]]] &]; 
{fromSecond[#[[1]]], 
              fromSecond[#[[2]]], #[[3]]} & /@ 
          temp
]

movieMarathon[{HV, JLH, LW, S, TBCB}]

At bottom of that piece of paper, it reads, "Watson, please meet me outside of the theatre at mid-night, if you don't mind we have dinner together." That's right. His last movie would end at 11:59PM. I can't wait to see my friend's face lit up when I tell him the exact order of movies he had watched during his afternoon movie marathon. In fact, I am going to the theatre right now.