Duas linhas de uma matriz são ortogonais se seu produto interno for igual a zero. Chame uma matriz com todas as linhas ortogonais pareadas e uma matriz ortogonal . Uma matriz circulante é aquela em que cada vetor de linha é girado um elemento para a direita em relação ao vetor de linha anterior. Nós estaremos interessados apenas em matrizes onde as entradas são -1
ou 1
.
Tarefa
Escrever código para contar como muitos diferente n/2
por n
, matrizes circulantes ortogonais quanto possível em 2 minutos (mesmo para n
).
Entrada
O código não tem entrada. Ele pode tentar quaisquer valores iguais de n
que goste. Por exemplo, o código pode tentar tudo o n
que é multiplicado, 4
começando pelo menor e também tentar n = 2
.
Resultado
O número de matrizes circulantes ortogonais que você encontrou. Também deve haver uma opção simples no seu código para permitir a saída das próprias matrizes.
Ponto
O número de matrizes circulantes que você encontrou.
Dicas
Ortogonais n/2
por n
matrizes circulantes só existem quando n
é um múltiplo de 4
ou n
é menor que 4
.
Um exemplo de matriz circulante ortogonal é:
-1 1 -1 -1
-1 -1 1 -1
Dicas para uma abordagem ingênua
A abordagem mais ingênua é apenas repetir todas as matrizes possíveis. Isso pode ser acelerado usando as duas observações a seguir.
Para testar a ortogonalidade de uma matriz circulante, precisamos comparar apenas cada linha com a primeira. Isso é implementado no código de exemplo.
Podemos iterar sobre as palavras de Lyndon e, se encontrarmos uma matriz ortogonal, multiplique pelo número de rotações possíveis. Esta é uma idéia ainda não testada, portanto pode ser um erro.
Código de amostra
Esta é uma resposta python muito simples e ingênua. Eu executei usando timeout 120
.
import itertools
def check_orthogonal(row):
for i in xrange(1,int(n/2)):
if (sum(row[j]*row[(j+i) % n] for j in xrange(n)) != 0):
return False
return True
counter = 0
for n in xrange(4,33,4):
for row in itertools.product([-1,1],repeat = n):
if check_orthogonal(row):
counter +=1
print "Counter is ", counter, ". n = ", n
Testes de correção
Pois n = 4,8,12,16,20,24,28
, o número de matrizes distintas que você deve obter é 12,40,144,128,80,192,560
, respectivamente.
Níveis de grandiosidade
A julgar pelo código de exemplo, apresento dois níveis de grandiosidade que qualquer resposta pode aspirar.
A grandiosidade do nível de prata é alcançada obtendo uma pontuação ou 1156 .
O nível de ouro de grandiosidade é ficar mais alto que isso.
Línguas e bibliotecas
Você pode usar qualquer idioma ou biblioteca que desejar (que não foi projetada para este desafio). No entanto, para fins de pontuação, executarei seu código na minha máquina, portanto, forneça instruções claras sobre como executá-lo no Ubuntu.
Minha máquina Os horários serão executados na minha máquina. Esta é uma instalação padrão do Ubuntu em um processador de 8 GB AMD FX-8350 de oito núcleos. Isso também significa que eu preciso poder executar seu código.
Respostas principais
332 por flawr em oitava
404 por RT em Python
744 por Sample Solution usando pypy
1156 por Thomas Kwa usando Java . Nível de prata impressionante!
1588 por Reimer Behrends em OCaml . Incrível nível ouro!
n
múltiplo de quatro?Respostas:
OCaml, 1588 (n = 36)
Esta solução usa a abordagem usual de padrão de bits para representar vetores de -1s e 1s. O produto escalar é calculado como de costume, obtendo-se o xor de vetores de dois bits e subtraindo n / 2. Os vetores são ortogonais se seu xor tiver exatamente n / 2 bits definidos.
As palavras de Lyndon não são, por si só, úteis como uma representação normalizada para isso, pois excluem qualquer padrão que seja uma rotação em si. Eles também são relativamente caros de calcular. Portanto, esse código usa uma forma normal um pouco mais simples, que exige que a sequência consecutiva mais longa de zeros após a rotação (ou um deles, se houver múltiplos) ocupe os bits mais significativos. Daqui resulta que o bit menos significativo é sempre 1.
Observe também que qualquer vetor candidato deve ter pelo menos n / 4 (e no máximo 3n / 4). Portanto, consideramos apenas vetores com n / 4 ... n / 2 bits definidos, pois podemos derivar outros via complemento e rotação (na prática, todos esses vetores parecem ter entre n / 2-2 e n / 2 + 2) , mas isso também parece ser difícil de provar).
Construímos essas formas normais a partir do bit menos significativo, observando a restrição de que quaisquer execuções remanescentes de zeros (chamadas de "lacunas" no código) devem seguir nosso requisito de forma normal. Em particular, desde que seja necessário colocar pelo menos mais um bit, deve haver espaço para o intervalo atual e outro que seja pelo menos tão grande quanto o intervalo atual ou qualquer outro intervalo observado até agora.
Também observamos que a lista de resultados é pequena. Portanto, não tentamos evitar duplicatas durante o processo de descoberta, mas simplesmente registramos os resultados em conjuntos por trabalhador e calculamos a união desses conjuntos no final.
Vale ressaltar que o custo de tempo de execução do algoritmo ainda aumenta exponencialmente e a uma taxa comparável à da versão de força bruta; o que isso nos compra é essencialmente uma redução por um fator constante e tem o custo de um algoritmo mais difícil de paralelizar do que a versão de força bruta.
Saída para n até 40:
O programa está escrito em OCaml, para ser compilado com:
Execute
./orthcirc -help
para ver quais opções o programa suporta.Nas arquiteturas que o suportam,
-fno-PIC
pode oferecer alguns pequenos ganhos adicionais de desempenho.Isso foi escrito para o OCaml 4.02.3, mas também pode funcionar com versões mais antigas (desde que não sejam muito antigas).
ATUALIZAÇÃO: Esta nova versão oferece melhor paralelização. Observe que ele usa
p * (n/4 + 1)
threads de trabalho por instância do problema, e alguns deles ainda serão consideravelmente mais curtos que outros. O valor dep
deve ser uma potência de 2. A aceleração em 4-8 núcleos é mínima (talvez em torno de 10%), mas é melhor para um grande número de núcleosn
.fonte
Java, 1156 matrizes
Isso usa bitmasking bastante ingênuo e leva menos de 15 segundos para n = 28 na minha máquina.
Matrizes circulantes são determinadas por suas primeiras linhas. Portanto, eu represento as primeiras linhas das matrizes como vetores de bits: 1 e 0 representam -1 e 1. Duas linhas são ortogonais quando o número de bits definidos quando eles são xor'd juntos é n / 2.
Não consigo fazer o Eclipse funcionar no momento, então isso foi testado no repl.it.
Aqui está o número de primeiras linhas ortogonais às primeiras r linhas depois para n = 28:
Otimizações:
long
e, em seguida, uso um únicoand
com os N bits inferiores para extrair os necessários.Possíveis otimizações adicionais:
00
e usar suas rotações (e rotações do NOT) quando encontrarmos uma matriz ortogonal. Podemos fazer isso porque,0101...01
e1010...10
não são possíveis, as primeiras linhas e todas as outras contêm a00
ou a11
.fonte
n=36
também.Python (matrizes 404 no i5-5300U)
Principalmente postar isso como um ponto de partida para outras pessoas melhorarem, isso pode ser muito limpo, paralelizado etc.
fonte
Matlab / Octave, matrizes 381/328
Também apenas a abordagem ingênua, tentando todas as combinações possíveis.
fonte
n
e umz
, essas duas podem ser comentadas com uma única%
. E então você pode adicionar um;
após ocounter = counter+1
e ok/N
que suprimirá a saída.