Random Permutation

Posted on Monday 4 June 2007

So, I’ve totally been writing this blog for almost a week, but so far I haven’t talked about how an actual algorithm works. Here’s a classic algorithm.

Let’s say you have an array of n elements a[0] through a[n-1]. You would like to generate a random permutation of this array. This means that any ordering of the array is equally probable, which means that if you look at any position in a, any element has an equal chance of being there: 1 in n (1/n). We assume that we have the function r(n) which generates a uniform random integer between 0 and n-1, inclusive. (In C, you could do this with random() % n, and in Java you’d do (int)(Math.random() * n).)

On two occasions while interviewing, I have been asked for an algorithm to permute an array. Both times the interviewer was amazed when I showed them this algorithm. Many people assume that a second storage array is necessary for remembering numbers you have picked, but you can do it in place with no memory. The algorithm (usually credited to Knuth, but see below) is quite simple:

  • For i = 1 to n-1:
    • Let j = r(i+1)
    • Swap a[i] and a[j]

My guess as to why more people don’t know it is due to its interesting use of induction. Software engineers generally learn of induction in college, fear it, learn how to parrot back enough to pass the classes that use it, then promptly forget it. But recursion is ubiquitous and unavoidable. Whether explicitly recursing with a function calling itself, or implicitly in a loop, any time later steps assume that earlier steps have done their work over part of the data, you need induction. Instead of fearing induction, learn how it works and learn how you can rely on it to do more with your data.

So how does the algorithm use induction? Suppose we are in iteration i. Assume inductively that a[0]…a[i-1] are a random permutation of that part of the array. As a base case, a[0] starts as being a permutation of itself. Now consider what the array will look like after the swap. Element i has an equal chance of being in any position 0…i. What about the remaining elements? Each one has a (i-1)/i chance of standing still and a 1/i chance of being put in the ith spot. If it stands still, it had a 1/(i-1) chance of being there by the induction hypothesis. But multiplying this by the stand-still probability, we get a 1/i chance of it being there after the swap. Together with the swap probability, this means that it has a 1/i chance of being in any spot, which is the definition of a uniform permutation.

As a side note, Wikipedia gives a different version:

  • For i = 0 to n-2:
    • Let j = i + r(n-i)
    • Swap a[i] and a[j]

This has the advantage that as soon as the i-loop visits a location, its value is finalized so you can output the permutation as it is being created. The disadvantage is that it’s “less inductive” in that, after the ith iteration, you don’t have a true permutation of the first i elements, but only part of a permutation of all n elements. Also, if you are simply generating a random permutation of the numbers 0…n-1 (or analogously the result of some function), you can replace a[i] with i (or f(i)) in the first version and you don’t need to initialize the array. This doesn’t work in the second because of its “future swaps”. This is the version that most people give. A proof of its correctness is given here.

What are some uses of random permutations? They can be good for generating test data, e.g. for a sorting function or any other function which should be able to deal with random data. They can be good as a starting point for a hill-climbing algorithm, e.g. with Traveling Salesman. And, of course, they’re great for asking about in interviews.

Update: I’m overwhelmed by the response! Thanks for all the replies, on here and Reddit. Here are some clarifications:

  • I agree that random() % n is not perfectly uniform, but neither is (int)(Math.random() * n) due to the finite precision of the floating point number. Both of them are pretty close for small values of n. We could do better, but it’s outside the scope of this post. Unlike rand, random is supposed to have good entropy in the low order bits.
  • Swapping i with r(n) is a simplification. This doesn’t give you a uniform distribution, however. As one reader mentioned, this page gives the lowdown, as do several commenters on reddit.
  • Someone took issue with the first algorithm. However, they cited that same page, which is only about the i-with-rand(n) issue (and a[i] remaining stationary). If you can find a fundamental flaw with my proof, please tell!

Again, thanks, and I hope you keep reading!


