Я уже давно бьюсь над этой проблемой, и это действительно начинает меня расстраивать. Проблема в:
У меня есть набор символов, A
, B
, C
, и D
. Я должен сказать, сколько способов строка может быть построена из этих символов, когда длина n
и каждый символ должен встречаться даже раз.
Например, ответ для n = 2
4:
AA
BB
CC
DD
Ответ для n = 4
40. Некоторые из этих допустимых строк:
AAAA
AABB
CACA
DAAD
BCCB
Я застрял при разработке логики. Я чувствую, что для этого может быть решение DP. Грубое форсирование моего пути не может быть и речи: число решений быстро возрастает до огромных.
Я пытался нарисовать все виды идей на бумаге, но безрезультатно. Почти все эти идеи мне пришлось отбросить, поскольку их сложность была слишком большой. Решение должно быть эффективным для n = 10^4
.
Одна из моих идей состояла в том, чтобы никогда не отслеживать реальные строки, а только то, появлялись ли каждый символ четные или нечетные времена. Я не мог придумать способ применить эту логику.
Может кто-нибудь мне помочь?
источник
Ответы:
Установите
f(n,d)
как функцию, дающую количество перестановок (четной) длиныn
с использованиемd
различных символов (т.е.d=4
в вашем случае).Понятно
f(0,d) = 1
и так,f(n,1) = 1
как есть только одно расположение, когда у вас есть только один символ, или ноль пробелов.Теперь шаг индукции:
Чтобы создать правильную строку с использованием
d
символов, возьмите любую более короткую строку четной длины, используяd-1
символы, и увеличьте ее, добавив четное число, кратное этому новому символу. Количество аранжировокchoose(n,n_newdigits)
обусловлено тем, что вы можете выбиратьn_newdigit
места из общей длины строки, чтобы иметь новую цифру, а остальные заполняются по порядку исходной строкой.Чтобы реализовать это, используя наивную рекурсию в R, я сделал:
для интересующих вас типов чисел я бы подумал, что было бы более эффективно хранить числа в двумерном массиве и выполнять итерации по увеличению d, но это может зависеть от вашего выбора языка.
источник
Ответ Миффа определенно элегантен. Так как я все равно почти закончил, я все же предоставляю его. Хорошо, что я получаю тот же результат для n = 500 :-)
Пусть d будет количеством допустимых символов, d = 4 в вашем случае.
Пусть n будет длиной строки, в конечном итоге вы будете смотреть на четные значения n.
Пусть u будет количеством непарных символов в строке.
Пусть N (n, d, u) будет числом строк длины n, построенных из d различных символов и имеющих u непарных символов. Давайте попробуем вычислить N.
Есть немало угловых случаев для наблюдения:
u> d или u> n => N = 0
и <0 => N = 0
n% 2! = u% 2 => N = 0.
При переходе от n к n + 1 значение u должно увеличиваться на 1 или уменьшаться на 1, поэтому мы имеем рекурсию в соответствии с
N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1))
Сколько существует способов уменьшить тебя на одного. Это легко, потому что мы должны соединить один из непарных символов, что делает его просто u. Таким образом, вторая часть f будет читать (u + 1) * N (n-1, d, u + 1), с оговоркой, конечно, что мы должны заметить, что N = 0, если u + 1> n-1 или u +1> d.
Как только мы поняли это, легко увидеть, что является первой частью f: во сколько раз мы можем увеличить u, когда есть непарные символы u-1. Ну, мы должны выбрать один из (k- (u-1)) символов, которые являются парными.
Таким образом, принимая во внимание все угловые случаи, рекурсивная формула для N
N (n, d, u) = (d- (u-1)) * N (n-1, d, u-1) + (u + 1) * N (n-1, d, u + 1)
Я не собираюсь читать в http://en.wikipedia.org/wiki/Concrete_Matmatics, как решить рекурсию.
Вместо этого я написал немного кода Java. Опять же, немного более неуклюже, как и Java в любом случае из-за его многословия. Но у меня была мотивация не использовать рекурсию, так как она ломается слишком рано, по крайней мере, в Java, когда стек переполняется на 500 или 1000 уровнях вложенности.
Результат для n = 500, d = 4 и u = 0:
N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
вычисляется за 0,2 секунды, благодаря запоминанию промежуточных результатов. N (40000,4,0) вычисляется менее чем за 5 секунд. Код также здесь: http://ideone.com/KvB5Jv
источник
Я попытался найти решение, но потерпел неудачу и задал тот же вопрос на Mathematics.StackExchange . Благодаря Rus May , вот решение для Common Lisp:
Это всегда возвращает 0 для нечетных значений
n
. Дляn = 500
, вот вывод с SBCL :источник