Como parear meias de uma pilha de maneira eficiente?


3912

Ontem eu estava emparelhando as meias da roupa limpa e descobri que a maneira como estava fazendo isso não é muito eficiente. Eu estava fazendo uma pesquisa ingênua - pegando uma meia e "iterando" a pilha para encontrar seu par. Isto requer a iteração n / 2 * n / 4 = n 2 /8 meias, em média.

Como cientista da computação, estava pensando no que poderia fazer? A classificação (de acordo com tamanho / cor / ...) obviamente veio à mente para obter uma solução O (NlogN).

Hashing ou outras soluções não no local não são uma opção, porque não consigo duplicar minhas meias (embora possa ser bom).

Então, a questão é basicamente:

Dada uma pilha de npares de meias, contendo 2nelementos (suponha que cada meia tenha exatamente um par correspondente), qual é a melhor maneira de combiná-las de maneira eficiente com espaço extra logarítmico? (Acredito que me lembro dessa quantidade de informações, se necessário.)

Aprecio uma resposta que aborda os seguintes aspectos:

  • Uma solução teórica geral para um grande número de meias.
  • O número real de meias não é tão grande, não acredito na minha esposa e tenho mais de 30 pares. (E é bastante fácil distinguir entre minhas meias e as dela; isso também pode ser usado?)
  • É equivalente ao problema de distinção de elementos ?

448
Eu uso o princípio do buraco do pombo para emparelhar exatamente um da pilha de roupas. Eu tenho 3 cores diferentes de meias (vermelho, azul e verde) e 2 pares de cada cor. Pego 4 meias cada vez e sempre faço um par e começo a trabalhar.
Srinivas

59
Outro princípio do buraco do pombo: se você usar um subconjunto de meias n / 2 +1, deve haver pelo menos um par nesse subconjunto.
wildplasser

40
Ótima pergunta! Você pode estar interessado no meu artigo sobre um problema relacionado, que é uma discussão sobre a probabilidade de puxar duas meias iguais da pilha: blogs.msdn.com/b/ericlippert/archive/2010/03/22/…
Eric Lippert

336
Por que não gerar um filho e waitpidpara que, como pai ou mãe, você não esteja nem mesmo escolhendo meias?
Mxyk

137
Resolvi esse problema apenas com meias brancas até o joelho. Todos eles combinam. Eu poderia simplesmente pegar duas meias aleatoriamente da pilha e elas combinariam. Simplifico ainda mais o problema NÃO emparelhando as meias. Eu tenho uma gaveta de meias na qual eu simplesmente jogo todas as minhas meias, sem par. Pego duas aleatoriamente na gaveta todas as manhãs. Simplifiquei para O (0). Não pode ser mais simples que isso. :) #
Lee

Respostas:


2450

Soluções de classificação foram propostas, mas a classificação é um pouco demais : não precisamos de ordem; nós apenas precisamos de grupos de igualdade .

Portanto, o hash seria suficiente (e mais rápido).

  1. Para cada cor de meias, forme uma pilha . Faça uma iteração sobre todas as meias da cesta de entrada e distribua-as nas pilhas de cores .
  2. Itere sobre cada pilha e distribua-a por outra métrica (por exemplo, padrão) no segundo conjunto de pilhas
  3. Aplique recursivamente esse esquema até distribuir todas as meias em pilhas muito pequenas que você possa processar visualmente imediatamente

Esse tipo de particionamento de hash recursivo está realmente sendo feito pelo SQL Server quando ele precisa ingressar ou agregar hash em grandes conjuntos de dados. Ele distribui seu fluxo de entrada de construção em muitas partições independentes. Esse esquema é escalonado para quantidades arbitrárias de dados e várias CPUs linearmente.

Você não precisa de particionamento recursivo se conseguir encontrar uma chave de distribuição (chave de hash) que forneça intervalos suficientes para que cada intervalo seja pequeno o suficiente para ser processado rapidamente. Infelizmente, acho que as meias não têm essa propriedade.

Se cada meia tivesse um número inteiro chamado "PairID", seria possível distribuí-los facilmente em 10 baldes de acordo com PairID % 10(o último dígito).

A melhor partição do mundo real que consigo pensar é criar um retângulo de pilhas : uma dimensão é cor, a outra é o padrão. Por que um retângulo? Porque precisamos de O (1) acesso aleatório a pilhas. (Um cubóide 3D também funcionaria, mas isso não é muito prático.)


Atualizar:

E o paralelismo ? Vários seres humanos podem combinar as meias mais rápido?

  1. A estratégia mais simples de paralelização é fazer com que vários trabalhadores retirem da cesta de entrada e ponham as meias nas estacas. Isso só aumenta muito - imagine 100 pessoas brigando por mais de 10 pilhas. Os custos de sincronização (manifestando-se como colisões manuais e comunicação humana) destroem a eficiência e a velocidade (veja a Lei de Escalabilidade Universal !). Isso é propenso a impasses ? Não, porque cada trabalhador precisa acessar apenas uma pilha por vez. Com apenas um "bloqueio", não pode haver um impasse. Os livelocks podem ser possíveis, dependendo de como os humanos coordenam o acesso às pilhas. Eles podem apenas usar backoff aleatóriocomo as placas de rede, faça isso no nível físico para determinar qual placa pode acessar exclusivamente o fio da rede. Se funciona para NICs , também deve funcionar para humanos.
  2. Escala quase indefinidamente se cada trabalhador tiver seu próprio conjunto de pilhas . Os trabalhadores podem retirar grandes pedaços de meias da cesta de entrada (muito pouca contenção, pois raramente fazem isso) e não precisam sincronizar ao distribuir as meias (porque elas têm pilhas locais de encadeamento). No final, todos os trabalhadores precisam unir suas pilhas. Acredito que isso pode ser feito em O (log (contagem de trabalhadores * pilhas por trabalhador)) se os trabalhadores formarem uma árvore de agregação .

E o problema da distinção de elementos ? Como afirma o artigo, o problema de distinção de elementos pode ser resolvido O(N). É o mesmo para o problema das meias (também O(N), se você precisar de apenas uma etapa de distribuição (propus várias etapas apenas porque os humanos são ruins em cálculos - uma etapa é suficiente se você distribuir md5(color, length, pattern, ...), ou seja, um hash perfeito de todos os atributos)).

Claramente, não se pode ir mais rápido do que O(N), portanto, atingimos o limite inferior ideal .

Embora as saídas não sejam exatamente as mesmas (em um caso, apenas um booleano. No outro caso, os pares de meias), as complexidades assintóticas são as mesmas.


72
É exatamente o que eu faço! Faço estacas dependentes do estilo da abertura da meia (só tenho branco), o que me dá "baldes" suficientes para combinar rapidamente cada uma delas.
usar o seguinte código

30
Eu tentei isso com minhas meias (eu tenho facilmente mais de 30 pares) e cara, é RÁPIDO. Um problema que encontrei é quando não consigo ter um algoritmo de hash bom o suficiente (tenho muitas meias brancas sem nenhum padrão), então fica difícil. Nesse caso, qual seria a melhor maneira de fazer isso?
usar o seguinte código

56
@NothingsImpossible É assim que os ataques de colisão de hash parecem para um servidor da web ruim! As meias brancas são distinguíveis por algum atributo? Deve haver algo em que você possa distribuí-los. Caso contrário, você poderia apenas formar pares arbitrariamente.
usr

37
Este é um Radix Sort, que eu concordo é a resposta certa. @ MarkPeters Eu não acho que você precise de uma tabela de pesquisa. Uma única passagem linear sobre as meias pode convertê-las em vetores numéricos, tornando o mapeamento de "segmento de meias" em um balde trivial. As meias podem ser amarradas aos vetores com barbante, para que você não precise de outra passagem linear no final.
Pointy

49
Um cara com quem eu fui para a faculdade realmente tinha PairIDs. Ele foi costurada em cada par de meias com fios: 1, 2, 3, 4 ...
Ryan Lundy

579

Como a arquitetura do cérebro humano é completamente diferente de uma CPU moderna, essa questão não faz sentido prático.

Os seres humanos podem conquistar os algoritmos da CPU usando o fato de que "encontrar um par correspondente" pode ser uma operação para um conjunto que não é muito grande.

Meu algoritmo:

spread_all_socks_on_flat_surface();
while (socks_left_on_a_surface()) {
     // Thanks to human visual SIMD, this is one, quick operation.
     pair = notice_any_matching_pair();
     remove_socks_pair_from_surface(pair);
}

Pelo menos é isso que estou usando na vida real e acho muito eficiente. A desvantagem é que requer uma superfície plana, mas geralmente é abundante.


