Palavras cruzadas em japonĂȘs (tambĂ©m nonograms) sĂŁo quebra-cabeças lĂłgicos nos quais uma imagem de pixel Ă© criptografada. VocĂȘ precisa resolver as palavras cruzadas usando nĂșmeros localizados Ă esquerda das linhas e acima das colunas.
O tamanho das palavras cruzadas pode atingir até 150 x 150. O jogador usa lógica especial para calcular a cor de cada célula. A solução pode levar alguns minutos em palavras cruzadas para iniciantes e dezenas de horas em quebra-cabeças complexos.
Um bom algoritmo pode resolver o problema muito mais rapidamente. O texto descreve como escrever uma solução que funciona quase instantaneamente usando os algoritmos mais adequados (que geralmente levam a uma solução), bem como suas otimizaçÔes e o uso de recursos do C ++ (que reduzem o tempo de execução por vårias dezenas de vezes).
Na versĂŁo mĂłvel do Habr, as fĂłrmulas no texto podem nĂŁo ser exibidas (nĂŁo em todos os navegadores mĂłveis) - use a versĂŁo completa ou outro navegador mĂłvel, se detectar problemas com as fĂłrmulas
Regras do jogo
Inicialmente, a tela de palavras cruzadas Ă© branca. Para palavras cruzadas em preto e branco, vocĂȘ precisa restaurar o local das cĂ©lulas negras.
Nas palavras cruzadas em preto e branco, o nĂșmero de nĂșmeros de cada linha (Ă esquerda da tela) ou de cada coluna (parte superior da tela) mostra quantos grupos de cĂ©lulas pretas estĂŁo na linha ou coluna correspondente e os prĂłprios nĂșmeros mostram quantas cĂ©lulas mescladas cada um desses grupos contĂ©m. Conjunto de nĂșmeros [ 3 , 2 , 5 ] significa que em uma determinada linha existem trĂȘs grupos consecutivos de 3 , 2 e 5 cĂ©lulas pretas seguidas. Os grupos podem ser organizados em uma fileira aleatoriamente, sem violar a ordem relativa (os nĂșmeros especificam seu comprimento e a posição deve ser adivinhada), mas devem ser separados por pelo menos uma cĂ©lula branca.
Exemplo correto
Nas palavras cruzadas em cores, cada grupo ainda tem sua própria cor - qualquer, exceto o branco, é uma cor de fundo. Grupos vizinhos de cores diferentes podem ficar lado a lado, mas para grupos vizinhos das mesmas cores, a separação por pelo menos uma célula branca ainda é necessåria.
O que nĂŁo Ă© uma palavra cruzada japonesa
Cada imagem de pixel pode ser criptografada como um jogo de palavras cruzadas. Mas pode nĂŁo ser possĂvel restaurĂĄ-lo de volta - o quebra-cabeça resultante pode ter mais de uma solução ou uma solução, mas nĂŁo pode ser resolvido logicamente. Em seguida, as cĂ©lulas restantes no jogo devem ser adivinhadas usando a tecnologia xamĂąnica usando computadores quĂąnticos .
Tais palavras cruzadas nĂŁo sĂŁo palavras cruzadas, mas graphomania. Acredita-se que as palavras cruzadas corretas sejam tais que, de maneira lĂłgica, vocĂȘ possa encontrar uma solução Ășnica.
O "caminho lĂłgico" Ă© a capacidade de restaurar cada cĂ©lula uma por uma, considerando a linha / coluna separadamente ou sua interseção. Se isso nĂŁo for possĂvel, o nĂșmero de opçÔes de resposta consideradas pode ser muito, muito mais do que uma pessoa pode calcular por si mesma.

