Codebreaker 2bc08752a0894eb2c7afb345286e391d

30Sep/090

Permutation Generation

Someone asked me how to generate all possible permutations out of a given set and I thought it was kind of interesting to think about it. I was able to come up with a general solution pretty easily (thanks to all the algorithm and math classes I took!) and I would like to share it with everyone.

Recursive solution

For a given set, S,

p(S) = 
\begin{cases} 
  \{\emptyset\},  & \mbox{if } S = \emptyset \\
  \{\{s_1\} + p(S - \{s_1\}), \{s_2\} + p(S - \{s_2\}), ..., \\
    \mbox{  } \{s_n\} + p(S - \{s_n\}) \}, & \mbox{otherwise}
\end{cases}

whereas + and - signs denote set addition and set subtraction respectively.

Python implementation

def permutation(set):
        if set == []:
                return [[]]
        else:
                r = []
                for i in xrange(0, len(set)):
                        for p in permutation(set[0:i] + set[i+1:]):
                                r.append([set[i]] + p)
                return r

Results

>>> print permutation([1, 2, 3])
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

I wouldn't say this particular implementation is the most efficient one, but at least we know it works with up to a certain size of set. Sadly, we all have machines with a finite size of memory, our machine will produce stack overflow if your set is too big.

I think languages like Haskell would work very efficiently with this kind of recursive solution due to its beautiful features like lazy evaluation, tail recursion, etc. Please feel free to share your solution or implementation.

27Jan/090

Class Note for CS445 on Jan 27, 2009

Announcements

Optional discussion section meets on Wednesday, 7-8pm at FCS 101

Largest Empty Rectangle Problem

Divide and conquer

function DivideAndConquer(A, m, n) begin
    PrecomputeLeftRuns(...)
    PrecomputeRightRuns(...)
    return DivAndConq(A, m, 1, n, L, R)
end
DevAndConq()

T(m, n) = 2T(m, n/2) + \Theta(m^2) = \Theta(m^2n)

PrecomputeLeftRuns() + PrecomputeRightRus()

\Theta(mn) + \Theta(m^2n)

Total number of rectangles
[m 2]          * [n 2]           = m*(m-1)/2 * n*(n-1)/2
(pair of rows)   (pair of cols)  theta(m^2)  theta(n^2)

To sum up, for Largest Empty Rectangle, we've seen following algorithms:

  • Exhaustive search (\Theta(m^3n^3) time)
  • [Optimized exhaustive search (\Theta(m^2n^2) time)]
  • Divide and conquer (\Theta(m^2n) time)
  • [Dynamic programming (\Theta(m^2n) time)]
  • [Amortization (\Theta(mn) time)] (optimal)
20Jan/090

Class Note for SC445 on Jan 20, 2009

Announcements

  • Office hours posted on the course webpage: M-F
  • Optional discussion section will be on Wednesdays at 7-8pm

Problem (Largest Monochromatic Rectangle)

Given an m*n boolean array A[1:m, 1:n] of zeroes and ones, find a subarray A[i:k, j:l] that contains only zeros.

Exhaustive Search (examine all possibilities; pick the best one)

function exhaustive(A, m, n) begin * returns the area of the largest solution
a := 0
for i := 1 to m do
for j := 1 to n do
for k := i to m do
for l := j to n do begin
scan A[i:k, j:l]
if it contains only zeroes then
a := max{a, (k-i+1)*(l-j+1)}
end (for)

return a
end (function)
  • (a) scan - a := max{}
  • (b) for l:= j to n
  • (c) for k := i to m
  • (d) for j := 1 to n
  • (e) for i := 1 to m

Analysis

  • (a) \Theta((k-i)(l-j))

  • (b) \displaystyle
\sum_{j=l}^n\Theta((k-i)(l-j))\\
= \Theta(\sum_{j=l}^n (k-i)(l-j))\\
= \Theta((k-i)\sum_{j=l}^n(l-j))\\
= \Theta((k-i)\sum_{x=0}^{n-j}x)\\
= \Theta((k-i)\Theta(n-j)^2)\\
= \Theta((k-i)(n-j)^2)

  • (c) \displaystyle
\sum_{i=k}^m\Theta((k-i)(n-j)^2)\\
= \Theta(\sum_{i=k}^m(k-i)(n-j)^2)\\
= \Theta((n-j)^2 \sum_{i=k}^m(k-i))\\
= \Theta((n-j)^2 \sum_{x=0}^{m-1}x)

  • (d) \Theta((m-i)^2 n^3)

  • (e) \Theta(m^3 n^3) : time for exhaustive search without any optimization

Notations and Formulas

  • \Theta(f(n)) upper- and lower-bounded by constant times f(n)
  • O(f(n)) upper-bounded by constant times f(n)
  • \Omega(f(n)) lower-bounded by constant times f(n)

  • \sum \Theta(f(i)) = \Theta(\sum f(i)) (almost always)

  • \displaystyle \sum_{i=1}^n i^k = \Theta(n^{k+1})

Divide and Conquer

Cut the input array vertically at the middle column. Then the optimal rectangle falls into three cases:

  1. It lies strictly in left half. (recurse)
  2. It lies strictly in right half. (recurse)
  3. It spans the middle. (some work to do)

If we cut it at the middle column, it breaks into a left rectangle that ends at the middle column and is optimal, and a right rectangle that starts at the middle column and is optimal. (where the start and end rows i and k are fixed -> new subproblem!)