229
À medida que o número de meias aumenta, o SIMD humano não se torna melhor que uma CPU.
Lie Ryan

25
A melhor resposta, IMO. Embora seja divertido e inteligente (e apropriado para o SO) reduzir um problema do dia-a-dia a um algoritmo de computador, faz muito mais sentido usar o poder de resolução do olho / cérebro do homem para um conjunto tão pequeno quanto ~ 60 meias.
drug_user841417

13
@LieRyan Se as meias forem distribuídas uniformemente, você acabará percebendo um par em um conjunto suficientemente pequeno de meias devido ao paradoxo do aniversário (a menos que você possa distinguir cores com precisão arbitrária, o que duvido), para que o gargalo aqui não seja o algoritmo de correspondência de cores humano, mas a etapa de espalhamento.
Thomas

13
@ dpc.ucore.info Não, porque eles têm diferentes padrões de manguito de tecido, comprimentos de manguito, comprimentos gerais e tons de preto (minha esposa provavelmente me machucaria fisicamente nesse último).
Christian

200
É melhor espero que você tenha um número par de meias, caso contrário você vai ser dobrar meias por um longo tempo ...
Patrick James McDougle

258

Caso 1 : Todas as meias são idênticas (a propósito, é isso que faço na vida real).

Escolha dois deles para formar um par. Tempo constante.

Caso 2 : Há um número constante de combinações (propriedade, cor, tamanho, textura, etc.).

Use classificação de base . Isso é apenas tempo linear, pois a comparação não é necessária.

Caso 3 : O número de combinações não é conhecido antecipadamente (caso geral).

Temos que fazer uma comparação para verificar se duas meias vêm em pares. Escolha um dos O(n log n)algoritmos de classificação com base em comparação.

No entanto, na vida real, quando o número de meias é relativamente pequeno (constante), esses algoritmos teoricamente ótimos não funcionam bem. Pode levar ainda mais tempo que a pesquisa seqüencial, que teoricamente requer tempo quadrático.


8
> Pode levar ainda mais tempo que a pesquisa seqüencial, o que requer tempo quadrático em teoria. Sim, isso é por isso que eu odeio fazer isso, talvez eu deva jogar fora todas as minhas meias e começar com caso 1.
Nils

57
o lado negativo de ter todas as meias idênticas é que elas tendem a envelhecer em taxas diferentes. Então você ainda tenta combiná-los com base no desgaste deles. (que é mais difícil do que simplesmente correspondência por padrão)
SDC

118
O problema de ter 60 pares de meias idênticas "porque facilita o pareamento" é que isso dá às pessoas a impressão de que você trabalha com computadores.
Steve Ives

13
O caso 1 não é constante quando há uma operação envolvida, como dobrar pares. Nesse caso, é o tempo linear com o menor fator constante (cuja prova é deixada como exercício para o leitor). Um não pode, possivelmente, levar ao mesmo tempo dobrando um par e um balde cheio de meias. No entanto, ele escala linearmente. Pela lei de Amdahl, ele tem aceleração ilimitada, ignorando as despesas gerais. Pela lei de Gustafson, você pode dobrar quantos pares forem necessários para dobrar um par, com trabalhadores suficientes (cuja quantidade é deixada como exercício para o leitor), ignorando a sobrecarga.
acelent

7
@PauloMadeira A triagem é tempo constante - você apenas pega a pilha e a coloca na gaveta. A única operação nesse caso é realmente colocar as meias nos pés, o que também é constante. O desempenho é obtido pela execução adiada do uso da meia, possivelmente com algum sacrifício no espaço (o espaço consumido das meias não dobradas é maior que a dobrada). Eu argumento que isso vale a pena; Eu geralmente perco essa discussão com minha esposa.
Travis

157

Resposta não algorítmica, mas "eficiente" quando o faço:

  • passo 1) descarte todas as meias existentes

  • passo 2) vá ao Walmart e compre-os em pacotes de 10 - n de branco e m de preto. Não há necessidade de outras cores na vida cotidiana.

No entanto, de vez em quando, tenho que fazer isso de novo (meias perdidas, meias danificadas etc.), e odeio descartar meias perfeitamente boas com muita frequência (e eu desejava que elas continuassem vendendo a mesma referência de meias!), Então eu tirei recentemente uma abordagem diferente.

Resposta algorítmica:

Considere que, se você desenhar apenas uma meia para a segunda pilha de meias, como está fazendo, suas chances de encontrar a meia correspondente em uma pesquisa ingênua são bastante baixas.

  • Então pegue cinco deles aleatoriamente e memorize sua forma ou comprimento.

Por que cinco? Geralmente, os humanos são bons em lembrar entre cinco e sete elementos diferentes na memória de trabalho - um pouco como o equivalente humano de uma pilha RPN - cinco é um padrão seguro.

  • Pegue um na pilha de 2n-5.

  • Agora, procure uma correspondência (correspondência de padrão visual - os humanos são bons nisso com uma pilha pequena) dentro das cinco que você desenhou, se você não encontrar uma, adicione-a às suas cinco.

  • Continue escolhendo aleatoriamente as meias da pilha e compare com as suas meias 5 + 1 para uma partida. À medida que sua pilha cresce, isso reduz seu desempenho, mas aumenta suas chances. Muito mais rapido.

Sinta-se à vontade para anotar a fórmula e calcular quantas amostras você precisa para obter 50% de chance de uma partida. IIRC é uma lei hipergeométrica.

Faço isso todas as manhãs e raramente preciso de mais de três empates - mas tenho npares semelhantes (cerca de 10, mais ou menos os perdidos) de mmeias brancas em forma. Agora você pode estimar o tamanho da minha pilha de ações :-)

BTW , eu descobri que a soma dos custos de transação para classificar todas as meias toda vez que eu precisava de um par era muito menor do que fazer isso uma vez e amarrar as meias. Um just-in-time funciona melhor porque então você não precisa amarrar as meias, e também há um retorno marginal decrescente (ou seja, você continua procurando essas duas ou três meias que quando estão na lavanderia e que você precisa para terminar de combinar suas meias e você perde tempo com isso).


25
Voto positivo para resposta 'não algorítmica'. É exatamente isso que faço e funciona maravilhosamente. A questão da substituição não é um problema se você 'girar' o seu material de meias, colocando as meias lavadas nas costas e puxando pela frente da gaveta pela manhã. Todas as meias usam uniformemente. Quando começo a notar um desgaste, coloquei a lista de compras para substituir completamente toda essa classe de meias. Para as meias velhas, dou os melhores 20% a Goodwill (amarrados em um saco de supermercado para que não se misturem de volta) e passo o resto. Você não está desperdiçando meias, neste momento, os 80% têm apenas 6 meses restantes de qualquer maneira.
precisa saber é o seguinte

2
BTW (1) Amarrar as meias faz com que a elástica seja esticada e ela falhará muito mais rapidamente. Limitar os tipos de meias únicas que você faz torna a encadernação não marcada. (2) Uma desvantagem de limitar as meias únicas é que, para pessoas com certas preocupações de moda, o método pode ser inadequado.
precisa saber é o seguinte

3
Eu vim aqui especificamente para postar sua resposta "não algorítmica". Como na verdadeira ciência da computação, a maioria das pessoas nunca presta atenção suficiente aos dados e sua estrutura.
precisa saber é o seguinte

Eu uso essa abordagem algorítmica todas as manhãs e funciona como um encanto! Além disso, coloquei meias desgastadas em uma pilha diferente para jogar fora mais tarde (infelizmente, elas conseguem chegar à pilha original novamente antes que eu encontre tempo para jogá-la no lixo).
Donatas Olsevičius

3
«N pacotes de branco e m pacotes de preto. Não há necessidade de outras cores na vida cotidiana »Uma boa regra padrão para a seleção fácil de meias é que elas devem combinar com a cor da calça ou com a cor do cinto. Por esse motivo, as cores mais usadas provavelmente serão preto, azul, cinza e marrom. É difícil acreditar que é preciso muitas meias brancas.
Andrea Lazzarotto

106

O que faço é pegar a primeira meia e colocá-la no chão (digamos, na beirada da pia). Então pego outra meia e verifico se é a mesma que a primeira. Se for, eu removo os dois. Caso contrário, coloquei ao lado da primeira meia. Depois pego a terceira meia e a comparo com as duas primeiras (se ainda estiverem lá). Etc.

Essa abordagem pode ser facilmente implementada em uma matriz, assumindo que "remover" meias seja uma opção. Na verdade, você nem precisa "remover" as meias. Se você não precisar classificar as meias (veja abaixo), basta movê-las e acabar com uma matriz que tenha todas as meias organizadas em pares na matriz.

