Estou batendo no meu crânio com esse problema há algum tempo, e está realmente começando a me frustrar. O problema é:
Eu tenho um conjunto de caracteres, A
, B
, C
, e D
. Eu tenho que dizer de quantas maneiras uma string pode ser construída a partir desses caracteres, quando o comprimento é n
e cada caractere deve ocorrer mesmo vezes.
Por exemplo, a resposta para n = 2
4 é:
AA
BB
CC
DD
A resposta n = 4
é 40. Algumas dessas cadeias válidas são:
AAAA
AABB
CACA
DAAD
BCCB
Estou preso em inventar uma lógica. Eu sinto que poderia haver uma solução de DP para isso. Forçar brutalmente o meu caminho está fora de questão: o número de soluções cresce rapidamente em grandes números.
Eu tentei desenhar todos os tipos de idéias no papel, sem sucesso. Quase todas essas idéias eu tive que descartar, porque sua complexidade era muito grande. A solução deve ser eficiente para n = 10^4
.
Uma das minhas idéias era nunca acompanhar as seqüências reais, mas apenas se cada personagem apareceu em momentos pares ou ímpares. Não consegui encontrar uma maneira de aplicar essa lógica.
Alguém pode me ajudar?
fonte
Respostas:
Configure
f(n,d)
como uma função fornecendo o número de permutações de comprimento (par)n
usandod
caracteres distintos (por exemplo,d=4
no seu caso).Claramente
f(0,d) = 1
ef(n,1) = 1
como há apenas um arranjo quando você tem apenas um caractere ou zero espaços.Agora a etapa de indução:
Para criar uma sequência válida usando
d
caracteres, pegue uma sequência menor de caracteres pares usandod-1
caracteres e complete-a adicionando um múltiplo par desse novo caractere. O número de arranjos échoose(n,n_newdigits)
porque você pode escolhern_newdigit
locais fora do comprimento total da string para ter o novo dígito e o restante é preenchido pela string original em ordem.Para implementar isso usando recursão ingênua em R, eu fiz:
para o tipo de números que você está interessado, eu pensaria que seria mais eficiente armazenar números em uma matriz bidimensional e iterar com o aumento de d, mas isso pode depender da sua escolha de idioma.
fonte
A resposta de Miff é definitivamente elegante. Desde que eu terminei o meu de qualquer maneira, eu o forneço. O bom é que eu recebo o mesmo resultado para n = 500 :-)
Seja d o número de caracteres diferentes que são permitidos, d = 4 no seu caso.
Seja n o comprimento da string, em última análise, você verá valores pares de n.
Seja u o número de caracteres não emparelhados em uma string.
Seja N (n, d, u) o número de cadeias de comprimento n, construídas a partir de d caracteres diferentes e com u caracteres não emparelhados. Vamos tentar calcular N.
Existem alguns casos de esquina a serem observados:
u> d ou u> n => N = 0
u <0 => N = 0
n% 2! = u% 2 => N = 0.
Ao passar de n para n + 1, você deve aumentar em 1 ou diminuir em 1, para que tenhamos uma recursão de acordo com
N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1))
Quantas maneiras existem para reduzir você em um. Este é fácil, porque temos que emparelhar um dos u caracteres não emparelhados, o que o torna apenas u. Portanto, a segunda parte de f lerá (u + 1) * N (n-1, d, u + 1), com a ressalva, é claro, que devemos observar que N = 0 se u + 1> n-1 ou u +1> d.
Depois de entendermos isso, é fácil ver qual é a primeira parte de f: de quantas maneiras podemos aumentar u quando existem caracteres u-1 não pareados. Bem, temos que escolher um dos caracteres (k- (u-1)) que estão emparelhados.
Portanto, considerando todos os casos de canto, a fórmula recursiva para N é
N (n, d, u) = (d- (u-1)) * N (n-1, d, u-1) + (u + 1) * N (n-1, d, u + 1)
Não vou ler em http://en.wikipedia.org/wiki/Concrete_Mathematics como resolver a recursão.
Em vez disso, escrevi algum código Java. Novamente, um pouco mais desajeitado, assim como o Java, devido à sua verbosidade. Mas eu tinha a motivação de não usar a recursão, já que ela quebra muito cedo, pelo menos em Java, quando a pilha excede em 500 ou 1000 níveis de aninhamento.
O resultado para n = 500, d = 4 e u = 0 é:
N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360
calculado em 0,2 segundos, devido à memorização de resultados intermediários. N (40000,4,0) calcula em menos de 5 segundos. Código também aqui: http://ideone.com/KvB5Jv
fonte
Tentei encontrar uma solução, mas falhei e fiz a mesma pergunta no Mathematics.StackExchange . Graças a Rus May , aqui está uma solução, no Common Lisp:
Isso sempre retorna 0 para valores ímpares de
n
. Poisn = 500
, aqui está a saída com SBCL :fonte