0

I'm really banging my head against the wall with this, as I have very little experience with algorithms.

Consider an app that maintains a set of TV shows and the episodes for those shows. The user is allowed to rank the shows (rank, not rate; so from 0 to n-1, 0 being the highest ranking).

The app is designed to present a list of episodes based on ranking of the shows. At any given time there are 0, 1, or 2 episodes that the app will display in the list for each show (ie, if the user has watched all episodes of TheFooShow, no episodes will appear in the list; if she has 10 unwatched episodes of TheBarShow, two will appear in the list). Those episodes will be considered primary and secondary.

The ordering of episodes within the list should adhere to some rules as best as possible:

  • The primary episode of a show in the list should appear before any episodes of lower-ranked shows
  • The secondary episode of a show should never appear before the primary episode of the same show
  • If possible, no two episodes of a show should be within 4 positions of each other
  • The secondary episode of a show can appear before the primary episode of a show ranked seven or more positions worse
  • etc

The point here is that episodes from a high ranking show will be listed before lower ranking shows, but only to the degree that a certain amount of variety is introduced whenever possible.

An example set could be (not strictly following the above rules): [0, 1, 3, 0', 3', 4, 5, 10, 4', 11, 11', 23]

I can get close to achieving the desired results when constructing the list from scratch, adding all the primary episodes, and then inserting the secondary episodes as necessary. Unfortunately given how the rest of the app works, I need to be able to insert episodes into an already-constructed list.

Given how the underlying episode data changes, whenever an episode needs to be inserted, all episodes for that show already in the list will be removed, the current set of episodes for that show will be discovered, and they (or it) will be inserted back-to-back.

I would essentially like a function such as:

- (int, int)indexesForEpisodesFromShow:(TVShow)aShow currentList:(List)aList

Which can take a list in any state and, given a show, take the appropriate episodes and determine where in the list they should be inserted.

My current solution is fairly primitive, and as I said only works in certain situations. I have tried other solutions that are more complex (read: more if statements), but they tend to become very fragile, and when I add additional complexity to handle edge cases, tests start to contradict other tests.

I feel like the given the kinds of rules I'm trying to implement, and how few there are, this should be solvable, but after many attempts I'm still stuck. Any help would be greatly appreciated.

Farski
  • 1,670
  • 3
  • 15
  • 30

1 Answers1

0

I think all you need is a function to compare two episodes.

First let‘s make some assumption:

  • an object of episode has two properties:
    1. Show: The show object that a episode belongs to
    2. IsPrime: Whether a episode is primary
  • and a show object has one property:
    1. Rank: The show's rank

And then, let's create a function that has two episode objects as input parameters, and returns:

  • -1: The first episode must be positioned before the second episode.
  • 0 : The two episode can be positioned in any order.
  • 1 : The first episode must be positioned after the second episode.

    CompareEpisode(e1, e2)
    {
      if(e1.IsPrime)
      {
        if(e2.IsPrime)
          return CompareSame(e1, e2);
        else
          return CompareDiff(e1, e2);
      }
      else
      {
        if(e2.IsPrime)
          return -CompareDiff(e2, e1);
        else
          return CompareSame(e1, e2);
      }
    }
    
    CompareSame(e1, e2)
    {
      if(e1.Show.Rank < e2.Show.Rank)
        return -1;
      else if(e1.Show.Rank == e2.Show.Rank)
        return 0;
      else
        return 1;
    }
    
    CompareDiff(p, s)
    {
      if(p.Show == s.Show)
        return -1;
      else
      {
        if(p.Show.Rank < s.Show.Rank + 7)
          return -1;
        else if(p.Show.Rank == s.Show.Rank + 7)
          return 0;
        else
          return 1;
      }
    }
    

And now you can iterator the list, compare each element in the list to the inserting episode using CompareEpisode, find the first element make the CompareEpisode returns 0 or 1, you can insert the new episode before the element found.

And if there are some elements make the CompareEpisode returns 0, you got mutiple candidate position, you can choose one to fit the "put episodes from same show as far as posible" rule.

Yong Tiger
  • 121
  • 3
  • That sounds like a good approach. I will try it and report back. – Farski Dec 10 '13 at 18:19
  • There's a mistake in the code, in the else branch of the outer if in CompareEpisode the first return should be `return -CompareDiff(e2, e1);`. – Yong Tiger Dec 11 '13 at 01:28
  • The problem I'm having with this approach is enforcing rules that have to do with keeping a minimum distance between like episodes. Eg if I'm inserting the secondary episode for a show, as I'm going through the list I pass it and the primary episode to this and return 1, but then when I move to the next episode, I still need to do a look around at episodes other than what was passed in to see if I'm meeting that min. distance requirement – Farski Dec 13 '13 at 16:15