Supondo que a única operação para meias seja comparar para igualdade, esse algoritmo ainda é basicamente um algoritmo n 2 , embora eu não conheça o caso médio (nunca aprendi a calcular isso).

A classificação, obviamente, melhora a eficiência, especialmente na vida real, onde você pode "inserir" facilmente uma meia entre duas outras meias. Na computação, o mesmo poderia ser alcançado por uma árvore, mas isso é espaço extra. E, é claro, estamos de volta ao NlogN (ou um pouco mais, se houver várias meias iguais pelos critérios de classificação, mas não do mesmo par).

Fora isso, não consigo pensar em nada, mas esse método parece ser bastante eficiente na vida real. :)


7
É também o que eu faço (observe que, se você simplesmente deixar espaços, as inserções também serão O (1)), mas a escala é baixa com teoricamente grande número de meias.
quer

15
escama mal com um grande número de tipos de meias, teoricamente
Steven Lu

@StevenLu - como eu disse - é n * n ou nLogn, dependendo de você ordenar ou não. Por isso, é tão ruim quanto qualquer algoritmo de classificação. Se você quiser mais rápido, numere-os e use a classificação radix.
Vilx-

Isso é essencialmente armazenar meias encontradas, mas não correspondidas, em uma pesquisa baseada em hash. Com um hash ideal, é O (n), mas se você tiver meias suficientes armazenadas para que o hash comece a degenerar, ele se tornará mais complexo.
Jon Hanna

3
que valor inserir uma meia entre duas outras meias fornece ao objetivo de emparelhar meias? não há cardinalidade de meias. : -x
JoeBrockhaus

60

Isso está fazendo a pergunta errada. A pergunta certa a fazer é: por que estou gastando tempo classificando meias? Quanto custa anualmente, quando você valoriza seu tempo livre para X unidades monetárias de sua escolha?

E, na maioria das vezes, esse não é apenas um tempo livre, é o tempo livre da manhã , que você pode passar na cama, tomar um café ou sair um pouco mais cedo e não ficar preso no trânsito.

Muitas vezes, é bom dar um passo atrás e pensar em uma maneira de contornar o problema.

E existe um caminho!

Encontre uma meia que você gosta. Leve em consideração todos os recursos relevantes: cores em diferentes condições de iluminação, qualidade e durabilidade gerais, conforto em diferentes condições climáticas e absorção de odores. Também importante é que eles não devem perder elasticidade no armazenamento; portanto, os tecidos naturais são bons e devem estar disponíveis em uma embalagem plástica.

É melhor se não houver diferença entre as meias esquerda e direita, mas não for crítico. Se as meias são esquerda-direita simétricas, encontrar um par é uma operação O (1) e classificar as meias é uma operação aproximada O (M), em que M é o número de locais em sua casa que você deixou com meias, idealmente alguns pequeno número constante.

Se você escolheu um par sofisticado com meia esquerda e direita diferente, ao fazer uma ordenação de caçamba cheia para as caçambas pé esquerdo e direito, use O (N + M), onde N é o número de meias e M é o mesmo que acima. Outra pessoa pode dar a fórmula para iterações médias de encontrar o primeiro par, mas o pior caso para encontrar um par com pesquisa cega é N / 2 + 1, o que torna astronomicamente improvável o caso de N. razoável. Isso pode ser acelerado usando imagem avançada algoritmos de reconhecimento e heurísticas, ao digitalizar a pilha de meias não classificadas com o Mk1 Eyeball .

Portanto, um algoritmo para obter a eficiência de emparelhamento de meias O (1) (assumindo uma meia simétrica) é:

  1. Você precisa estimar quantos pares de meias precisará para o resto da sua vida, ou talvez até se aposentar e mudar para climas mais quentes sem precisar usar meias novamente. Se você é jovem, também pode estimar quanto tempo leva para que todos tenhamos robôs de classificação de meias em nossas casas, e todo o problema se torna irrelevante.

  2. Você precisa descobrir como encomendar a meia selecionada a granel, quanto custa e como ela é entregue.

  3. Encomende as meias!

  4. Livre-se de suas meias velhas.

Um passo alternativo 3 envolveria a comparação dos custos de compra da mesma quantidade de meias talvez mais baratas, alguns pares de cada vez ao longo dos anos e a adição do custo da classificação de meias, mas acredite nisso: comprar a granel é mais barato! Além disso, as meias no armazenamento aumentam de valor à taxa de inflação dos preços das ações, que é mais do que você obteria em muitos investimentos. Por outro lado, também há custo de armazenamento, mas as meias realmente não ocupam muito espaço na prateleira superior de um armário.

Problema resolvido. Portanto, compre meias novas, jogue / doe as velhas e viva feliz para sempre, sabendo que você está economizando tempo e dinheiro todos os dias pelo resto da vida.


Um suprimento vitalício (supondo 75 anos) de meias (supondo que você esgote 4 pares / mês, que produz 3600 pares) seria necessário (supondo que um novo par de meias ocupe 20 polegadas cúbicas), totalizando 1 1/2 jardas cúbicas. Isso é uma quantidade enorme de espaço. Supondo que eles entreguem a você em uma caixa que é aproximadamente um cubo, esse caixote terá cerca de 1,80 metro de cada lado.
AJMansfield

2
@AJMansfield preocupação válida. No entanto, eu discordo de alguns de seus números. Eu levaria um tempo de apenas 40 anos (25 ... 65) (tempo entre não morar nos pais / dormitório / etc e me aposentar, veja acima). Além disso, acho que um par mede mais de 0,5x4x6 polegadas na embalagem original. Esses números diminuem bastante o espaço!
Hyde

O passo 4 é desnecessariamente desnecessário, -1.
precisa

2
Guia para outros que podem estar confusos com as medidas de AJMansfield, uma tradução em métrica: »seria necessária (assumindo que um novo par de meias ocupasse 327 cm³) um total de 1,14 m³. Isso é uma quantidade enorme de espaço. Supondo que eles entreguem a você em uma caixa que é aproximadamente um cubo, essa caixa terá cerca de 1,04 m de lado. «
Joey

Como uma pergunta baseada na curiosidade pode ser "a pergunta errada"? O StackOverflow clássico ...
Timmmm

52

O limite teórico é O (n) porque você precisa tocar em cada meia (a menos que algumas já estejam emparelhadas de alguma forma).

Você pode obter O (n) com a classificação radix . Você só precisa escolher alguns atributos para os buckets.

  1. Primeiro você pode escolher (dela, minha) - divida-as em 2 pilhas,
  2. depois use cores (pode ter qualquer ordem para as cores, por exemplo, alfabeticamente pelo nome da cor) - divida-as em pilhas por cor (lembre-se de manter o pedido inicial da etapa 1 para todas as meias na mesma pilha),
  3. então comprimento da meia,
  4. então textura, ....

Se você pode escolher um número limitado de atributos, mas atributos suficientes que podem identificar cada par exclusivamente, você deve fazer isso em O (k * n), que é O (n) se considerarmos que k é limitado.


3
As meias geralmente vêm em pacotes de 4 ou mais, uma vez que são mais baratas, mas também as tornam indistinguíveis. Para combater isso, minha esposa costura uma pequena marca em cada novo par de meias que compro. A marca é de uma cor diferente para cada par ou de uma forma diferente, se ela ficar sem cores. Com essa abordagem, você nem precisa de um conjunto limitado de atributos. Basta costurar um número único em cada par. :) Para pontos extras, use binário.
Vilx-

29
@ Vilx- POR QUE?!? Não é o ponto principal que eles são indistinguíveis?
Flup

2
@ Flup - Eu acho que o ponto principal é vender em pacotes maiores. :) Quanto a mim, isso ajuda a desgastá-los aos pares. Caso contrário, eu posso acabar com três meias muito gastas e uma nova. Meio bobo.
Vilx-

13
Não concordo com o cálculo de O (n). O que é $ k $? $ k $ é o número de atributos. Eu argumentaria que $ k $ é $ O (log n) $ porque deve ser suficiente para identificar exclusivamente cada par. Se você possui 2 pares (preto e branco), a cor ($ k = 1, n = 2 $) é suficiente. Se você tem um par de preto, curto; um par de preto, comprido; um par de branco, curto; e um par de branco, longo - então $ k = 2, n = 4 $. Então, se limitarmos $ k $, ao mesmo tempo limitaremos $ n $. Se vamos limitar $ n $, o cálculo do pedido não faz mais sentido.
Emory

3
@ory, acho que você está procurando o backtick, não o $personagem, para fazer com que suas coisas pareçam código-y.
Xymostech 21/01

33

