Thursday, 14 May 2015

Линейность математического ожидания или Typewriter Monkey

"Абсолютно случайным образом ударяя по 
клавишам пишущей машинки, гипотетическая 
обезьяна рано или поздно напечатает одну 
из пьес Шекспира."
Вы знаете что такое математическое ожидание? Тогда сегодня я вам предлагаю поразмыслить над следующей задачкой по программированию Typewriter Monkey.

На прошлых выходных я участвовал в конкурсе Google Code Jam и единственная задача, которая не далась мне полностью оказалась задача про обезьян с печатными машинками.

Итак, для начала, вкратце об условиях конкурса. Условия задачи описаны в тексте по ссылке выше. Решать задачу можно на абсолютно любом языке программирования. На странице с заданием можно скачать два тестовых набора данных: маленький и большой. Данные в обоих случаях структурированы одинаково, отличие в ограничениях на начальные  условия и количество времени, которое можно потратить на генерирование ответа. 

В нашем случае ограничения следующие:
  • Маленький набор данных
    • 100 однотипных задач
    • 1 ≤ K ≤ 7
    • 1 ≤ L ≤ S ≤ 7
    • 4 минуты на генерирование корректного ответа
  • Большой набор данных
    • 100 однотипных задач
    • 1 ≤ K ≤ 100
    • 1 ≤ L ≤ S ≤ 100
    • 8 минут на генерирование корректного ответа
Я не буду переводить условия задачи и сосредоточусь на принципах решения, с вашего позволения. Здесь и далее, автор предполагает, что читатель внимательно ознакомился с условием задачи.

Маленький набор данных

Здесь всё просто: ограничения на данные достаточно низкие, а 4х минут хватит с лихвой для того чтобы сгенерировать все возможные строки длинной в 7 (или меньше) символов из предложенного алфавита и в каждой из них посчитать количество вхождений искомой строки. Максимум находится во время подсчёта, а мат. ожидание по старинной формуле:

, где х – кол-во вхождений искомой строки, а Р(х) – вероятность появления строк с ровно х вхождениями искомой строки.

Viva Brute force!

Большой набор данных

Здесь всё сложнее и именно этот набор послужил причиной для статьи. Brute Force для строк из 100 символов из большого алфавита займёт слишком много времени и даже близко не уложится в 8 минут. Приходится придумывать более изощрённые аналитические решения.

Подсчитать максимум не представляется сложным, а вот ожидаемое значение (или мат. ожидание) гораздо проблематичнее. Как ни странно всё начинается с той же самой формулы мат. ожидания: E[x] = Σ x*P(x)

Попробуем немного преобразовать равенство:

Σ x*P(x) = Σ x*COUNT(кол-во всевозможных строк, в которых ровно x вхождений искомой строки) / COUNT(все возможные строки, которые могут генерировать обезьяны)
В нашем случае всё логично – вероятность можно выразить подсчётом возможных исходов.

Как можно легко убедится: COUNT(все возможные строки, которые могут генерировать обезьяны) = KS

Итого: Σ x*P(x) = 1/KS * [Σ x*COUNT(кол-во всевозможных строк, в которых ровно x вхождений искомой строки)]

К сожалению, посчитать COUNT(кол-во всевозможных строк, в которых ровно x вхождений искомой строки) не очень легко, особенно с учётом того факта, что строки могут пересекаться (смотри условие задачи).

Тем не менее, если пойти дальше и заметить, что Σ x*COUNT(кол-во всевозможных строк, в которых ровно x вхождений искомой строки) есть ни что иное, как кол-во ВСЕХ появлений искомой строки среди всех возможных строк, которые генерируют обезьяны, то жизнь становится веселее.

Получается: Σ x*P(x) = 1/KS * COUNT(кол-во всевозможных появлений искомой строки)

Уже интересно. Осталось понять как лучше посчитать кол-во всех появлений искомой строки. Легче всего это сделать разбив задачу на кусочки, а именно посчитать сколько раз искомая строка может появится в самом начале строки длины S, сколько раз начиная с позиции 1, сколько раз начиная с позиции 2 и т.д.

Т.е. COUNT(кол-во всевозможных появлений искомой строки) = Σ COUNT(кол-во искомых строк на позиции i)

Вероятность появления искомой строки на разных позициях одинакова, а кол-во позиций с которых может начинаться искомая строка ровно (S-L+1), т.о.
Σ COUNT(кол-во искомых строк на позиции i) = (S-L+1) * COUNT(кол-во искомых строк на позиции 0)

Вернёмся к нашему равенству:
Σ x*P(x) = 1/KS * COUNT(кол-во всевозможных появлений искомой строки) =
= (S-L+1)/KS * COUNT(кол-во искомых строк на позиции 0)

Заметим, что
COUNT(кол-во искомых строк на позиции 0) = P(набора искомой строки обезьянами) * KL * KS-L
, где первые L символов образуют исходную строку, а оставшиеся S-L символов – любые из заданного алфавита.

Итого:
Σ x*P(x) = (S-L+1)/KS * COUNT(кол-во искомых строк на позиции 0) =
(S-L+1) KS * P(набора искомой строки обезьянами) * KL * KS-L =
(S-L+1) * P(набора искомой строки обезьянами)

Т.е. достаточно знать вероятность набора обезьянами искомой строки, что вычисляется очень просто из заданного алфавита.

Bingo!

P.S. По сути мы с вами сегодня вывели одно из свойств математического ожидания – линейность.




item

No comments:

Post a Comment