Return to the Index of Meeting Notes and Topics


15-295 Meeting 2 (Wednesday, January 19, 2005)

Readings and Problems

Announcements

Problem D

A "map" includes a number of segments, specified by the planar coordinates of their endpoints. We should determine the minimal number of segments required for drawing this map.

Consider a simplified problem: All segments are on the x-axis:

In the simplified problem, as shown above, we can sort the segments in the increasing order of their left endpoints and then process them from left-to-right. We denote the array of left points as left[0...n-1] and the array of right endpoints as right[0...n-1]. The algorithm for solving the problem is as follows:

// Precondition: left[0...n-1] is sorted
int simpleSegments (int left[], int right[], int n) {

  int counter = 1;
  int r = right[0];

  for (int i=2; i < n; i++) {
    if (left[i] > r) 
      conter++;
    if (right[i] > r) 
      r = right[i];
  }

  return counter;
}

Returning to the original problem with two dimensions. If segments in two dimensions all belong to the same straight line, we can use the same problem. It is really a 1D problem, with a different baseline orientation.

So, the big picture solution to the 2D problem is to divide the segments into groups such that segments in the same group share the same line. In other words, create one group for each base line and include in each group all segments oriented along that line. Then, for each group, determine the minimal number of segments.

For each group we determine the values a and b in the line equation, y = a*x + b. If two segmetns have the same a and b, they are along the same line and therefore in the same group.

If x1 is not equal to x2, then the line equation is more complex:

y = x* (y2-y1)/(x2-x1) + (y1*x2-x1*y2)/ (x2-x1)

In the case above,

a = (y2-y1)/(x2>-x1)

b = (y1*x2-x1*y2)/ (x2-x1)

So, we have the overal algorithm:

Implementation issues:

Problem H

We consider a list of word pairs in some unknown language and a list of their translations. The order of translations is not the same as the order of original word pairs. We need to determine the right translation of each word.

The right idea is to create the bipartite graphs of possible word matches and possible pair matches and then to prune these graphs until there is only one possible match.

For example, we can use the following greedy-search algorithm:

Initialize the search

Prune the graph

The word counts are consistent if the the word on the left side of an edge and the word on the right side of an edge are used the same number of times as first words and they are also used the same number of times as second words. In other words, for every edge (u,v) the counts for u are the same as the counts for v.

  1 0    *----------*     1 0     (good)
  1 0    *----------*     2 1     (bad)

A graph is internally consistent if for every edge (u, v), if u has only one outgoing edge, the v has only one outgoing edge.

   *----------*     (good)

   *----------*     (bad)
   *_____----/
  
   

The "pair" graph is consistent with the "word" graph iff, for every edge [U1, U2], [V1, V2), there are edges (U1,V1) and (U2,V2).

The "word" graph is consistent with the "pair" graph iff the following are true for every (U,V) in the word graph:

If the Greedy search produces a one-to-one correspondance we are quite lucky -- this isn't a guarantee. If not, we need to use a recursive search.

Implementation Strategy

Implement the greedy search and submit your solution. Espectially in our region, this tends to be enough. They often look for good solutions that aren't exhaustive or guaranteed. If this fails, then implement the exhaustive search (ouch!)

Problem E

We use several dice, where each die has a certain number of facets with return values. We need to add a new die, with a given number of values, that ensures chances of getting certain sums.

For example, consider the following dice: 1,1,1,2 or 1,2,2,2 or 1,1,1,2,2,2.

Initial Chances
Value 1 2 3 4 5 6
Chances 0 0 9 39 39 9

Now, add a four-sided die to ensure that the chances of getting a 4 is a 5 and the chance of getting a 9 is a 48. It is important to note that the chances of getting other sums do not matter. The solution is a die: 1,2,3,3.

This is a messy problem with no neat solution -- at least we don't have one. The basic approach is to compute the initial chances and to search through all possible labels of the new die.

To make this problem manageable, we use dynamic programming. We use recursion and use a table to prevent computing partial solutions that overlap among one or more paths.

It is important to note that the maximum number of dice is 20 and the maximum value is 50. As a result, the required table can be no more than 20 * (50*20) = 20,000 entries.

Consider the example:

DiePossible values
1 3 1 0 0 0 0
2 0 3 6 3 0 0
3 0 0 0 39 39 0

The algorithm for initializing procedes as follows:

int chances[0...20, 0...10000] = { {0, ..., 0}, {0, ..., 0} }

chances[0,0] = 1;

int i = 1;
for each dice do {
  input the next die, with f facets and values sides[0,..., f-1]

  for (k=0; k < f; k++) {
    for (j=f[k]+1; k < 1000; k++) {
      chances[i,j] = chances[i,j] + chances[i-1, j-f[k]]
    }
  }
}

From there, we try all possible lables and prune unacceptable combinations. In the worst case, search through 506* 1010 combinations. It is very slow in theory, but smart pruning can make it work in practice.

for (each possible value of face1) do {
  if (no inconcsistency) {
    for (each possible value of face2) do {
      for (each possible value of face3) do {
  
        . . . 

      } 
    } 
  } 
}

An inconsistency occurss if the chance for a given sum is strictly greater than the given chance. For example:

Initial Chances
1 2 3 4 5 6
0 0 9 39 39 9

Required Chances
7 8 9
9 1 1

In the above example, the possible values for face1 are:

When setting some value for a facet, make the respective update in the table of chances. If some chance value becomes too large, then consistency is violated and we can skip the inner loops.

There are a few ways to optimize this process:

The implementtion can use recursion in place of nested loops, but this will run a bit more slowly. In some cases, it is more difficult to represent the solution using nested loops -- i whcih case the recursion might be more appropriate.