Como uma solução prática:

  1. Faça rapidamente pilhas de meias facilmente distinguíveis. (Diga por cor)
  2. Classifique cada pilha e use o comprimento da meia para comparação. Como humano, você pode tomar uma decisão bastante rápida que meia usar para particionar que evita o pior caso. (Você pode ver várias meias em paralelo, use isso para sua vantagem!)
  3. Pare de classificar as pilhas quando elas atingirem um limite no qual você se sinta à vontade para encontrar instantaneamente pares de pontos e meias incomparáveis

Se você tiver 1000 meias, com 8 cores e uma distribuição média, poderá fazer 4 pilhas de cada 125 meias em tempo c * n. Com um limite de 5 meias, você pode classificar todas as pilhas em 6 corridas. (Contando 2 segundos para jogar uma meia na pilha certa, você levará pouco menos de 4 horas.)

Se você tiver apenas 60 meias, 3 cores e 2 tipos de meias (sua / da sua esposa), poderá classificar cada pilha de 10 meias em 1 corrida (novamente o limiar = 5). (Contando 2 segundos, você levará 2 minutos).

A classificação inicial do balde agilizará seu processo, porque divide suas n meias em k baldes no c*ntempo, para que você só precise fazer o c*n*log(k)trabalho. (Não levando em consideração o limite). Então, apesar de tudo, você faz n*c*(1 + log(k))trabalho, onde c é a hora de jogar uma meia em uma pilha.

Essa abordagem será favorável em comparação com qualquer c*x*n + O(1)método aproximadamente desde que log(k) < x - 1.


Na ciência da computação, isso pode ser útil: temos uma coleção de n coisas , uma ordem nelas (comprimento) e também uma relação de equivalência (informações extras, por exemplo, a cor das meias). A relação de equivalência nos permite fazer uma partição da coleção original e, em todas as classes de equivalência, nossa ordem ainda é mantida. O mapeamento de uma coisa para sua classe de equivalência pode ser feito em O (1), portanto, apenas O (n) é necessário para atribuir cada item a uma classe. Agora usamos nossas informações extras e podemos proceder de qualquer maneira para classificar todas as classes. A vantagem é que os conjuntos de dados já são significativamente menores.

O método também pode ser aninhado, se tivermos várias relações de equivalência -> criar pilhas de cores, do que dentro de cada partição de pilha na textura, do que classificar no comprimento. Qualquer relação de equivalência que crie uma partição com mais de 2 elementos que tenham tamanho uniforme trará uma melhoria na velocidade da classificação (desde que possamos atribuir diretamente uma meia à pilha), e a classificação poderá ocorrer muito rapidamente em conjuntos de dados menores.


3
Otimização humana: eu diria que, como humano, para a etapa 2, você deve plonk as meias em ordem ascendente, depois repita com granularidade cada vez mais fina até classificar, um pouco como a espécie de concha. Isso seria muito mais rápido para um ser humano (estimativa visual) do que uma abordagem baseada em comparação-troca.
AndrewC

28

Você está tentando resolver o problema errado.

Solução 1: Sempre que colocar meias sujas no cesto de roupa suja, amarre-as em um pequeno nó. Dessa forma, você não terá que fazer nenhuma classificação após a lavagem. Pense nisso como registrar um índice em um banco de dados Mongo. Um pouco de trabalho pela frente para algumas economias de CPU no futuro.

Solução 2: se for inverno, você não precisará usar meias iguais. Nós somos programadores. Ninguém precisa saber, desde que funcione.

Solução 3: Espalhe o trabalho. Você deseja executar um processo de CPU tão complexo de forma assíncrona, sem bloquear a interface do usuário. Pegue essa pilha de meias e coloque-as em um saco. Procure apenas um par quando precisar. Dessa forma, a quantidade de trabalho necessária é muito menos perceptível.

Espero que isto ajude!


5
Amarrar meias (ou qualquer roupa) em um nó reduz a capacidade da lavadora de lavar a roupa e faz com que ela seja desatada ao desgaste muito mais difícil. A solução 2 torna a manutenção mais difícil quanto mais a situação progride; Depois de 6 meses, quando você precisa de duas meias pretas para usar com um par de shorts e tênis, 6 meses fazendo o que for necessário farão com que seja menos provável encontrar esse par na mesma condição (suja / limpa, desgaste semelhante). A solução 3 é menos "assíncrona" e mais "preguiçosa"; faça o trabalho mínimo de que você precisa exatamente quando precisa.
Keith

Re: Solução 2: As pessoas vão saber que eu não estou usando meias combinando, porque eles vão vê-los no meu Birks :)
Bob Probst

@BobProbst Sim, mas seus colegas programadores também usarão meias inigualáveis ​​com a Birks e, portanto, ficarão felizes em perceber que não são os únicos.
Francesco Pasa

27

Esta questão é realmente profundamente filosófica. No fundo, é sobre se o poder das pessoas em resolver problemas (o "wetware" de nossos cérebros) é equivalente ao que pode ser realizado por algoritmos.

Um algoritmo óbvio para classificação de meias é:

Let N be the set of socks that are still unpaired, initially empty
for each sock s taken from the dryer
  if s matches a sock t in N
    remove t from N, bundle s and t together, and throw them in the basket
  else
    add s to N

Agora, a ciência da computação nesse problema trata das etapas

  1. "se s pares com uma meia t em N". Com que rapidez podemos "lembrar" o que vimos até agora?
  2. "remova t de N" e "adicione s a N". Quão caro é acompanhar o que vimos até agora?

Os seres humanos usarão várias estratégias para efetuá-las. A memória humana é associativa , algo como uma tabela de hash em que conjuntos de recursos de valores armazenados são combinados com os próprios valores correspondentes. Por exemplo, o conceito de "carro vermelho" é mapeado para todos os carros vermelhos de que uma pessoa é capaz de se lembrar. Alguém com uma memória perfeita possui um mapeamento perfeito. A maioria das pessoas é imperfeita nesse aspecto (e na maioria das outras). O mapa associativo tem uma capacidade limitada. Os mapeamentos podem bipar fora da existência sob várias circunstâncias (uma cerveja a mais), ser gravada por engano ("eu pensei que o nome dela era Betty, não Nettie"), ou nunca ser substituída, apesar de observarmos que a verdade mudou (o "carro do pai" evoca "Firebird laranja" quando sabíamos que ele havia trocado isso pelo Camaro vermelho).

No caso de meias, recordar perfeitamente significa olhar para uma meia ssempre produz a memória de seu irmão t, incluindo informações suficientes (onde está na tábua de passar) para localizar tem tempo constante. Uma pessoa com memória fotográfica realiza 1 e 2 em tempo constante, sem falhas.

Alguém com memória menos que perfeita pode usar algumas classes de equivalência de senso comum com base em recursos dentro de sua capacidade de rastrear: tamanho (papai, mamãe, bebê), cor (esverdeado, avermelhado etc.), padrão (argyle, comum etc.) , estilo (footie, até o joelho, etc.). Portanto, a tábua de passar seria dividida em seções para as categorias. Isso geralmente permite que a categoria seja localizada em tempo constante pela memória, mas é necessária uma pesquisa linear na categoria "bucket".

Alguém sem memória ou imaginação (desculpe) apenas mantém as meias em uma pilha e faz uma pesquisa linear de toda a pilha.

Uma aberração pura pode usar rótulos numéricos para pares, como alguém sugeriu. Isso abre a porta para um pedido total, o que permite que o ser humano use exatamente os mesmos algoritmos que podemos usar com uma CPU: pesquisa binária, árvores, hashes etc.

Portanto, o "melhor" algoritmo depende das qualidades do wetware / hardware / software que o está executando e da nossa vontade de "trapacear" impondo uma ordem total aos pares. Certamente, o "melhor" meta- algoritmo é contratar o melhor classificador de meias do mundo: uma pessoa ou máquina que pode adquirir e armazenar rapidamente um grande conjunto N de conjuntos de atributos de meia em uma memória associativa 1-1 com pesquisa constante, inserir, e apague. Pessoas e máquinas como essa podem ser adquiridas. Se você tiver um, poderá emparelhar todas as meias no tempo O (N) para N pares, o que é ideal. As tags de pedido total permitem que você use o hash padrão para obter o mesmo resultado com um computador humano ou de hardware.


Ok, isso é melhor, embora ainda esteja muito errado ... esta questão não é sobre isso. Quer a tese de Church-Turing esteja correta ou não, tanto os humanos quanto nossos computadores podem classificar as meias. (A realidade é que, os seres humanos, sendo entidades altamente finitos, têm poder muito menos computacional do que máquinas de Turing ... eo mesmo é verdade para os nossos computadores, mas as limitações são diferentes.)
Jim Balter