O nonograma errado Ă© a Ășnica solução, mas Ă© impossĂvel resolver normalmente. CĂ©lulas "insolĂșveis" rotuladas em laranja. Retirado da Wikipedia.
Essa restrição é explicada da seguinte maneira - no caso mais geral, as palavras cruzadas japonesas são um problema de NP-completo. No entanto, adivinhar não se torna uma tarefa completa de NP, se houver um algoritmo que, a cada instante, indique inequivocamente quais células abrir mais. Todas as palavras cruzadas usadas por seres humanos (exceto pelo método Monte Carlo tentativa e erro) são baseados nisso.
A maioria das palavras cruzadas ortodoxas divide-se por 5 larguras e alturas, nĂŁo hĂĄ linhas que possam ser contadas instantaneamente (aquelas em que cĂ©lulas coloridas obstruem todos os lugares ou elas nĂŁo existem) e o nĂșmero de cores complementares Ă© limitado. Esses requisitos nĂŁo sĂŁo necessĂĄrios.
As palavras cruzadas erradas mais simples:
|1 1| --+---+ 1| | 1| | --+---+
FreqĂŒentemente, a arte pixel codificada, que usa um padrĂŁo de "tabuleiro de damas" para simular um gradiente, nĂŁo Ă© resolvida ao contrĂĄrio. Ă melhor entender se vocĂȘ digitar "gradiente de pixelart" na pesquisa. O gradiente Ă© como estas palavras cruzadas 2x2 fayl.
PossĂveis soluçÔes
Temos o tamanho das palavras cruzadas, uma descrição das cores e todas as linhas e colunas. Como montar uma imagem a partir disso:
Pesquisa completa
Para cada cĂ©lula, classificamos todos os estados possĂveis e verificamos se Ă© satisfatĂłrio. A complexidade dessa solução para palavras cruzadas em preto e branco O ( N â M â 2 N â M ) para cor O ( N * M * c o r e s N * H ) . Por analogia com a classificação de palhaços, essa solução tambĂ©m pode ser chamada de palhaço. Ă adequado para o tamanho 5x5.
Backtracking
Todos os mĂ©todos possĂveis de organizar grupos horizontais de cĂ©lulas sĂŁo resolvidos. Colocamos um grupo de cĂ©lulas em uma linha, verifique se ele nĂŁo quebra a descrição dos grupos de colunas. Se quebrar, colocamos mais uma cĂ©lula, verificamos novamente. Set - vĂĄ para o prĂłximo grupo. Se vocĂȘ nĂŁo pode colocar um grupo de forma alguma, revertemos dois grupos, reorganizamos o grupo anterior e assim por diante, atĂ© colocarmos o Ășltimo grupo com ĂȘxito.
Separadamente para cada linha
Esta decisão é muito melhor e é verdadeiramente verdadeira. Podemos analisar cada linha e cada coluna individualmente. Cada linha tentarå revelar o måximo de informaçÔes.
O algoritmo de cada cĂ©lula da linha obtĂ©m mais informaçÔes - pode ser impossĂvel colorir a cĂ©lula com alguma cor, os grupos nĂŁo convergirĂŁo. VocĂȘ nĂŁo pode expandir completamente a linha imediatamente, mas as informaçÔes recebidas "ajudarĂŁo" a se abrir melhor para vĂĄrias colunas e, quando começarmos a analisĂĄ-las, elas "ajudarĂŁo" novamente as linhas, e assim por diante por vĂĄrias iteraçÔes, atĂ© que todas as cĂ©lulas tenham uma cor possĂvel.
DecisĂŁo verdadeiramente verdadeira
Uma linha, duas cores
A adivinhação eficaz da âlinha Ășnicaâ em preto e branco, para a qual algumas cĂ©lulas jĂĄ adivinharam, Ă© uma tarefa muito difĂcil. Ela conheceu em lugares como:
- Quartas de final do ACM ICPC 2006 - vocĂȘ pode tentar resolver o problema sozinho . O limite de tempo Ă© de 1 segundo, o limite para o nĂșmero de grupos Ă© 400 e o comprimento da sĂ©rie tambĂ©m Ă© 400. Ele possui um nĂvel muito alto de complexidade em comparação com outras tarefas.
- OlimpĂada Internacional de InformĂĄtica 2016 - condição para passar a tarefa . O limite de tempo Ă© de 2 segundos, o limite do nĂșmero de grupos Ă© 100, o comprimento da linha Ă© 200'000. Essas limitaçÔes sĂŁo um exagero, mas a solução de um problema com restriçÔes do ACM ganha 80/100 pontos nesse problema. Aqui, tambĂ©m, o nĂvel nĂŁo decepcionou, crianças de todo o mundo com um nĂvel de QI cruel treinam hĂĄ vĂĄrios anos para resolver truques diferentes, depois vĂŁo para esta OlimpĂada (apenas 4 pessoas do paĂs devem passar), decidem 2 rodadas de 5 horas
e, no caso da vitória épica (bronze em anos diferentes, para 138-240 pontos em 600), a admissão em Oxford e, em seguida, oferece de empresas claras ao departamento de busca, uma vida longa e feliz abraçada com dakimakura.
A linha Ășnica monocromĂĄtica tambĂ©m pode ser resolvida de diferentes maneiras e, por O ( N â 2 N ) (enumerando todas as opçÔes, verificando a correção, selecionando cĂ©lulas que tĂȘm a mesma cor em todas as opçÔes) e, de alguma forma, menos estĂșpida.
A idĂ©ia principal Ă© usar um anĂĄlogo de retorno, mas sem cĂĄlculos desnecessĂĄrios. Se de alguma forma configuramos vĂĄrios grupos e agora estamos em algum tipo de cĂ©lula, precisamos descobrir se Ă© possĂvel colocar os demais grupos, começando na cĂ©lula atual.
Pseudo cĂłdigo def EpicWin(group, cell): if cell >= last_cell:
Essa abordagem é chamada de programação dinùmica . O pseudocódigo é simplificado e até os valores calculados não são armazenados lå.
As CanInsertBlack/CanInsertWhite
sĂŁo necessĂĄrias para verificar se Ă© teoricamente possĂvel colocar um grupo do tamanho certo no lugar certo. Tudo o que eles precisam fazer Ă© verificar se nĂŁo hĂĄ cĂ©lula â100% brancaâ (para a primeira função) ou â100% pretaâ (para a segunda) no intervalo de cĂ©lulas indicado. Para que eles possam trabalhar O ( 1 ) , isso pode ser feito usando valores parciais:
CanInsertBlack white_counter = [ 0, 0, ..., 0 ]
A mesma feitiçaria com somas parciais aguarda uma linha do formulårio
can_place_black[cell .. (cell + len[group] - 1)] = True
Aqui podemos, em vez de = True
aumentar o nĂșmero em 1. E se precisarmos fazer muitas adiçÔes em um segmento em uma determinada matriz a r r a y e, alĂ©m disso, nĂŁo usamos essa matriz antes de adiçÔes diferentes (eles dizem que essa tarefa foi "resolvida offline") e, em vez de um loop:
VocĂȘ pode fazer isso:
Assim, todo o algoritmo trabalha em O ( k â n ) onde k - o nĂșmero de grupos de cĂ©lulas negras, n Ă o comprimento da string. E vamos Ă semifinal do ACM ICPC ou obtemos o bronze do interneur. Solução ACM (Java) . Solução IOI (C ++) .
Uma linha, muitas cores
Quando mudamos para nonogramas multicoloridos, que ainda nĂŁo estĂŁo claros como resolver, aprendemos uma verdade terrĂvel - acontece que todas as colunas sĂŁo magicamente afetadas por todas as colunas! Isso Ă© mais claro atravĂ©s de um exemplo:
Fonte - MĂ©todos para resolver palavras cruzadas em japonĂȘs
Enquanto os nonogramas de duas cores normalmente ficavam sem ele, eles nĂŁo precisavam olhar para os amigos ortogonais.
A figura mostra que, no exemplo da esquerda, as trĂȘs cĂ©lulas da extrema direita estĂŁo vazias, porque a configuração Ă© interrompida se vocĂȘ colorir essas cĂ©lulas em aquelas cores nas quais vocĂȘ se pinta cor nĂŁo branca.
Mas essa piada Ă© matematicamente decidĂvel - cada cĂ©lula deve receber um nĂșmero, onde eu o i-Ă©simo bit significa se essa cĂ©lula pode ser fornecida eu ÂȘ cor. Inicialmente, todas as cĂ©lulas tĂȘm um valor 2 c o r e s - 1 . A decisĂŁo da dinĂąmica nĂŁo mudarĂĄ muito.
VocĂȘ pode observar o seguinte efeito: no mesmo exemplo Ă esquerda, de acordo com a versĂŁo das linhas, a cĂ©lula na extremidade direita pode ter a cor azul ou branca.
De acordo com a versão das colunas, essa célula tem a cor verde ou branca.
De acordo com a versão de senso comum, essa célula terå apenas a cor branca. E então continuamos a calcular a resposta.
Se considerarmos o bit zero como "branco", o primeiro "azul", o segundo "verde", a linha calcularĂĄ o estado da Ășltima cĂ©lula 011 e a coluna 101 . Isso significa que o estado dessa cĂ©lula Ă© real 011 \ & 101 = 001
Muitas linhas, muitas cores
Fazendo constantemente a atualização dos estados de todas as linhas e colunas, descritas no Ășltimo parĂĄgrafo, atĂ© que nĂŁo haja uma Ășnica cĂ©lula com mais de um bit. Em cada iteração, apĂłs atualizar todas as linhas e colunas, atualizamos o estado de todas as cĂ©lulas nelas por meio de AND mĂștuo.
Primeiros resultados
Suponha que nĂŁo escrevemos o cĂłdigo como pica-paus, ou seja, nĂŁo copiamos objetos em lugar nenhum por engano, em vez de passarmos por referĂȘncia, nĂŁo inventamos bicicletas em lugar algum, nĂŁo inventamos bicicletas, para operaçÔes de bit usamos __builtin_popcount
e __builtin_ctz
( recursos do gcc ), tudo Ă© limpo e arrumado.
Vamos dar uma olhada na hora do programa, que lĂȘ o quebra-cabeça do arquivo e o resolve completamente. Vale a pena avaliar as vantagens da mĂĄquina na qual todas essas coisas foram escritas e testadas:
Especificação do motor para minha motocicleta clåssica Harley Davidson Modelo B 1932 RAM - 4GB - AMD E1-2500, 1400MHz L1 - 128KiB, 1GHz L2 - 1MiB, 1GHz - 2, - 2 « SoC » 2013 28
Ă claro que esse supercomputador foi escolhido porque as otimizaçÔes nele tĂȘm um efeito maior do que em uma mĂĄquina shaitan voadora.
Portanto, depois de executar nosso algoritmo no mais complicado jogo de palavras cruzadas (de acordo com nonograms.ru), obtemos um resultado nĂŁo muito bom - de 47 a 60 segundos (isso inclui a leitura de um arquivo e uma solução). Note-se que a "complexidade" no site foi bem calculada - as mesmas palavras cruzadas em todas as versĂ”es do programa tambĂ©m foram as mais difĂceis, e outras palavras-chave mais complexas, na opiniĂŁo do arquivo, foram mantidas em tempo integral.
Palavras cruzadas cor nĂșmero 9596, a maior dificuldade Para testes rĂĄpidos, foi feita a funcionalidade do benchmark. Para obter dados para ele, eu usei um script especial e os salvei em um formato que o programa entende. Podemos supor que essas palavras cruzadas sĂŁo uma amostra aleatĂłria; portanto, o benchmark mostraria valores mĂ©dios adequados. AlĂ©m disso, o nĂșmero de algumas das palavras cruzadas mais "complexas" (tambĂ©m as maiores em tamanho e nĂșmero de cores) foi registrado em um script para carregar canetas.
Otimização
Aqui vocĂȘ pode ver as possĂveis tĂ©cnicas de otimização que foram feitas (1) durante a gravação de todo o algoritmo, (2) para reduzir o tempo de trabalho de meio minuto para uma fração de segundo, (3) as otimizaçÔes que podem ser Ășteis ainda mais.
No momento da escrita do algoritmo
- FunçÔes especiais para operaçÔes de bit, no nosso caso
__builtin_popcount
para contar unidades em uma notação binĂĄria de um nĂșmero e __builtin_ctz
para o nĂșmero de zeros antes da primeira unidade menor. Tais funçÔes podem nĂŁo aparecer em alguns compiladores. Para o Windows, esses anĂĄlogos sĂŁo adequados:
Popcount do Windows #ifdef _MSC_VER # include <intrin.h> # define __builtin_popcount __popcnt #endif
- Organização de matrizes - um tamanho menor é o primeiro. Por exemplo, é melhor usar a matriz [2] [3] [500] [1024] do que [1024] [500] [3] [2].
- O mais importante é a adequação geral do código e a prevenção de preocupaçÔes desnecessårias nos cålculos.
O que reduz o tempo de trabalho
- O sinalizador -O2 ao compilar.
- Para nĂŁo lançar a linha / coluna completamente resolvida no algoritmo novamente, vocĂȘ pode definir os sinalizadores para cada linha em um std :: vector / array separado, marcĂĄ-los com uma solução completa e nĂŁo deixar ir, se nĂŁo houver mais nada a resolver.
- As especificidades de uma solução de repetição mĂșltipla de um problema dinĂąmico sugerem que uma matriz especial que contenha sinalizadores marcando partes jĂĄ "calculadas" do problema seja redefinida toda vez que uma nova solução for feita. No nosso caso, esse Ă© um vetor / array bidimensional, em que a primeira dimensĂŁo Ă© o nĂșmero de grupos, a segunda Ă© a cĂ©lula atual (consulte o pseudocĂłdigo EpicWin acima, onde esse array nĂŁo Ă©, mas a idĂ©ia Ă© clara). Em vez de zerar, vocĂȘ pode fazer isso - vamos ter uma variĂĄvel "timer", e a matriz consiste em nĂșmeros que mostram a Ășltima "hora" em que essa peça foi recontada pela Ășltima vez. Ao iniciar uma nova tarefa, o "timer" aumenta em 1. Em vez de verificar o sinalizador booleano, vocĂȘ deve verificar a igualdade do elemento da matriz e o "timer". Isso Ă© eficaz, especialmente nos casos em que longe de todos os estados possĂveis sĂŁo cobertos pelo algoritmo (o que significa que zerar a matriz "jĂĄ consideramos isso" leva um bom tempo).
- Substituindo operaçÔes simples do mesmo tipo (loops com atribuição, etc.) por anĂĄlogos em STL ou coisas mais adequadas. Ăs vezes, funciona mais rĂĄpido que uma bicicleta.
std::vector<bool>
em C ++ compacta todos os valores booleanos em bits individuais em nĂșmeros, o que funciona no acesso um pouco mais lento do que se fosse um valor regular no endereço. Se um programa usa esses valores com muita frequĂȘncia, a substituição de bool por um tipo inteiro pode ter um bom efeito no desempenho.- Outras fraquezas podem ser buscadas atravĂ©s dos criadores de perfil e editĂĄ-las. Eu mesmo uso o Valgrind, sua anĂĄlise de desempenho Ă© conveniente para examinar o kCachegrind. Os criadores de perfil sĂŁo incorporados a muitos IDEs.
Essas ediçÔes foram suficientes para obter esses dados no benchmark:
Nonogramas coloridos izaron@izaron:~/nonograms/build$ ./nonograms_solver -x ../../nonogram/source2/ -e [2018-08-04 22:57:40.432] [Nonograms] [info] Starting a benchmark... [2018-08-04 22:58:03.820] [Nonograms] [info] Average time: 0.00497556, Median time: 0.00302781, Max time: 0.215925 [2018-08-04 22:58:03.820] [Nonograms] [info] Top 10 heaviest nonograms: [2018-08-04 22:58:03.820] [Nonograms] [info] 0.215925 seconds, file 9596 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.164579 seconds, file 4462 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.105828 seconds, file 15831 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.08827 seconds, file 18353 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.0874717 seconds, file 10590 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.0831248 seconds, file 4649 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.0782811 seconds, file 11922 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.0734325 seconds, file 4655 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.071352 seconds, file 10828 [2018-08-04 22:58:03.820] [Nonograms] [info] 0.0708263 seconds, file 11824
Nonogramas em preto e branco izaron@izaron:~/nonograms/build$ ./nonograms_solver -x ../../nonogram/source/ -e -b [2018-08-04 22:59:56.308] [Nonograms] [info] Starting a benchmark... [2018-08-04 23:00:09.781] [Nonograms] [info] Average time: 0.0095679, Median time: 0.00239274, Max time: 0.607341 [2018-08-04 23:00:09.781] [Nonograms] [info] Top 10 heaviest nonograms: [2018-08-04 23:00:09.781] [Nonograms] [info] 0.607341 seconds, file 5126 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.458932 seconds, file 19510 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.452245 seconds, file 5114 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.19975 seconds, file 18627 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.163028 seconds, file 5876 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.161675 seconds, file 17403 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.153693 seconds, file 12771 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.146731 seconds, file 5178 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.142732 seconds, file 18244 [2018-08-04 23:00:09.781] [Nonograms] [info] 0.136131 seconds, file 19385
VocĂȘ pode notar que, em mĂ©dia, as palavras cruzadas em preto e branco sĂŁo "mais difĂceis" do que as coloridas. Isso confirma as observaçÔes dos amantes de jogos, que tambĂ©m acreditam que a "cor" Ă© resolvida em mĂ©dia com mais facilidade.
Portanto, sem alteraçÔes radicais (como reescrever todo o cĂłdigo em C ou inserçÔes de assembler com chamada rĂĄpida e abaixar o ponteiro do quadro), vocĂȘ pode obter alto desempenho em um computador muito modesto. O princĂpio de Pareto pode ser aplicĂĄvel Ă s otimizaçÔes - verifica-se que a otimização menor influencia fortemente, porque esse trecho de cĂłdigo Ă© crĂtico e Ă© chamado com muita frequĂȘncia.
Otimização adicional
Os métodos a seguir podem melhorar muito o desempenho do programa. Alguns deles não funcionam em todos os casos, mas sob certas condiçÔes.
- Reescrevendo o cĂłdigo no estilo C e outros 1972. SubstituĂmos ifstream pelo equivalente analĂłgico, vetores por arrays, aprendemos todas as opçÔes do compilador e lutamos por cada ciclo de clock do processador.
- Paralelização. Por exemplo, no cĂłdigo, hĂĄ uma parte em que as linhas sĂŁo atualizadas seqĂŒencialmente e as colunas:
bool Puzzle :: UpdateState (...) ... if (!UpdateGroupsState(solver, dead_rows, row_groups, row_masks)) { return false; } if (!UpdateGroupsState(solver, dead_cols, col_groups, col_masks)) { return false; } return true;
Essas funçÔes sĂŁo independentes umas das outras e nĂŁo possuem memĂłria compartilhada, exceto pelo solucionador de variĂĄveis ââ(tipo OneLineSolver), para que vocĂȘ possa criar dois objetos do solucionador (aqui, obviamente, apenas um Ă© solucionador) e iniciar dois threads na mesma função. O efeito Ă© o seguinte: nas palavras cruzadas coloridas, o âmais difĂcilâ Ă© resolvido duas vezes mais rĂĄpido, em preto e branco o mesmo Ă© um terço mais rĂĄpido e o tempo mĂ©dio aumentou, devido ao custo relativamente alto da criação de threads.
Mas, em geral, eu nĂŁo recomendaria fazĂȘ-lo corretamente no programa atual e usĂĄ-lo com calma - em primeiro lugar, criar threads Ă© uma operação cara, vocĂȘ nĂŁo deve constantemente criar threads para tarefas de microssegundos e, em segundo lugar, com alguma combinação de argumentos do programa, os threads podem se referir a quais memĂłria externa, por exemplo, ao criar imagens de uma solução - isso deve ser levado em consideração e protegido.
Se a tarefa fosse sĂ©ria e eu tivesse muitos dados e mĂĄquinas com vĂĄrios nĂșcleos, iria ainda mais longe - vocĂȘ pode criar vĂĄrios encadeamentos constantes, cada um com seu prĂłprio objeto OneLineSolver e outra estrutura segura de encadeamento que controla a distribuição do trabalho e sob demanda fornece uma referĂȘncia para a prĂłxima linha / coluna da solução. Os threads apĂłs resolver outro problema se voltam para a estrutura novamente para resolver outra coisa. Em princĂpio, um problema de nĂŁo programa pode ser iniciado sem concluir o anterior, por exemplo, quando essa estrutura estĂĄ envolvida no AND mĂștuo de todas as cĂ©lulas, e entĂŁo alguns fluxos sĂŁo livres e nĂŁo fazem nada. Mais paralelismo pode ser feito na GPU via CUDA - hĂĄ muitas opçÔes.
- Usando estruturas de dados clåssicas. Observe que, quando mostrei o pseudocódigo da solução para nonograms de cores, as funçÔes CanInsertColor e PlaceColor não funcionam de todo O ( 1 ) , diferentemente dos nonogramas em preto e branco. Parece que isso em um programa:
CanPlaceColor e SetPlaceColor bool OneLineSolver::CanPlaceColor(const vector<int>& cells, int color, int lbound, int rbound) {
Ou seja, funciona para a linha, para O(N) (Posteriormente, haverå uma explicação do significado de um código assim).
Vamos ver como vocĂȘ pode obter a melhor complexidade. Tome CanPlaceColor
. Esta função verifica que entre todos os nĂșmeros do vetor no segmento [vinculado,vinculado] nĂșmero de bit cor definido como 1. Equivalente a isso, vocĂȘ pode AND todos os nĂșmeros deste segmento e verifique o nĂșmero do bit cor . Usando o fato de que a operação AND comutativa , bem como soma, mĂnimo / mĂĄximo, produto ou operação Xou , para contagem rĂĄpida AND , . :
- SQRT-. O(âN) , O(âN) . .
- . O(NlogN) , O(logN) . .
- (Sparse Table). O(NlogN) , O(1) . .
, - ( O(N) , O(1) ) , , , varphi , Ï(α,ÎČ)â{α,ÎČ} , â (, ...)
SetPlaceColor
. . SQRT- ( " "). - .
- â .
, â , ? :
- . log , , â ( magic number , ). , N=105 , O(N2) 10 , O(NlogN) 0.150 , N , . , ( ): O(NâN) versus O(Nlog2N) . N ( ) â 15-30.
- , .
â , - â N , . , ~25% ~11% , . - , , , .
â , . .
- . , , - . : , , "" ? , , ! . , .
- (, 1337 1000x1000) . std::bitset , â , .
, . "" . , C++ ( rope , ), hashtable , 3-6 / 4-10 /, unordered_map. , boost.
, , , -.
ROFL â , "GNU's Not Unix". , , , , , . Omitting â (, , : â ).
, Matroska â 4 [52][4F][46][4C], , , , 3 , â , .
, MIT, â , .
GitHub . , , (, ). Magick++ args .
, ( ). nonograms.ru , , .
nonograms.ru .. KyberPrizrak , , , ! nonograms.ru, .
.