Estou trabalhando no software para uma máquina que cortará automaticamente as unhas dos pés, para que os usuários possam simplesmente colocar os pés nela e executá-la em vez de ter que fazer isso manualmente, mordendo-as ou usando um cortador de unhas.
Uma porcentagem considerável de nossa base de usuários em potencial provavelmente será judia e, evidentemente, há uma tradição de não aparar as unhas dos pés ( ou unhas ) em ordem sequencial
Parece haver opinião divergente sobre a aplicação precisa dessa tradição, mas pensamos que as seguintes regras são suficientes para acomodar pessoas cujas práticas religiosas proíbem cortar as unhas dos pés na ordem:
- Duas unhas adjacentes não devem ser cortadas consecutivamente
- A sequência de corte do pé esquerdo não deve corresponder à sequência do pé direito
- A sequência de corte em duas execuções consecutivas não deve ser a mesma. As sequências não devem ser facilmente previsíveis, portanto, codificar uma sequência alternada não funciona.
É assim que decidimos numerar os dedos dos pés:
5 4 3 2 1 1 2 3 4 5
Left foot Right foot
Eu escrevi um código para resolver o problema, mas o algoritmo usado é abaixo do ideal: na verdade, o desempenho do pior caso é O (∞) . A forma como funciona é comparável ao bogosort . Aqui está uma simplificação de pseudocódigo do código real usado:
function GenerateRandomSequence
sequence = Array[5]
foreach (item in sequence)
item = RandomNumberBetween(1,5)
return sequence
function GetToenailCuttingOrder
while (true)
sequence = GenerateRandomSequence()
if (!AllItemsAreUnique(sequence))
continue
if (NoTwoAdjacentItemsHaveConsecutiveNumbers(sequence))
return sequence
do
leftFootSequence = GetToenailCuttingOrder()
rightFootSequence = GetToenailCuttingOrder()
until (leftFootSequence != rightFootSequence &&
leftFootSequence != leftFootSequenceFromLastRun &&
rightFootSequence != rightFootSequenceFromLastRun)
Basicamente, ele gera sequências aleatórias e verifica se atendem aos critérios. Se não atender aos critérios, é reiniciado. Não leva um tempo ridiculamente longo, mas é muito imprevisível.
Percebo que a maneira como estou fazendo isso é terrível, mas estou tendo problemas para encontrar uma maneira melhor. Algum de vocês pode sugerir um algoritmo mais elegante e de desempenho?
fonte
Respostas:
Você pode gerar todas as sequências de corte de unha possíveis sem restrições e, em seguida, filtrar todas as sequências que violam a regra judaica. Felizmente, os humanos têm apenas cinco dedos por pé *, então existem apenas 5! = 120 sequências irrestritas.
Exemplo Python:
Para impor sua regra de "não repetir a mesma sequência", você pode escolher apenas quatro das sequências acima e usá-las alternadamente. O único problema aqui é que se você contar os dois dedões do pé como "consecutivos", não poderá escolher duas sequências que terminem e comecem com 1, respectivamente.
* Você pode querer criar uma variável numberOfToesPerFoot, para que possa alterá-la facilmente mais tarde se algum de seus clientes tiver menos dedos do que o esperado ou mais.
fonte
Existe um número finito de sequências que satisfazem seus requisitos.
EDITAR: Se não se trata realmente de dedos, mas de algum problema aleatório em que o conjunto pode ser muito maior que 5, o espaço da sequência torna-se muito grande e a chance de repetir a mesma sequência no segundo pé torna-se muito pequena. Portanto, é uma boa ideia gerar sequências aleatoriamente e rejeitá-las se corresponderem. Gerar sequências aleatórias de acordo com alguma regra como "pular em dois ou três, e preencher os espaços em branco" provavelmente será mais rápido do que gerar permutações e testes aleatórios, e a chance de sobreposição ainda será pequena se o número de "dedos" for grande .
fonte
Na verdade, gosto mais do seu algoritmo original.
Visto que 14 de 120 permutações funcionam, 106 de 120 não funcionam. Portanto, cada verificação tem 106/120 de chance de falhar.
Isso significa que o número esperado de falhas é:
Não é muito difícil somar essa série infinita:
Multiplique por x:
Subtrair:
Multiplique por x novamente e subtraia novamente:
Uma vez que x = 106/120, S = 64,9.
Portanto, em média, seu loop precisa de apenas 65 iterações para encontrar uma solução.
Qual é a probabilidade de que isso leve, digamos, mil iterações?
Bem, a probabilidade de falhar em qualquer iteração única é 104/120, então a probabilidade de falhar em 1000 iterações é (104/120) ^ 1000, que é algo como 10 ^ (- 63). Ou seja, você nunca verá isso acontecer em sua vida, e provavelmente não durante a vida do universo.
Sem tabelas pré-computadas, fácil adaptação a diferentes números de dedos / pés / mãos / pés, fácil adaptação a diferentes conjuntos de regras ... O que há para não gostar? A decadência exponencial é uma coisa maravilhosa.
[atualizar]
Opa, eu entendi errado a fórmula original ... Já que minhas probabilidades não somam 1. :-)
A expressão correta para o número esperado de falhas é:
(Por exemplo, para obter exatamente duas falhas, você precisa de duas falhas seguidas de um sucesso . Duas falhas têm probabilidade (106/120) ^ 2; um sucesso tem probabilidade (14/120); multiplique-os juntos para obter o peso para o Termo "2".)
Portanto, meu S está errado por um fator de (1-x) (ou seja, 14/120). O número real de falhas esperado é apenas x / (1-x) = 106/14 = 7,57. Portanto, em média, leva apenas 8-9 iterações para encontrar uma solução (7,5 falhas mais um sucesso).
Minha matemática para o caso das "1000 falhas" ainda está correta, eu acho.
fonte
O óbvio: encontre um pedido que funcione e codifique-o. Mas eu não acho que você queira fazer isso.
Você pode gerar permutações muito melhor do que a maneira como está fazendo. Você não precisa fazer amostragem de rejeição. Use um embaralhamento de Fisher Yates em uma permutação inicialmente classificada (1, 2, .. 5) e você terá uma permutação aleatória. http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mas, em geral, o método de geração e teste parece totalmente bom para mim, desde que a probabilidade de gerar uma entrada bem-sucedida seja alta o suficiente. Tenho certeza de que existem muitas sequências válidas de acordo com seus critérios, uma vez que você alternar para uma permutação aleatória, duvido que você terá que fazer muitas iterações de rejeição.
fonte
Nada realmente novo aqui, a mesma solução que o @Kevin já postou, mas acho interessante ver como se traduz para uma linguagem funcional. Neste caso, o Mathematica :
Algumas explicações:
O resultado final é:
Editar
Ou, mais difícil de explicar, mas mais curto:
fonte
Não há realmente nenhuma razão para introduzir aleatoriedade neste problema. Existem apenas 14 sequências que satisfazem esse problema e, certamente, alguma ordem dessas sequências satisfaria melhor o senso estético que você está tentando acomodar. Portanto, você deve apenas reduzir esse problema a um algoritmo para selecionar uma sequência dessas 14, provavelmente em uma ordem predefinida.
Implementação Javascript do algoritmo para encontrar o 14:
EDITAR: O novo requisito de que as sequências devem ser geradas aleatoriamente não pode ser atendido facilmente. O melhor que você provavelmente pode fazer é gerá-los pseudo-aleatoriamente, o que é tão determinístico quanto codificá-los com antecedência e, portanto, não deve satisfazer as superstições de ninguém.
fonte