Discordo. É claro que qualquer um dos nossos computadores atuais é essencialmente um DFA enorme (diferenças de módulo de E / S) em vez de uma TM. Qualquer dispositivo analógico, no entanto, como nossos corpos, é capaz de emular uma fita infinita. Ainda não temos uma caracterização útil da maneira como nossa mente calcula.
Gene

Nenhuma fita infinita para humanos ou outros dispositivos físicos, porque nada no cérebro humano tem resolução infinita, nem poderia. Também ajudaria a aprender alguma neurociência. De qualquer forma, não havia nenhuma questão filosófica profunda aqui, independentemente do seu desejo de injetar uma. Mas acredite no que você quiser ... este não é o lugar para esse tipo de debate e já o tive muitas vezes antes. Mas sempre me divirto com pessoas que mal conseguem resolver os problemas mais simples (somos todos nós) imaginando que eles são equivalentes à TM.
perfil completo de Jim Balter

22

Custo: Meias em movimento -> altas, encontrar / pesquisar meias em linha -> pequenas

O que queremos fazer é reduzir o número de movimentos e compensar com o número de pesquisas. Além disso, podemos utilizar o ambiente multithreded do Homo Sapiens para armazenar mais coisas no cache de decisão.

X = Seu, Y = Seus cônjuges

Da pilha A de todas as meias:

Escolha duas meias, coloque a meia X correspondente na linha X e a meia Y na linha Y na próxima posição disponível.

Faça até que A esteja vazio.

Para cada linha X e Y

  1. Escolha a primeira meia da fila, procure ao longo da linha até encontrar a meia correspondente.

  2. Coloque na linha final correspondente de meias.

  3. Opcional Enquanto estiver pesquisando a linha e a meia atual que você estiver olhando for idêntica à anterior, siga a etapa 2 para essas meias.

Opcionalmente, na etapa um, você escolhe duas meias dessa linha em vez de duas, pois a memória do cache é grande o suficiente para que possamos identificar rapidamente se uma das meias corresponde à atual na linha que você está observando. Se você tiver a sorte de ter três braços, poderá analisar três meias ao mesmo tempo, uma vez que a memória do objeto é grande o suficiente.

Faça até X e Y estarem vazios.

Feito

No entanto, como isso tem complexidade semelhante à classificação, o tempo gasto é muito menor devido às velocidades de E / S (meias móveis) e pesquisa (pesquisando na linha por uma meia).


22

Aqui está um limite inferior Omega (n log n) no modelo baseado em comparação. (A única operação válida é comparar duas meias.)

Suponha que você saiba que suas meias 2n estão organizadas desta maneira:

p 1 p 2 p 3 ... p n p f (1) p f (2) ... p f (n)

onde f é uma permutação desconhecida do conjunto {1,2, ..., n}. Saber disso não pode dificultar o problema. Existem n! saídas possíveis (correspondências entre a primeira e a segunda metade), o que significa que você precisa de comparações log (n!) = Omega (n log n). Isso pode ser obtido por classificação.

Como você está interessado em conexões com o problema de distinção de elementos: provar que o Omega (n log n) vinculado à distinção de elementos é mais difícil, porque a saída é binária yes / no. Aqui, a saída deve ser uma correspondência e o número de saídas possíveis é suficiente para obter um limite decente. No entanto, há uma variante conectada à distinção de elemento. Suponha que você receba 2n meias e pergunte-se se elas podem ser unicamente combinadas. Você pode obter uma redução de ED enviando (a 1 , a 2 , ..., a n ) para (a 1 , a 1 , a 2 , a 2 , ..., a n , a n ). (Entre parênteses, a prova de dureza da ED é muito interessante, via topologia.)

Eu acho que deveria haver um Omega (n 2 ) vinculado ao problema original se você permitir apenas testes de igualdade. Minha intuição é: considere um gráfico em que você adiciona uma aresta após um teste e defenda que, se o gráfico não for denso, a saída não será determinada exclusivamente.


19

É assim que eu realmente faço isso, para p pares de meias ( n = 2p meias individuais):

  • Pegue uma meia aleatoriamente da pilha.
  • Para a primeira meia, ou se todas as meias escolhidas anteriormente tiverem sido emparelhadas, basta colocá-la no primeiro "slot" de um "conjunto" de meias não emparelhadas à sua frente.
  • Se você tiver uma ou mais meias não emparelhadas selecionadas, verifique sua meia atual contra todas as meias não emparelhadas da matriz.
    • É possível separar as meias em classes ou tipos gerais (branco / preto, tornozelo / gola, atletismo / vestuário) ao criar sua matriz e "drill down" para comparar apenas igual para igual.
    • Se você encontrar uma correspondência aceitável, junte as duas meias e remova-as da matriz.
    • Caso contrário, coloque a meia atual no primeiro slot aberto da matriz.
  • Repita com cada meia.

O pior cenário desse esquema é que cada par de meias é diferente o suficiente para ser correspondido exatamente e que as primeiras meias n / 2 que você escolhe são todas diferentes. Esse é o seu cenário de O (n 2 ) e é extremamente improvável. Se o número de tipos únicos de meias t for menor que o número de pares p = n / 2 , e as meias de cada tipo forem iguais o suficiente (geralmente em termos relacionados ao desgaste), qualquer meia desse tipo poderá ser emparelhada com qualquer outro, então como eu inferido acima, o número máximo de meias que você nunca vai ter de comparar é t , após o qual o próximo você puxa vontadecombinar uma das meias não emparelhadas. Esse cenário é muito mais provável na gaveta média do que no pior caso e reduz a complexidade do pior caso a O (n * t), onde geralmente t << n .


1
Provavelmente isso está bem próximo do meu processo mental. Eu tenho uma camada adicional de otimização de pré-classificação. Minhas meias esportivas são lavadas com os brancos e minhas meias são lavadas com cores. Isso significa que, desde que eu não jogue duas roupas na lavanderia, minhas meias já estarão agrupadas por tipo. A carga branca passa muito rápido (muitas meias idênticas), mas as meias de vestido demoram mais tempo. Outra dica chave - mais memória disponível para o tipo (dobrar e retirar todos os não-meias primeiro e depois executar o algoritmo de emparelhamento)
orh

17

Abordagem do mundo real:

O mais rápido possível, remova as meias da pilha não classificada, uma de cada vez, e coloque-as em pilhas à sua frente. As estacas devem ser dispostas de maneira um pouco eficiente no espaço, com todas as meias apontando na mesma direção; o número de pilhas é limitado pela distância que você pode alcançar facilmente. A seleção de uma pilha na qual colocar uma meia deve ser - o mais rápido possível - colocando uma meia em uma pilha de meias aparentemente semelhantes; o erro ocasional tipo I (colocando uma meia em uma pilha à qual não pertence) ou tipo II (colocando uma meia em sua própria pilha quando houver uma pilha existente de meias semelhantes) pode ser tolerado - a consideração mais importante é a velocidade .

Quando todas as meias estiverem empilhadas, passe rapidamente pelas pilhas com várias meias, criando pares e removendo-os (eles estão indo para a gaveta). Se houver meias sem correspondência na pilha, empilhe-as novamente da melhor forma possível (dentro da restrição o mais rápido possível). Quando todas as pilhas de meias múltiplas tiverem sido processadas, combine as meias pareadas restantes que não foram emparelhadas devido a erros do tipo II. Whoosh, você terminou - e eu tenho muitas meias e não as lavo até que uma grande fração esteja suja. Outra observação prática: abro a parte de cima de uma meia sobre a outra, aproveitando suas propriedades elásticas, para que elas fiquem juntas enquanto são transportadas para a gaveta e enquanto estão na gaveta.


15

De sua pergunta, é claro que você não tem muita experiência real com lavanderia :). Você precisa de um algoritmo que funcione bem com um pequeno número de meias não emparelhadas.

As respostas até agora não fazem bom uso de nossos recursos de reconhecimento de padrões humanos. O jogo de Set fornece uma pista de como fazer isso bem: coloque todas as meias em um espaço bidimensional para que você possa reconhecê-las bem e alcançá-las facilmente com as mãos. Isso limita você a uma área de aproximadamente 120 * 80 cm. A partir daí, selecione os pares que você reconhece e remova-os. Coloque meias extras no espaço livre e repita. Se você lava para pessoas com meias facilmente reconhecíveis (crianças pequenas vêm à mente), pode fazer uma classificação radical selecionando essas meias primeiro. Esse algoritmo funciona bem apenas quando o número de meias únicas é baixo


