Created
April 21, 2018 22:14
-
-
Save AnthonyMikh/c96d2282483407b37c05d03513f1dcaf to your computer and use it in GitHub Desktop.
Решение задачи №87 от UniLecs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Решение напрямую | |
data Variant = Chosen | Skipped | |
deriving (Eq) | |
type Row = [Variant] | |
isValid :: Row -> Bool | |
isValid [] = True | |
isValid (x:[]) = True | |
isValid (x:y:ys) = ((x, y) /= (Chosen, Chosen)) && (isValid (y:ys)) | |
isNoChoose :: Row -> Bool | |
isNoChoose = all (== Skipped) | |
seed :: [Row] | |
seed = [[]] | |
expandRow :: Row -> [Row] | |
expandRow x = [Chosen:x, Skipped:x] | |
rows = iterate (concatMap expandRow) seed | |
countCorrect = length . filter id . map (\x -> isValid x && (not . isNoChoose) x) | |
-- Бесконечный список ответов | |
values = map countCorrect $ rows | |
answer n = values !! n | |
-- Решение по формуле | |
fibs = 0:1:zipWith (+) fibs (tail fibs) | |
values' = map (\x -> x - 1) . drop 2 $ fibs | |
answer' n = values' !! n | |
-- Вспомогательные функции для тестирования | |
assert cond = | |
if cond | |
then return () | |
else error "Assertion violated" | |
assertEq x y = | |
if x == y | |
then return () | |
else error $ "Assertion violated: first argument is " ++ show x ++ " but second argument is " ++ show y | |
assertEq' = uncurry assertEq | |
testThreshold = 20 | |
main = do | |
-- Тесты избранных вариантов | |
assertEq' (answer 1, 1) | |
assertEq' (answer 2, 2) | |
assertEq' (answer 3, 4) | |
assertEq' (answer' 1, 1) | |
assertEq' (answer' 2, 2) | |
assertEq' (answer' 3, 4) | |
-- Проверка того, что первые testTreshold ответов, | |
-- полученные разными способами, совпадают | |
mapM_ assertEq' . take testThreshold $ zip values values' | |
print "All tests passed" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Обоснование:
Назовём строку длины
n
из0
и1
правильной, если в ней нет двух идущих подряд1
. Легко доказать, что любая непрерывная подпоследовательность правильной последовательности сама является правильной, из чего следует, что невозможно из неправильной последовательности получить правильную приписыванием числа.Обозначим через
right(n)
число правильных последовательностей длиныn
.Теорема 1: для всех неотрицательных
n
справедливоright(n) = F(n+2)
, гдеF(n)
—n
-ое число Фибоначчи.Доказательство: для небольших
n
значенияright(n)
можно подсчитать непосредственно:Допустим, нам известно значение
right(k - 2)
иright(k - 1)
. Найдём, исходя из этого,right(k)
.Рассмотрим множество всех правильных последовательностей длины
k - 1
. Из каждой последовательности этого множества можно получить правильную последовательность, приписав к концу цифру, не совпадающую с последней. Этот вариант даёт намright(k - 1)
последовательностей. Приписать же цифру, совпадающую с последней, можно только в том случае, если последняя цифра0
. Условие окончания правильной последовательности на0
не налагает никаких дополнительных ограничений на последовательность, поэтому этот вариант даёт намright(k - 1 - 1) = right(k - 2)
последовательностей. Т. к. рассматриваемые варианты взаимно исключают друг друга и исчерпывают все возможности получения правильных последовательностей длиныk
, получаемИспользуя принцип математической индукции, доказываем требуемое для всех неотрицательных
n
. ◼Обозначим через
answer(n)
ответ на вопрос задачи, т. е. количество способов выбрать из шеренги вn
человек нескольких так, чтобы среди них не было стоящих рядом.Теорема 2: для любого натурального
n
справедливоanswer(n) = F(n + 2) - 1
.Доказательство: для каждого подобного выбора, где
n > 0
, можно построить последовательность из1
и0
, гдеi
-тая цифра равна1
, если человек под этим номером выбран, и0
, если верно противное. Из условия задачи следует, что соответствующая последовательность является правильной. Таким образом, существует инъективное отображение из множества выборов во множество правильных последовательностей. Обратное не верно — для последовательности, состоящей из одних нулей, нет прообраза во множестве выборов, поэтомуanswer(n) = right(n) - 1
. Используя теорему 1, получаемanswer(n) = F(n + 2) - 1
.Для
n = 0
, очевидно, число выборов равно нулю, ноF(0 + 2) - 1 = 1 - 1 = 0
Т. о., получаем, что формула из условия теоремы верна для любых натуральных
n
. ◼