Permutations, Combinations & Probability (Part 2)

Monte Carlo Method

I seem to be having a bit of a probability-fest at the moment. I enjoyed reading Jeff Atwood’s probability puzzle earlier this week: The Problem of the Unfinished Game and the explanation: Finishing The Game. And I’m in the middle of reading Fooled by Randomness: The Hidden Role of Chance in Life and in the Markets, a book that Chris Roos recommended to me a while back – I’m really enjoying it. The author is a big fan of the Monte Carlo method which is effectively how I solved the probability problem in my previous post

So when Nathan Zook emailed me to suggest that I might have got my previous calculation wrong, I was interested enough to do what I should have done originally i.e. write some tests to check my solution. I extracted the critical bit of logic from both my solution and from Nathan’s solution and wrote tests against this method.

Nathan’s Solution

def matches(index, base)
  counts = [0] * base
  4.times do
    counts[index % base] += 1
    index /= base
  end
  count = 0
  4.times do
    count += counts[index % base]
    index /= base
  end
  count
end

James’ Solution

def matches(index, base)
  places = 4
  permutation = "%0#{places * 2}d" % index.to_s(base)
  original = (0...places).map { |i| permutation[i].chr }
  guess = (places...(places * 2)).map { |i| permutation[i].chr }
  matches = 0
  guess.each do |g|
    (0...places).each do |i|
      if g == original[i]
        original[i] = nil
        matches += 1
        break
      end
    end
  end
  matches
end

Failing Tests

You can see the full set of tests here, but the critical ones turn out to be the ones below. It appears that Nathan’s solution doesn’t work when there are duplicates. But I’m glad he pushed me into writing these tests – now I have more confidence in the answer I calculated previously.

def test_should_have_two_duplicate_matches_in_same_positiion
  assert_equal 2, matches(11341178, 10)
end

def test_should_have_two_duplicate_matches_in_different_positiion
  assert_equal 2, matches(11345611, 10)
end

def test_should_have_three_duplicate_matches_in_same_positiion
  assert_equal 3, matches(11141118, 10)
end

def test_should_have_three_duplicate_matches_in_different_positiion
  assert_equal 3, matches(11145111, 10)
end

def test_should_have_four_duplicate_matches
  assert_equal 4, matches(11111111, 10)
end

def test_should_have_one_match_when_digit_appears_twice_on_lhs_but_once_on_rhs
  assert_equal 1, matches(11345178, 10)
end

def test_should_have_one_match_when_digit_appears_once_on_lhs_but_twice_on_rhs
  assert_equal 1, matches(12345118, 10)
end

def test_should_have_two_matches_when_digit_appears_three_times_on_lhs_but_twice_on_rhs
  assert_equal 2, matches(11145118, 10)
end

def test_should_have_two_matches_when_digit_appears_twice_on_lhs_but_three_times_on_rhs
  assert_equal 2, matches(12315111, 10)
end

def test_should_have_three_matches_when_digit_appears_four_times_on_lhs_but_three_times_on_rhs
  assert_equal 3, matches(11115111, 10)
end

def test_should_have_three_matches_when_digit_appears_three_times_on_lhs_but_four_times_on_rhs
  assert_equal 3, matches(11141111, 10)
end

TextMate Bug

While running these tests, I noticed a bug in TextMate’s Ruby syntax highlighting, which I’ve now reported


textmate-ruby-syntax-highlighting-bug