Geralmente é assim que eu faço. Funciona muito melhor do que repetir todas as meias restantes de cada vez.
precisa saber é

Boa abordagem e acho que também pode ser aplicada a alguns problemas reais de CS. Você pode adicionar um exemplo disso (um problema de CS no qual poderíamos usar uma abordagem semelhante para resolver problemas)? Além disso, como essa solução é dimensionada para milhões de meias?
Amit

Eu acho que isso é basicamente o mesmo que a outra resposta aqui, stackoverflow.com/a/14423956 , de 20 de janeiro. Ambos +1. O sistema de visão humana é massivamente paralelo.
Will Ness

15

Pegue uma primeira meia e coloque-a sobre uma mesa. Agora escolha outra meia; se corresponder ao primeiro escolhido, coloque-o em cima do primeiro. Caso contrário, coloque-o sobre a mesa a uma pequena distância do primeiro. Escolha uma terceira meia; se ele corresponder a um dos dois anteriores, coloque-o em cima deles ou coloque-o a uma pequena distância do terceiro. Repita o procedimento até pegar todas as meias.


1
Esta é a única resposta válida. Todos os outros desconsideram o fato de passar mais tempo distinguindo meias semelhantes (então, agrupá-las pela aparência física torna ainda pior).
entonio 5/05

Para se divertir Eu escrevi este método de empilhar meias-se em um pequeno programa python gist.github.com/justinfay/53b574cf0a492f6795ef
Justin Fay

12

Para dizer o quão eficiente é parear meias de uma pilha, precisamos primeiro definir a máquina, porque o emparelhamento não é feito nem por uma máquina de turing nem por uma máquina de acesso aleatório, que normalmente são usadas como base para uma análise algorítmica.

A máquina

A máquina é uma abstração de um elemento do mundo real chamado ser humano. É capaz de ler do ambiente através de um par de olhos. E nosso modelo de máquina é capaz de manipular o ambiente usando 2 braços. As operações lógicas e aritméticas são calculadas usando nosso cérebro (espero ;-)).

Também devemos considerar o tempo de execução intrínseco das operações atômicas que podem ser realizadas com esses instrumentos. Devido a restrições físicas, as operações realizadas por um braço ou olho têm complexidade de tempo não constante. Isso ocorre porque não podemos mover uma pilha infinitamente grande de meias com um braço, nem um olho pode ver a meia superior em uma pilha infinitamente grande de meias.

No entanto, a física mecânica também nos oferece alguns benefícios. Não estamos limitados a mover no máximo uma meia com um braço. Podemos mover um par inteiro deles de uma só vez.

Portanto, dependendo da análise anterior, as seguintes operações devem ser usadas em ordem decrescente:

  • operações lógicas e aritméticas
  • leituras ambientais
  • modificações ambientais

Também podemos usar o fato de que as pessoas só têm uma quantidade muito limitada de meias. Portanto, uma modificação ambiental pode envolver todas as meias da pilha.

O algoritmo

Então aqui está a minha sugestão:

  1. Espalhe todas as meias na pilha pelo chão.
  2. Encontre um par olhando as meias no chão.
  3. Repita de 2 até que nenhum par possa ser formado.
  4. Repita de 1 até que não haja meias no chão.

A operação 4 é necessária, porque ao espalhar meias pelo chão, algumas podem esconder outras. Aqui está a análise do algoritmo:

A análise

O algoritmo termina com alta probabilidade. Isso se deve ao fato de que não é possível encontrar pares de meias na etapa número 2.

Para a análise em tempo de execução a seguir de emparelhamento de npares de meias, supomos que pelo menos metade das 2nmeias não esteja oculta após a etapa 1. Portanto, no caso médio, podemos encontrar n/2pares. Isso significa que o loop é executado na etapa 4 O(log n)vezes. O passo 2 é executado O(n^2)vezes. Para que possamos concluir:

  • O algoritmo envolve O(ln n + n)modificações ambientais (etapa 1 O(ln n)mais escolher cada par de meias do chão)
  • O algoritmo envolve O(n^2)leituras ambientais da etapa 2
  • O algoritmo envolve O(n^2)operações lógicas e aritméticas para comparar uma meia com outra na etapa 2

Portanto, temos uma complexidade total do tempo de execução de O(r*n^2 + w*(ln n + n))onde re wsão os fatores para as operações de leitura e gravação ambiental, respectivamente, para uma quantidade razoável de meias. O custo das operações lógicas e aritméticas é omitido, porque supomos que é necessária uma quantidade constante de operações lógicas e aritméticas para decidir se 2 meias pertencem ao mesmo par. Isso pode não ser viável em todos os cenários.



@WillNess Sim, com um pouco mais de explicação
SpaceTrucker

12
List<Sock> UnSearchedSocks = getAllSocks();
List<Sock> UnMatchedSocks = new list<Sock>();
List<PairOfSocks> PairedSocks = new list<PairOfSocks>();

foreach (Sock newSock in UnsearchedSocks)
{
  Sock MatchedSock = null;
  foreach(Sock UnmatchedSock in UnmatchedSocks)
  {
    if (UnmatchedSock.isPairOf(newSock))
    {
      MatchedSock = UnmatchedSock;
      break;
    }
  }
  if (MatchedSock != null)
  {
    UnmatchedSocks.remove(MatchedSock);
    PairedSocks.Add(new PairOfSocks(MatchedSock, NewSock));
  }
  else
  {
    UnmatchedSocks.Add(NewSock);
  }
}

12

Saí com outra solução que não prometeria menos operações, nem menos consumo de tempo, mas deve-se tentar ver se pode ser uma heurística suficientemente boa para fornecer menos consumo de tempo em grandes séries de emparelhamento de meias.

Condições prévias: Não há garantia de que existem as mesmas meias. Se eles são da mesma cor, isso não significa que eles tenham o mesmo tamanho ou padrão. As meias são aleatoriamente embaralhadas. Pode haver um número ímpar de meias (algumas estão faltando, não sabemos quantas). Prepare-se para lembrar uma variável "index" e defina-a como 0.

O resultado terá uma ou duas pilhas: 1. "correspondido" e 2. "ausente"

Heurística:

  1. Encontre a meia mais distinta.
  2. Encontre a correspondência.
  3. Se não houver correspondência, coloque-a na pilha "ausente".
  4. Repita de 1. até que não haja mais meias mais distintas.
  5. Se houver menos de 6 meias, passe para 11.
  6. Emparelhe cegamente todas as meias ao seu vizinho (não o embale)
  7. Encontre todos os pares correspondentes, faça as malas e mova os pares compactados para a pilha "correspondente"; Se não houver novas correspondências - aumente o "índice" em 1
  8. Se o "índice" for maior que 2 (isso pode depender do número da meia, porque com maior número de meias, há menos chances de emparelhá-las cegamente) vá para 11
  9. Embaralhe o resto
  10. Vá para 1
  11. Esqueça o "índice"
  12. Escolha uma meia
  13. Encontre seu par
  14. Se não houver par para a meia, mova-o para a pilha "ausente"
  15. Se a correspondência encontrada emparelhá-lo, empacote o par e mova-o para a pilha "correspondente"
  16. Se ainda houver mais de uma meia, passe para 12
  17. Se sobrar apenas um, vá para 14
  18. Sorriso satisfeito :)

Além disso, pode-se adicionar verificação de meias danificadas também, como se a remoção delas. Pode ser inserido entre 2 e 3 e entre 13 e 14.

Estou ansioso para ouvir sobre quaisquer experiências ou correções.


Depois de escrever isso, eu o uso sempre. Isso me ajudou a me tornar um pouco mais eficiente e o trabalho é menos chato agora.
Sasa

11

Quando ordeno as meias, faço uma classificação aproximada , deixando as meias perto de outras da mesma cor / tipo de padrão. Exceto no caso em que vejo uma correspondência exata no / próximo ao local em que estou prestes a largar a meia, extraio o par nesse momento.

Quase todos os outros algoritmos (incluindo a resposta de pontuação mais alta por usr ) classificam e removem os pares. Acho que, como humano, é melhor minimizar o número de meias sendo consideradas ao mesmo tempo.

Eu faço isso por:

  1. Escolher uma meia distinta (o que me chama a atenção primeiro na pilha).
  2. Iniciar uma classificação radix a partir dessa localização conceitual, puxando meias da pilha com base na semelhança com aquela.
  3. Coloque a meia nova na pilha atual, com uma distância baseada na diferença entre ela. Se você se encontrar colocando a meia em cima de outra por ser idêntica, forme o par e remova-a. Isso significa que comparações futuras exigem menos esforço para encontrar o local correto.