435 Comments for 'Random Permutation'

  1.  
    June 5, 2007 | 6:46 am
     

    Two modifications that occur to me (in the interest of simplifying code) would be:

    For i = 0 to n-1:
    * Let j = r(n)
    * Swap a[i] and a[j]

    and even better — getting rid of the potential boundary error in the for loop

    For i = 0 to TIMESTHROUGH
    * Let j = r(n)
    * Let k = r(n)
    * Swap a[i] and a[j]

    The second one is obviously correct, as TIMESTHROUGH approaches infinity. Both would require some analysis to find out how bad their fall-off from the perfect solution is. How large a value of TIMESTHROUGH do you really need, for instance?

    Of course, in actual (C++) code, I’d just write:

    //where v is a container transversed by random access iterators
    random_shuffle(v.begin(), v.end());

  2.  
    June 5, 2007 | 6:47 am
     

    Should be this in the second algorithm
    * Swap a[j] and a[k]

  3.  
    June 5, 2007 | 6:50 am
     

    From an analytical point of view your solution is quite elegant. But from a practical one I would propose Ruby:
    [1, 2, 4, 5].sort_by {rand}
    Regards, polzr

  4.  
    Anson
    June 5, 2007 | 7:01 am
     

    Great blog, I like what you have so far!

  5.  
    June 5, 2007 | 7:05 am
     

    That was a real eye-opener, and obvious once it’s explained. Time for me to re-read Kunth, I think…

  6.  
    June 5, 2007 | 7:08 am
     

    random() % n is not uniform since it prefers lower numbers to higher depending on divisibility of n and RAND_MAX. If RAND_MAX would be 25 and n=10, you will get 3/26 probability of numbers 0 to 5 and 2/26 for numbers 6 to 9.

    Otherwise I like the simplicity of permutaion algorithm.

  7.  
    June 5, 2007 | 7:12 am
     

    the wikipedia is, if the phrase means anything at all, “more inductive”, not less. on each loop, you have an array, from which you pick one element at random. that leaves you with a smaller array remaining. the induction terminates when there is only one element (or none - either case is trivial).

    look here - http://en.wikipedia.org/wiki/Mathematical_induction - and see how the empty array corresponds to the first step and the process of extracting one number corresponds to the second step.

  8.  
    June 5, 2007 | 7:25 am
     

    […] June 5th, 2007 [link][more] […]

  9.  
    June 5, 2007 | 7:51 am
     

    well n be better be pretty small or the random number generator be really really beefy, because n! is super huge, bigger then most random gens cycle.

    reddit +1

  10.  
    EvilKayak
    June 5, 2007 | 8:06 am
     

    I have no clue what this means.. Can someone show an example and write back to me pls.

    thx.

  11.  
    pdw
    June 5, 2007 | 8:18 am
     

    random() % n will only give you an uniform random integer if n happens to divide RAND_MAX :)

  12.  
    Mike
    June 5, 2007 | 8:34 am
     

    Random permutations are also great for implementing bogosort.

  13.  
    June 5, 2007 | 8:48 am
     

    Hi there,

    interesting stuff ! Keep up the good work :)
    Here a ruby quickhack to visualize your algorithm on the CLI… (was just curious..)

    #!/usr/bin/ruby -w
    #

    array = Array.new

    0.upto(20) { |n| array.push(n) }

    def permutate array
    1.upto( array.length - 1 ) do |i|
    j = rand( i + 1 )
    array[i], array[j] = array[j], array[i]
    end
    array
    end

    def print array
    array.each_with_index do |number, index|
    printf(”%2i | %s\n”, index, (” “*number+”*”))
    end
    end


    Result:

    0 | *
    1 | *
    2 | *
    3 | *
    4 | *
    5 | *
    6 | *
    7 | *
    8 | *
    9 | *
    10 | *
    11 | *
    12 | *
    13 | *
    14 | *
    15 | *
    16 | *
    17 | *
    18 | *
    19 | *
    20 | *
    “—”
    0 | *
    1 | *
    2 | *
    3 | *
    4 | *
    5 | *
    6 | *
    7 | *
    8 | *
    9 | *
    10 | *
    11 | *
    12 | *
    13 | *
    14 | *
    15 | *
    16 | *
    17 | *
    18 | *
    19 | *
    20 | *

    Cheers !

  14.  
    Josh McFarlane
    June 5, 2007 | 9:02 am
     

    Just as a warning, doing random() % n is not guaranteed to produce random numbers in C. Many of the pseudo random number generators pretty much follow a set pattern for the lower bits, so using random() % n for small values of n can produce a visible pattern.

    Only by using all the bits of the randomly generated value can you ensure truly pseudo-random numbers.

  15.  
    mr. lol
    June 5, 2007 | 9:13 am
     

    lol?

  16.  
    Luke Gorrie
    June 5, 2007 | 9:43 am
     

    I’d like to mention another random-shuffling algorithm that is less efficient but particularly easy to understand:

    1. Assign a random weight (e.g. floating point number) to each element
    2. Sort by weight
    3. Remove the weights

    Here’s an example implementation in Erlang:

    random_perm(L) ->
    Weighted = [{random:uniform(), E} || E

  17.  
    Adam Langley
    June 5, 2007 | 9:45 am
     

    > uniform random integer between 0 and n-1, inclusive. (In C, you could do this with random() % n

    Except that random() returns uniformly (depending on libc) from 0..RAND_MAX which is 0..2147483647 on my system. However, this is prime (mersenne 31), so n never divides it (unless your list is really long) and so the modulus operation never produces a uniform random number.

  18.  
    June 5, 2007 | 11:19 am
     

    Very cool. There’s an extensive comment thread on Reddit which you might be interested by…

  19.  
    June 5, 2007 | 11:36 am
     

    Ah, you submitted it. *headsmack*

  20.  
    Evan D
    June 5, 2007 | 1:28 pm
     

    What are some uses of random permutations? They can be good for generating test data, e.g. for a sorting function or any other function which should be able to deal with random data. They can be good as a starting point for a hill-climbing algorithm, e.g. with Traveling Salesman. And, of course, they’re great for asking about in interviews.

    And, of course, it’s good for card shuffling. Which not everyone gets right.

    (I’m hoping HTML works…)

  21.  
    Evan D
    June 5, 2007 | 1:31 pm
     

    BTW, rand() % n isn’t a *good* way to get a random number from 0 to n-1 in C; many random number generators have less entropy in the lower bits than the upper ones. Read this (search for “lower”) and this.

    Use this instead:
    rand() / (RAND_MAX / N + 1)

  22.  
    June 5, 2007 | 1:34 pm
     

    Very cool! Not bad, you got in the del.icio.us popular links in ur first week :)

  23.  
    MrX_TLO
    June 5, 2007 | 1:41 pm
     

    This was actually common on 8 bit systems.

  24.  
    OC
    June 5, 2007 | 2:01 pm
     

    Nice Blog! Hope you keep it up and you were already reddited. ;-)

  25.  
    June 5, 2007 | 3:55 pm
     

    haha, great post - thank you ! :)

  26.  
    June 5, 2007 | 4:54 pm
     

    […] This post on Algo Blog about random permutation shows a cool a little algorithm for randomizing an array which apparently impresses interviewers. I thought it might be fun to write it in Ruby. I wrapped the algorithm in a reusable function as well: def permute_array(a) 1.upto(a.length - 1) do |i| j = rand(i + 1) a[i], a[j] = a[j], a[i] end a end # alternate version def permute_array2(a) 0.upto(a.length - 2) do |i| j = rand(a.length - i) a[i], a[j] = a[j], a[i] end a end […]

  27.  
    Ricardo
    June 5, 2007 | 7:03 pm
     

    I think in the first loop it is i = 0
    but correct me if I am wrong

    Nice to know for a future job interview
    Nice post

  28.  
    simon francis
    June 5, 2007 | 7:35 pm
     
  29.  
    June 6, 2007 | 12:44 am
     

    Very useful article mate. In fact it inspired me so much that I wrote the algorithm in Python. Here is my implementation:

    import random

    array = [2,3,-1,9,8,11,99] #The array to be permuted.

    # This could be in a loop to generate the random permutations and print them as well.

    for i in range(len(array)-1):
    j=random.randint(0,i+1)
    array[i], array[j] = array[j], array[i]

    print array

    Just for an afternoon break ;-). It is indeed a very useful piece of algorithm. Thanks for sharing it.

  30.  
    David
    June 6, 2007 | 1:22 am
     

    @ MrX_TLO

    It also works for 16 bit systems, as well as 32 bit systems, if you have the time and the right quote.

    Also, RAND_MAX only works with odd calculations.

    There is no cow level.

  31.  
    jpl
    June 6, 2007 | 1:50 am
     

    Python module “random” comes with a shuffle method that does this in place permutation.
    You can even pass your own random function as argument. Take a look at random.py in the python distribution, certainly funnier than C or Java.

  32.  
    June 6, 2007 | 6:05 am
     

    […] Algo Blog » Random Permutation (tags: algorithms programming) […]

  33.  
    June 6, 2007 | 9:05 pm
     

    @Simon
    The article you linked shows that the following is wrong.
    for (i is 1 to n)
    Swap i with random position between 1 and n
    This is a different algorithm than the one presented on this blog. The cigital article algorithm (for a size 3 list) randomly swaps item 1 into a random position from 1 to 3, and does the same for items 2 and 3.

    The algorithm here first swaps item 1 between position 1 and 2, then item 2 into a position from 1 to 3.

    Just try both of them out, you’ll see the difference immediately.

  34.  
    June 7, 2007 | 5:30 am
     

    Hmm, I think this is a valid python implementation

    import random

    def permute(a):
    for i in range(0,len(a)):
    j = random.randrange(0,i + 1)
    a[i],a[j] = a[j],a[i]
    return a

  35.  
    itsame
    June 30, 2007 | 4:50 pm
     

    hi

  36.  
    Peter Wilson
    September 9, 2007 | 10:59 am
     

    Some people have asked what use a random permutation is. I maintain a ski racing program which uses the Knuth algorithm to produce the start order.

  37.  
    alert("WAL");
    September 10, 2007 | 1:38 am
     

    alert(”WAL”);

  38.  
    Hey
    September 10, 2007 | 1:38 am
     

    Nice site

  39.  
    December 15, 2007 | 8:10 am
     

    2955c6211c754ff9ef37…

    2955c6211c75…

  40.  
    December 15, 2007 | 5:05 pm
     

    buy wholesale adderall…

    news…

  41.  
    December 16, 2007 | 5:08 pm
     

    side effects of drug lexapro…

    news…

  42.  
    December 16, 2007 | 5:11 pm
     

    buy percocet…

    news…

  43.  
    December 16, 2007 | 5:14 pm
     

    buy viagra online…

    news…

  44.  
    December 16, 2007 | 5:17 pm
     

    buy hydrocodone…

    news…

  45.  
    December 16, 2007 | 5:20 pm
     

    discount phentermine…

    news…

  46.  
    December 17, 2007 | 5:38 pm
     

    long term use of percocet…

    news…

  47.  
    December 17, 2007 | 5:40 pm
     

    buy adderall without a prescription…

    news…

  48.  
    December 17, 2007 | 5:42 pm
     

    ritalin…

    news…

  49.  
    December 17, 2007 | 5:44 pm
     

    discount viagra…

    news…

  50.  
    December 17, 2007 | 5:46 pm
     

    buy hydrocodone without a prescription…

    news…

  51.  
    December 17, 2007 | 5:49 pm
     

    buy ambien…

    news…

  52.  
    December 18, 2007 | 5:09 pm
     

    amoxicillin acne…

    news…

  53.  
    December 18, 2007 | 11:41 pm
     

    ephedra yellow swarm…

    news…

  54.  
    December 19, 2007 | 5:24 pm
     

    buy cialis…

    news…

  55.  
    December 19, 2007 | 5:27 pm
     

    oxycodone online…

    news…

  56.  
    December 19, 2007 | 5:29 pm
     

    online phentermine…

    news…

  57.  
    December 19, 2007 | 5:31 pm
     

    buy vicodin without script…

    news…

  58.  
    December 19, 2007 | 5:33 pm
     

    vaspro ephedrine…

    news…

  59.  
    December 20, 2007 | 5:29 pm
     

    valium no prescription…

    news…

  60.  
    December 20, 2007 | 5:31 pm
     

    fioricet cod…

    news…

  61.  
    December 20, 2007 | 5:34 pm
     

    lexapro medicine…

    news…

  62.  
    December 20, 2007 | 5:36 pm
     

    wellbutrin xl…

    news…

  63.  
    December 21, 2007 | 5:40 pm
     

    hydrocodone apap…

    news…

  64.  
    December 21, 2007 | 5:43 pm
     

    what is oxycodone…

    news…

  65.  
    December 21, 2007 | 5:45 pm
     

    buy phentermine…

    news…

  66.  
    December 21, 2007 | 5:52 pm
     

    biotek ephedrine…

    news…

  67.  
    December 21, 2007 | 5:55 pm
     

    buy valium online…

    news…

  68.  
    December 22, 2007 | 6:03 pm
     

    oxycodone extraction iv…

    news…

  69.  
    December 23, 2007 | 6:05 pm
     

    percocet 93-490…

    news…

  70.  
    December 23, 2007 | 6:09 pm
     

    adderall without a prescription…

    news…

  71.  
    December 23, 2007 | 6:13 pm
     

    ritalin online…

    news…

  72.  
    December 24, 2007 | 5:37 pm
     

    side effects of wellbutrin…

    news…

  73.  
    December 24, 2007 | 5:41 pm
     

    pill propecia…

    news…

  74.  
    December 24, 2007 | 5:44 pm
     

    zoloft common side effects…

    news…

  75.  
    December 24, 2007 | 5:47 pm
     

    ephedra pills…

    news…

  76.  
    December 25, 2007 | 6:01 pm
     

    tramadol side effects…

    news…

  77.  
    December 25, 2007 | 6:05 pm
     

    adderall…

    news…

  78.  
    December 25, 2007 | 6:09 pm
     

    phentermine without prescription…

    news…

  79.  
    December 25, 2007 | 6:12 pm
     

    hydrocodone no prescription…

    news…

  80.  
    December 26, 2007 | 6:04 pm
     

    xanax side effects…

    news…

  81.  
    December 26, 2007 | 6:07 pm
     

    health risks of ephedrine…

    news…

  82.  
    December 26, 2007 | 6:09 pm
     

    free viagra…

    news…

  83.  
    December 26, 2007 | 6:13 pm
     

    ephedra…

    news…

  84.  
    December 27, 2007 | 5:45 pm
     

    buy amoxicillin without prescription…

    news…

  85.  
    December 27, 2007 | 5:48 pm
     

    generic percocet…

    news…

  86.  
    December 28, 2007 | 6:03 pm
     

    natural herb reverse impotence from propecia…

    news…

  87.  
    December 28, 2007 | 6:07 pm
     

    buy valium…

    news…

  88.  
    December 28, 2007 | 6:11 pm
     

    meridia no prescription…

    news…

  89.  
    December 28, 2007 | 6:15 pm
     

    buy fioricet…

    news…

  90.  
    December 29, 2007 | 11:40 am
     

    nice article…

    nice article…

  91.  
    December 29, 2007 | 6:14 pm
     

    viagra hgh…

    news…

  92.  
    December 29, 2007 | 6:16 pm
     

    propecia and online drugs stores…

    news…

  93.  
    December 29, 2007 | 6:20 pm
     

    prescription weight loss medications - meridia…

    news…

  94.  
    December 29, 2007 | 6:22 pm
     

    fioricet free shipping…

    news…

  95.  
    December 29, 2007 | 6:24 pm
     

    soma and addiction…

    news…

  96.  
    December 30, 2007 | 1:37 pm
     

    nice article…

    nice article…

  97.  
    December 30, 2007 | 5:59 pm
     

    nice article…

    nice article…

  98.  
    December 30, 2007 | 6:19 pm
     

    side effects of percocet…

    news…

  99.  
    December 30, 2007 | 6:22 pm
     

    long-term side effects of ritalin…

    news…

  100.  
    December 30, 2007 | 6:24 pm
     

    hydrocodone…

    news…

  101.  
    December 30, 2007 | 8:19 pm
     

    nice article…

    nice article…

  102.  
    December 31, 2007 | 9:36 pm
     

    Your Honours save us from…

    Justice Brian Sully at his Supreme Court retirement ceremony gave an all-embracing spray to law reformers (”unstable obsessives”), bureaucrats (”all…

  103.  
    January 1, 2008 | 5:06 pm
     

    ephedra liquid gel products…

    news…

  104.  
    January 4, 2008 | 2:29 am
     

    nice article…

    nice article…

  105.  
    January 4, 2008 | 2:31 am
     

    nice article…

    nice article…

  106.  
    January 4, 2008 | 2:31 am
     

    nice article…

    nice article…

  107.  
    January 4, 2008 | 2:52 am
     

    nice article…

    nice article…

  108.  
    January 4, 2008 | 2:54 am
     

    nice article…

    nice article…

  109.  
    January 4, 2008 | 2:55 am
     

    nice article…

    nice article…

  110.  
    January 4, 2008 | 2:55 am
     

    nice article…

    nice article…

  111.  
    January 4, 2008 | 5:29 am
     

    Schaffner EMC Increases Attenuation Performance…

    … household appliance, test and measurement, power supply and electric data processing industries. In addition, the filter’s low leakage…

  112.  
    January 5, 2008 | 5:05 am
     

    nice article…

    nice article…

  113.  
    January 5, 2008 | 8:26 am
     

    nice article…

    nice article…

  114.  
    January 5, 2008 | 8:27 am
     

    nice article…

    nice article…

  115.  
    January 5, 2008 | 8:28 am
     

    nice article…

    nice article…

  116.  
    January 5, 2008 | 8:29 am
     

    nice article…

    nice article…

  117.  
    January 5, 2008 | 8:30 am
     

    nice article…

    nice article…

  118.  
    January 5, 2008 | 8:31 am
     

    nice article…

    nice article…

  119.  
    January 5, 2008 | 8:33 am
     

    nice article…

    nice article…

  120.  
    January 5, 2008 | 8:33 am
     

    nice article…

    nice article…

  121.  
    January 5, 2008 | 8:34 am
     

    nice article…

    nice article…

  122.  
    January 5, 2008 | 8:35 am
     

    nice article…

    nice article…

  123.  
    January 5, 2008 | 8:36 am
     

    nice article…

    nice article…

  124.  
    January 5, 2008 | 8:37 am
     

    nice article…

    nice article…

  125.  
    January 5, 2008 | 8:37 am
     

    nice article…

    nice article…

  126.  
    January 6, 2008 | 3:23 am
     

    nice article…

    nice article…

  127.  
    January 6, 2008 | 3:24 am
     

    nice article…

    nice article…

  128.  
    January 6, 2008 | 3:24 am
     

    nice article…

    nice article…

  129.  
    January 6, 2008 | 3:25 am
     

    nice article…

    nice article…

  130.  
    January 6, 2008 | 3:26 am
     

    nice article…

    nice article…

  131.  
    January 6, 2008 | 3:27 am
     

    nice article…

    nice article…

  132.  
    January 6, 2008 | 3:27 am
     

    nice article…

    nice article…

  133.  
    January 6, 2008 | 3:28 am
     

    nice article…

    nice article…

  134.  
    January 6, 2008 | 3:28 am
     

    nice article…

    nice article…

  135.  
    January 6, 2008 | 3:29 am
     

    nice article…

    nice article…

  136.  
    January 6, 2008 | 3:29 am
     

    nice article…

    nice article…

  137.  
    January 6, 2008 | 3:30 am
     

    nice article…

    nice article…

  138.  
    January 6, 2008 | 3:30 am
     

    nice article…

    nice article…

  139.  
    January 6, 2008 | 3:31 am
     

    nice article…

    nice article…

  140.  
    January 6, 2008 | 3:32 am
     

    nice article…

    nice article…

  141.  
    January 6, 2008 | 3:32 am
     

    nice article…

    nice article…

  142.  
    January 6, 2008 | 12:11 pm
     

    New High-Tech Products for Underwater…

    The Intova IC700 Underwater Digital Camera 7MP has Image resolution up to 2816 x 2112 with a 4x digital zoom….

  143.  
    January 6, 2008 | 5:31 pm
     

    Checking the Checkers - The…

    The armed Securitas security guard in question was able to get state certification to work in Portland from the Department…

  144.  
    January 8, 2008 | 7:07 am
     

    nice article…

    nice article…

  145.  
    January 8, 2008 | 7:09 am
     

    nice article…

    nice article…

  146.  
    January 8, 2008 | 7:09 am
     

    nice article…

    nice article…

  147.  
    January 8, 2008 | 7:10 am
     

    nice article…

    nice article…

  148.  
    January 8, 2008 | 7:10 am
     

    nice article…

    nice article…

  149.  
    January 8, 2008 | 7:11 am
     

    nice article…

    nice article…

  150.  
    January 8, 2008 | 7:12 am
     

    nice article…

    nice article…

  151.  
    January 8, 2008 | 7:12 am
     

    nice article…

    nice article…

  152.  
    January 8, 2008 | 7:13 am
     

    nice article…

    nice article…

  153.  
    January 8, 2008 | 7:14 am
     

    nice article…

    nice article…

  154.  
    January 8, 2008 | 7:15 am
     

    nice article…

    nice article…

  155.  
    January 8, 2008 | 7:16 am
     

    nice article…

    nice article…

  156.  
    January 8, 2008 | 7:17 am
     

    nice article…

    nice article…

  157.  
    January 8, 2008 | 7:17 am
     

    nice article…

    nice article…

  158.  
    January 8, 2008 | 7:18 am
     

    nice article…

    nice article…

  159.  
    January 8, 2008 | 7:19 am
     

    nice article…

    nice article…

  160.  
    January 8, 2008 | 7:20 am
     

    nice article…

    nice article…

  161.  
    January 8, 2008 | 7:21 am
     

    nice article…

    nice article…

  162.  
    January 8, 2008 | 7:21 am
     

    nice article…

    nice article…

  163.  
    January 8, 2008 | 7:22 am
     

    nice article…

    nice article…

  164.  
    January 8, 2008 | 7:23 am
     

    nice article…

    nice article…

  165.  
    January 8, 2008 | 7:23 am
     

    nice article…

    nice article…

  166.  
    January 8, 2008 | 7:25 am
     

    nice article…

    nice article…

  167.