Isso tira proveito da capacidade humana de fazer a correspondência difusa no tempo O (1), o que é um pouco equivalente ao estabelecimento de um mapa de hash em um dispositivo de computação.

Ao puxar as meias distintivas primeiro, você deixa espaço para "ampliar" os recursos que são menos distintos, para começar.

Depois de eliminar o fluro colorido, as meias com listras e os três pares de meias longas, você pode acabar usando principalmente meias brancas, classificadas de acordo com o desgaste.

Em algum momento, as diferenças entre as meias são pequenas o suficiente para que outras pessoas não notem a diferença, e não é necessário nenhum esforço adicional.


10

Sempre que você pegar uma meia, coloque-a em um só lugar. Em seguida, a próxima meia que você pegar, se não corresponder à primeira, coloque-a ao lado da primeira. Se isso acontecer, há um par. Dessa forma, não importa quantas combinações existem, e existem apenas duas possibilidades para cada meia que você escolhe - ou ela tem uma correspondência que já está na sua coleção de meias ou não, o que significa que você adicione-o a um local na matriz.

Isso também significa que você quase certamente nunca terá todas as suas meias na matriz, porque elas serão removidas à medida que forem correspondidas.


Isto é o que eu faço ... O (n)
Pykler

2
@ Pykler - É O (n) na melhor das hipóteses e O (n * n) na pior das hipóteses.
Vilx-

2
Isso pressupõe que você não pode criar um hash totalmente exclusivo em sua mente de todas as meias que você já viu, o que para mim é um O (1) para combinar com uma meia que eu já vi e coloquei na espera por um hash
Pykler

10

Considere uma tabela de tamanho 'N'.

Se assumirmos a distribuição normal, o número estimado de 'inserções' com pelo menos uma meia mapeada para um bucket é NlogN (ou seja, todos os buckets estão cheios)

Eu derivara isso como parte de outro quebra-cabeça, mas ficaria feliz em provar que estou errado. Aqui está o meu artigo sobre o mesmo

Deixe 'N' corresponder a um limite superior aproximado do número de cores / padrão únicos de meias que você possui.

Depois de ter uma colisão (aka: uma partida), basta remover esse par de meias. Repita o mesmo experimento com o próximo lote de meias NlogN. A vantagem disso é que você pode fazer comparações paralelas ao NlogN (resolução de colisão) por causa da maneira como a mente humana funciona. :-)


10

As meias, sejam reais ou alguma estrutura de dados análoga, seriam fornecidas em pares.

A resposta mais simples é antes de permitir que o par seja separado, uma única estrutura de dados para o par deveria ter sido inicializada, contendo um ponteiro para a meia esquerda e direita, permitindo assim que as meias fossem referidas diretamente ou através de seu par. Uma meia também pode ser estendida para conter um ponteiro para seu parceiro.

Isso resolve qualquer problema de emparelhamento computacional removendo-o com uma camada de abstração.

Aplicando a mesma idéia ao problema prático de emparelhar meias, a resposta aparente é: não permita que suas meias sejam pareadas. As meias são fornecidas como um par, colocadas na gaveta como um par (talvez juntando-as), usadas como um par. Mas o ponto em que é possível desemparelhar é na lavadora, então tudo o que é necessário é um mecanismo físico que permita que as meias fiquem juntas e sejam lavadas com eficiência.

Existem duas possibilidades físicas:

Para um objeto de 'par' que mantém um ponteiro para cada meia, poderíamos ter um saco de pano que usamos para manter as meias juntas. Isso parece uma sobrecarga enorme.

Mas para cada meia manter uma referência à outra, existe uma solução interessante: um popper (ou um 'botão de pressão' se você for americano), como estes:

http://www.aliexpress.com/compare/compare-invisible-snap-buttons.html

Tudo o que você faz é ajuntar as meias logo depois de tirá-las e colocá-las no cesto de lavar roupa e, novamente, você removeu o problema de precisar emparelhá-las com uma abstração física do conceito de 'par'.


Ele não responde à pergunta, porque o manuseio com dados já emparelhados é fácil, a questão é o que fazer quando os dados não são PAIROS e você deseja emparelhá-los.
Amit

8

Se a operação "mover" for bastante cara e a operação "comparar" for barata, e você precisar mover todo o conjunto de qualquer maneira, para um buffer em que a pesquisa seja muito mais rápida do que no armazenamento original ... basta integrar a classificação no campo obrigatório. mover.

Eu achei que integrar o processo de classificação em pendurar para secar torna uma brisa. Eu preciso pegar cada meia de qualquer maneira e pendurá-la (mover) e não me custa nada pendurá-la em um local específico nas cordas. Agora, apenas para não forçar a pesquisa de todo o buffer (as cordas), escolho colocar as meias por cor / sombra. Mais escura à esquerda, mais brilhante à direita, frente mais colorida etc. Agora, antes de pendurar cada meia, olho na "vizinhança certa" se já existe uma correspondente - isso limita a "varredura" para 2-3 outras meias - e se for , Penduro o outro ao lado dele. Depois enrolo-os em pares, removendo das cordas, quando secos.

Agora, isso pode não parecer tão diferente de "formar pilhas por cor", sugerido pelas respostas principais, mas primeiro, não escolhendo pilhas discretas, mas intervalos, não tenho problema em classificar se "roxo" vai para a pilha "vermelha" ou "azul"; apenas vai entre. E então, integrando duas operações (travar para secar e classificar), a sobrecarga da classificação durante a suspensão é 10% do que seria uma classificação separada.


Essa abordagem tem duas outras vantagens: a secagem da linha perde muito menos IME do que a secadora, e o processo de classificação pode ser estendido para o restante da roupa, de modo que (por exemplo) todas as toalhas estão próximas umas das outras para serem dobradas da roupa. enfileirados e transportados direto para o seu armazenamento. Também funciona em dois passes de baixo esforço, colocando as roupas e retirando-as novamente.
precisa saber é o seguinte

8

Acabei de emparelhar minhas meias agora e descobri que a melhor maneira de fazer isso é a seguinte:

  • Escolha uma das meias e guarde-a (crie um 'balde' para esse par)
  • Se o próximo for o par do anterior, coloque-o no bucket existente, caso contrário, crie um novo.

Na pior das hipóteses, isso significa que você terá n / 2 baldes diferentes e terá n-2 determinações sobre o balde que contém o par da meia atual. Obviamente, esse algoritmo funciona bem se você tiver apenas alguns pares; Eu fiz isso com 12 pares.

Não é tão científico, mas funciona bem :)


Este ainda é um algoritmo O (n ^ 2), já que você precisa iterar cada balde sempre que retirar uma meia nova. Mas, considerando o fato de que mesmo as meias compradas no mesmo lote têm pequenas diferenças que as tornam efetivamente únicas (ou até únicas), de qualquer maneira, não há maneira melhor
Semisonic

Concordo, mas meu algoritmo está assumindo que humanos estão fazendo o emparelhamento. Portanto, haverá um tipo de cache em sua mente quando você estiver pesquisando o depósito correspondente, para que você não precise realmente iterar sobre os depósitos. Não tenho certeza de que tipo de estrutura de dados é criada para esse mecanismo de cache na minha cabeça durante o emparelhamento.
maestro

8

Minha solução não corresponde exatamente aos seus requisitos, pois exige formalmente O(n)espaço "extra". No entanto, considerando minhas condições, é muito eficiente em minha aplicação prática. Portanto, acho que deveria ser interessante.

Combinar com outra tarefa

A condição especial no meu caso é que eu não uso a máquina de secar, apenas penduro minhas roupas em um secador de roupas comum. Pendurar panos requer O(n)operações (a propósito, eu sempre considero um problema de empacotamento de lixeira aqui) e o problema, por sua natureza, requer o espaço "extra" linear. Quando tiro uma meia nova do balde, tente pendurá-la ao lado do par, se o par já estiver pendurado. Se é uma meia de um novo par, deixo algum espaço ao lado.

Oracle Machine is Better ;-)

Obviamente, requer algum trabalho extra para verificar se a meia correspondente já está pendurada em algum lugar e renderizaria a solução O(n^2)com coeficiente 1/2para um computador. Mas, neste caso, o "fator humano" é realmente uma vantagem - eu geralmente consigo O(1)identificar (quase ) muito rapidamente a meia correspondente se ela já estava pendurada (provavelmente está envolvido algum cache imperceptível no cérebro) - considere-a uma espécie de limitado "oracle" como no Oracle Machine ;-) Nós, os humanos, temos essas vantagens sobre as máquinas digitais em alguns casos ;-)

Já quase O(n)!

Assim, conectando o problema de emparelhar meias com o problema de pendurar panos, recebo O(n)"espaço extra" de graça e tenho uma solução que está quase O(n)no tempo, requer apenas um pouco mais de trabalho do que simples panos pendurados e permite acessar imediatamente par completo de meias mesmo em uma péssima segunda de manhã ... ;-)


8

Espero poder contribuir com algo novo para esse problema. Percebi que todas as respostas negligenciam o fato de que existem dois pontos em que você pode executar o pré-processamento , sem diminuir o desempenho geral da lavanderia.

Além disso, não precisamos assumir um grande número de meias, mesmo para famílias grandes. As meias são retiradas da gaveta e desgastadas e depois jogadas em um local (talvez uma lixeira) onde ficam antes de serem lavadas. Embora eu não chamasse o bin bin de LIFO-Stack, eu diria que é seguro assumir que

  1. as pessoas jogam as duas meias aproximadamente na mesma área da lixeira,
  2. o compartimento não é randomizado em nenhum momento e, portanto,
  3. qualquer subconjunto retirado da parte superior desse compartimento geralmente contém as duas meias de um par.

Como todas as máquinas de lavar roupa que eu conheço são de tamanho limitado (independentemente de quantas meias você precisa lavar), e a randomização real ocorre na máquina de lavar, não importa quantas meias nós temos, sempre temos pequenos subconjuntos que quase não contêm singletons.

Nossas duas etapas de pré-processamento são "colocar as meias no varal" e "Tirar as meias do varal", o que temos que fazer para obter meias que não são apenas limpas, mas também secas. Assim como as máquinas de lavar, os varais são finitos e presumo que tenhamos toda a parte da linha em que colocamos nossas meias à vista.

Aqui está o algoritmo para put_socks_on_line ():

while (socks left in basket) {
 take_sock();
 if (cluster of similar socks is present) { 
   Add sock to cluster (if possible, next to the matching pair)
 } else {
  Hang it somewhere on the line, this is now a new cluster of similar-looking socks.      
  Leave enough space around this sock to add other socks later on 
 }
}

Não perca seu tempo movendo as meias ou procurando a melhor combinação, tudo isso deve ser feito em O (n), que também seria necessário para colocá-las na linha sem classificação. As meias ainda não estão emparelhadas, só temos vários grupos de similaridade em jogo. É útil termos um conjunto limitado de meias aqui, pois isso nos ajuda a criar grupos "bons" (por exemplo, se houver apenas meias pretas no conjunto de meias, agrupar por cores não seria o caminho a seguir).

Aqui está o algoritmo para take_socks_from_line ():

while(socks left on line) {
 take_next_sock();
 if (matching pair visible on line or in basket) {
   Take it as well, pair 'em and put 'em away
 } else {
   put the sock in the basket
 }

Devo ressaltar que, para melhorar a velocidade das etapas restantes, é aconselhável não escolher aleatoriamente a próxima meia, mas sequencialmente tirar meia após meia de cada cluster. Ambas as etapas de pré-processamento não levam mais tempo do que colocar as meias na linha ou na cesta, o que temos que fazer, não importa o que aconteça, portanto isso deve melhorar muito o desempenho da lavanderia.

Depois disso, é fácil executar o algoritmo de particionamento de hash. Normalmente, cerca de 75% das meias já estão emparelhadas, deixando-me com um subconjunto muito pequeno de meias, e esse subconjunto já está (um pouco) agrupado (não introduzo muita entropia na minha cesta após as etapas de pré-processamento). Outra coisa é que os clusters restantes tendem a ser pequenos o suficiente para serem manuseados de uma só vez, portanto, é possível retirar um cluster inteiro da cesta.

Aqui está o algoritmo para sort_remaining_clusters ():

while(clusters present in basket) {
  Take out the cluster and spread it
  Process it immediately
  Leave remaining socks where they are
}

Depois disso, restam apenas algumas meias. É aqui que introduzo meias anteriormente não emparelhadas no sistema e processo as meias restantes sem nenhum algoritmo especial - as meias restantes são muito poucas e podem ser processadas visualmente muito rapidamente.

Para todas as meias restantes, suponho que os colegas ainda não estejam lavados e os guardo para a próxima iteração. Se você registrar um crescimento de meias não emparelhadas ao longo do tempo (um "vazamento de meia"), verifique sua lixeira - ela pode ser aleatória (você tem gatos que dormem lá?)

Eu sei que esses algoritmos têm muitas suposições: uma lixeira que funciona como algum tipo de pilha LIFO, uma máquina de lavar normal limitada e um varal normal, limitado - mas isso ainda funciona com um número muito grande de meias.

Sobre o paralelismo: contanto que você jogue as duas meias na mesma lixeira, é possível paralelizar facilmente todas essas etapas.


As meias são apenas uma metáfora para emparelhar objetos arbitrários em algum banco de dados.
Amit

1
Entendi, não vi que você é o autor. Se você queria uma solução genérica, realmente deveria ter dito isso. De qualquer forma, não há nada de errado em levar em consideração qualquer informação que você tenha, a menos que você tenha que encontrar uma solução geral - desistir da reutilização da solução pode resultar em um desempenho consideravelmente melhor. Nesse caso, considerar o caso de uso e o banco de dados disponível como um todo é benéfico. No entanto, esta resposta especial à sua pergunta especial tem problemas com meias de aparência semelhante, por exemplo, meias pretas em tamanhos diferentes, portanto, não é aplicável em alguns casos.
Philipp Flenker

1
Além disso, você não recebeu> 2k upvotes porque fez uma pergunta sobre o emparelhamento de objetos arbitrários no banco de dados. Você restringiu a pergunta especificamente devido à própria natureza das meias (que não pode ser duplicada, em oposição aos dados), e até incentivou a usar o fato de que você pode distinguir facilmente suas meias das meias do seu cônjuge. Se você perguntar a uma pergunta sobre meias, não espere que as respostas para ser sobre bancos de dados ;-)
Philipp Flenker

1
Existem algumas suposições: uma máquina de lavar roupa normal, um varal normal e o fato de você jogar as duas meias na lixeira ao mesmo tempo, o que significa que, na maioria dos casos, as duas meias estão na mesma máquina e o número de as sobras a serem classificadas são, portanto, pequenas. Mas como você realmente queria uma resposta sobre o armazenamento de objetos arbitrários no banco de dados, é realmente útil discutir minha solução ainda mais?
Philipp Flenker

1
Como disse, acho que resolvi tudo o que você pediu, exceto o problema de distinção entre elementos, que foi respondido por outras pessoas. Não estou tentando ser um idiota aqui, mas dediquei muito esforço a essa resposta há algum tempo e estou um pouco decepcionado por você agora ter algumas respostas e afirmar que elas não responderam à pergunta original . Por que você não deixa o tópico inteiro em branco - ainda é uma leitura interessante, mais de 2 anos depois que você o solicitou?
Philipp Flenker

8

Tomei medidas simples para reduzir meu esforço em um processo que leva tempo O (1).

Ao reduzir minhas entradas para um dos dois tipos de meias (meias brancas para recreação, meias pretas para o trabalho), só preciso determinar qual das duas meias tenho em mãos. (Tecnicamente, como nunca são lavados juntos, reduzi o processo para o tempo O (0).)

É necessário algum esforço inicial para encontrar as meias desejáveis ​​e comprar em quantidade suficiente para eliminar a necessidade das meias existentes. Como eu havia feito isso antes da minha necessidade de meias pretas, meu esforço era mínimo, mas a milhagem pode variar.

Esse esforço inicial foi visto muitas vezes em códigos muito populares e eficazes. Os exemplos incluem # DEFININDO pi com várias casas decimais (existem outros exemplos, mas esse é o que vem à mente agora).


7

Crie uma tabela de hash que será usada para meias incomparáveis, usando o padrão como hash. Repita as meias uma a uma. Se a meia tiver uma correspondência de padrões na tabela de hash, tire-a da mesa e faça um par. Se a meia não corresponder, coloque-a na mesa.


Como fazê-lo não no local, como mencionado especificamente na pergunta?
Amit

7

O problema de classificar seus n pares de meias é O (n) . Antes de jogá-los no cesto de roupa suja , enfie o da esquerda para o da direita. Ao retirá-las, você corta a linha e coloca cada par na gaveta - 2 operações em n pares, então O (n).

Agora, a próxima pergunta é simplesmente se você lava sua própria roupa e sua esposa lava a dela. Esse é um problema provavelmente em um domínio de problemas completamente diferente . :)


Isso não responde à pergunta, onde as meias são apenas uma metáfora.
Amit

A questão era como emparelhar as meias de uma pilha não emparelhada, não como evitar a necessidade de emparelhar.
Amit
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.