Entre os melhores
User Language Score
=========================================
Ell C++11 293,619,555
feersum C++11 100,993,667
Ell C++11 78,824,732
Geobits Java 27,817,255
Ell Python 27,797,402
Peter Taylor Java 2,468
<reference> Julia 530
fundo
Ao trabalhar em uma grade bidimensional de coordenadas inteiras, às vezes você deseja saber se dois vetores (com componentes inteiros) têm a mesma magnitude. Obviamente, na geometria euclidiana, a magnitude de um vetor (x,y)
é dada por
√(x² + y²)
Portanto, uma implementação ingênua pode calcular esse valor para os dois vetores e comparar os resultados. Isso não apenas gera um cálculo desnecessário de raiz quadrada, mas também causa problemas com imprecisões de ponto flutuante, que podem gerar falsos positivos: vetores cujas magnitudes são diferentes, mas onde os dígitos significativos na representação de ponto flutuante são todos idênticos.
Para os propósitos deste desafio, definimos um falso positivo como um par de pares de coordenadas (a,b)
e (c,d)
para o qual:
- Sua magnitude ao quadrado é diferente quando representada como números inteiros não assinados de 64 bits.
- Sua magnitude é idêntica quando representada como número de ponto flutuante binário de 64 bits e calculada através de uma raiz quadrada de 64 bits (conforme IEEE 754 ).
Como um exemplo, usando representações de 16 bits em vez de (64), o mais pequeno um par de vectores que produz um falso positivo é
(25,20) and (32,0)
Suas magnitudes ao quadrado ao quadrado são 1025
e 1024
. Tomando os rendimentos da raiz quadrada
32.01562118716424 and 32.0
Mas em carros alegóricos de 16 bits, ambos são truncados 32.0
.
Da mesma forma, o menor par 2 que produz um falso positivo para representações de 32 bits é
(1659,1220) and (1951,659)
1 "Menor", medido pela magnitude do ponto flutuante de 16 bits.
2 "Menor", medido pela magnitude do ponto flutuante de 32 bits.
Por fim, aqui estão alguns casos válidos de 64 bits:
(51594363,51594339) and (54792160,48184783)
(54356775,54353746) and (54620742,54088476)
(54197313,46971217) and (51758889,49645356)
(67102042, 956863) and (67108864, 6) *
*
O último caso é um dos vários com a menor magnitude possível para falsos positivos de 64 bits.
O desafio
Em menos de 10.000 bytes de código, usando um único encadeamento, você encontrará o número de falsos positivos para números de ponto flutuante (binário) de 64 bits no intervalo de coordenadas 0 ≤ y ≤ x
(ou seja, apenas dentro do primeiro octante do plano euclidiano) tal que dentro de 10 minutos . Se dois envios empatam para o mesmo número de pares, o desempate é o tempo real necessário para encontrar o último desses pares.x² + y² ≤ 253
Seu programa não deve usar mais de 4 GB de memória a qualquer momento (por motivos práticos).
Deve ser possível executar o seu programa de dois modos: um que gera todos os pares como o encontra e outro que gera apenas o número de pares encontrados no final. O primeiro será usado para verificar a validade de seus pares (observando algumas amostras de saídas) e o segundo será usado para realmente cronometrar o envio. Observe que a impressão deve ser a única diferença. Em particular, o programa de contagem pode não codificar o número de pares que poderia encontrar. Ele ainda deve executar o mesmo loop que seria usado para imprimir todos os números e omitir apenas a impressão em si!
Testarei todos os envios no meu laptop com Windows 8; portanto, pergunte nos comentários se você deseja usar um idioma não muito comum.
Observe que os pares não devem ser contados duas vezes sob a comutação do primeiro e do segundo par de coordenadas.
Observe também que executarei seu processo por meio de um controlador Ruby, que matará seu processo se não terminar após 10 minutos. Certifique-se de gerar o número de pares encontrados até então. Você pode acompanhar o tempo e imprimir o resultado imediatamente antes dos 10 minutos decorridos, ou você apenas gera o número de pares encontrados esporadicamente, e tomarei o último número como sua pontuação.
fonte
Respostas:
C ++, 275.000.000+
Vamos nos referir a pares cuja magnitude é exatamente representável, como (x, 0) , como pares honestos e a todos os outros pares como pares desonestos de magnitude m , onde m é a magnitude relatada incorretamente do par. O primeiro programa do post anterior usou um conjunto de pares estreitamente relacionados de pares honestos e desonestos:
(x, 0) e (x, 1) , respectivamente, para x suficientemente grandes. O segundo programa usou o mesmo conjunto de pares desonestos, mas estendeu o conjunto de pares honestos, procurando todos os pares honestos de magnitude integral. O programa não termina em dez minutos, mas encontra a grande maioria de seus resultados muito cedo, o que significa que a maior parte do tempo de execução é desperdiçada. Em vez de continuar procurando pares honestos cada vez menos frequentes, esse programa usa o tempo livre para fazer a próxima coisa lógica: estender o conjunto de pares desonestos .
No post anterior, sabemos que para todos os números inteiros grandes o suficiente r , sqrt (r 2 + 1) = r , em que sqrt é a função de raiz quadrada de ponto flutuante. Nosso plano de ataque é encontrar pares P = (x, y) tais que x 2 + y 2 = r 2 + 1 para algum número inteiro grande o suficiente r . Isso é simples o suficiente, mas procurar ingenuamente esses pares individuais é muito lento para ser interessante. Queremos encontrar esses pares em massa, assim como fizemos para pares honestos no programa anterior.
Seja { v , w } um par de vetores ortonormal. Para todos os escalares reais r , || r v + w || 2 = r 2 + 1 . No ℝ 2 , este é um resultado direto do teorema de Pitágoras:
Estamos à procura de vetores V e w tal que existe um número inteiro r para o qual x e y também são inteiros. Como observação lateral, observe que o conjunto de pares desonestos que usamos nos dois programas anteriores era simplesmente um caso especial disso, onde { v , w } era a base padrão de ℝ 2 ; desta vez, queremos encontrar uma solução mais geral. É aqui que os trigêmeos pitagóricos (trigêmeos inteiros (a, b, c) satisfazem a 2 + b 2 = c 2, que usamos no programa anterior) voltam.
Seja (a, b, c) um trigêmeo pitagórico. Os vectores de v = (b / c, a / c) e w = (-a / C, b / c), (e também
w = (a / c, b / c) ) são ortonormais, como é fácil de verificar . Como se vê, para qualquer escolha do trigêmeo pitagórico, existe um número inteiro r tal que x e y são números inteiros. Para provar isso e encontrar efetivamente r e P , precisamos de um pouco de teoria dos números / grupos; Vou poupar os detalhes. De qualquer maneira, suponha que tenhamos nossa integral r , x e y . Ainda faltam algumas coisas: precisamos de rser grande o suficiente e queremos um método rápido para derivar muitos outros pares semelhantes deste. Felizmente, existe uma maneira simples de fazer isso.
Observe que a projeção de P em v é r v , portanto r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tudo isso para dizer que xb + ya = rc . Como resultado, para todos os números inteiros n , (x + bn) 2 + (y + an) 2 = (x 2 + y 2 ) + 2 (xb + ya) n + (a 2 + b 2 ) n 2 = ( r 2 + 1) + 2 (rc) n + (c 2 ) n 2 = (r + cn) 2 + 1. Em outras palavras, a magnitude ao quadrado dos pares da forma
(x + bn, y + an) é (r + cn) 2 + 1 , que é exatamente o tipo de pares que estamos procurando! Para n grande o suficiente , esses são pares desonestos de magnitude r + cn .
É sempre bom olhar para um exemplo concreto. Se pegarmos o trigêmeo pitagórico (3, 4, 5) , então em r = 2 , temos P = (1, 2) (você pode verificar que (1, 2) · (4/5, 3/5) = 2 e, claramente, um 2 + 2 2 = 2 2 + 1 .) a adição de 5 a r e (4, 3) de P nos leva a r '= 2 + 5 = 7 e P' = (1 + 4, 2 + 3) = (5, 5) . Lo e eis que 5 2 + 5 2 = 7 2 + 1. As próximas coordenadas são r '' = 12 e P '' = (9, 8) e, novamente, 9 2 + 8 2 = 12 2 + 1 , e assim por diante ...
Uma vez que r é grande o suficiente, nós começar a receber desonestas pares com incrementos de magnitude de 5 . São aproximadamente 27.797.402 / 5 pares desonestos.
Então agora temos muitos pares desonestos de magnitude integral. Podemos facilmente associá-los aos pares honestos do primeiro programa para formar falsos positivos e, com o devido cuidado, também podemos usar os pares honestos do segundo programa. Isso é basicamente o que esse programa faz. Como o programa anterior, ele também encontra a maioria dos resultados muito cedo - chega a 200.000.000 de falsos positivos em alguns segundos - e depois diminui consideravelmente.
Compile com
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Para verificar os resultados, adicione-DVERIFY
(isso será notavelmente mais lento).Corra com
flspos
. Qualquer argumento da linha de comandos para o modo detalhado.fonte
Python, 27.797.402
Só para deixar a barra um pouco mais alta ...
É fácil verificar se, para todos os 67.108.864 <= x <= 94.906.265 = floor (sqrt (2 53 )), os pares (x, 0) e (x, 1) são falsos positivos.
Por que funciona : 67.108.864 = 2 26 . Portanto, todos os números x no intervalo acima têm a forma 2 26 + x ' para alguns 0 <= x' <2 26 . Para todo positivo e , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Se queremos ter
(x + e) 2 = x 2 + 1 , precisamos de pelo menos 2 27 e <= 1 , isto é, e <= 2-27 No entanto, como a mantissa de números de ponto flutuante de precisão dupla tem 52 bits de largura, o menor e é tal que x + e> x é e = 2 26 - 52 = 2 -26 . Em outras palavras, o menor número representável maior que x é x + 2-26, enquanto o resultado de sqrt (x 2 + 1) é no máximo x + 2-27 . Como o modo de arredondamento padrão IEEE-754 é o arredondamento para o mais próximo; empates para pares, sempre arredondará para x e nunca para x + 2-26 (onde o desempate é realmente relevante apenas para x = 67.108.864, se houver. Qualquer número maior será arredondado para x independentemente).
C ++, 75.000.000+
Lembre-se de que 3 2 + 4 2 = 5 2 . O que isto significa é que o ponto (4, 3) se encontra no círculo do raio 5, centralizado em torno da origem. Na verdade, para todos os números inteiros n , (4n, 3n) se encontra esse círculo de raio 5n . Para n grande o suficiente (ou seja, tal que 5n> = 2 26 ), já sabemos um falso positivo para todos os pontos deste círculo: (5n, 1) . Ótimo! São outros 27.797.402 / 5 pares de falsos positivos gratuitos aqui! Mas por que parar aqui? (3, 4, 5) não é o único desses trigêmeos.
Este programa procura todos os trigêmeos inteiros positivos (a, b, c) de modo que a 2 + b 2 = c 2 e conte falso-positivos dessa maneira. Chega a 70.000.000 de falsos positivos bastante rápido, mas diminui consideravelmente à medida que os números aumentam.
Compile com
g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Para verificar os resultados, adicione-DVERIFY
(isso será notavelmente mais lento).Corra com
flspos
. Qualquer argumento da linha de comandos para o modo detalhado.fonte
2**53
limite foi escolhido para descartar isso, mas acho que não.C ++ 11 - 100.993.667
EDIT: Novo programa.
O antigo usava muita memória. Este reduz pela metade o uso de memória usando uma matriz de vetores gigante em vez de uma tabela de hash. Também remove a crosta aleatória do encadeamento.
Execute um
-P
argumento para imprimir os pontos em vez do número.Para mim, leva menos de 2 minutos no modo de contagem e cerca de 5 minutos com a impressão direcionada para um arquivo (~ 4 GB), para que não se torne limitado à E / S.
Meu programa original foi arrumado, mas perdi a maior parte dele, pois só podia produzir na ordem de 10 ^ 5 resultados. O que ele fez foi procurar parametrizações da forma (x ^ 2 + Ax + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) para que, para qualquer A soma de dois algarismos distintos é igual a: (x ^ 2 + Ax + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Quando encontrou esse conjunto de parâmetros {a, b, c, d, A, B, C, D}, passou a verificar todos os valores de x abaixo do máximo. Enquanto olhava para minha saída de depuração deste programa, notei uma certa parametrização da parametrização da parametrização que me permitiu produzir muitos números facilmente. Optei por não imprimir os números de Ell, já que eu tinha muitos. Espero que agora alguém não imprima ambos os nossos conjuntos de números e afirme ser o vencedor :)
fonte
g++ (GCC) 4.8.1
. Ok, eu removi os bits do thread, mas ainda não o reconhecistricmp
por algum motivo.Varredura de círculo em Java, estilo Bresenham
Heuristicamente, espero obter mais colisões começando no final mais amplo do espaço anular. Eu esperava obter alguma melhoria fazendo uma varredura para cada colisão, registrando valores
surplus
entre0
er2max - r2
inclusivos, mas em meus testes isso se mostrou mais lento que esta versão. Da mesma forma, tenta usar um únicoint[]
buffer em vez de criar muitas listas e matrizes de dois elementos. A otimização de desempenho é realmente uma besta estranha.Execute com um argumento de linha de comando para saída dos pares e sem contagens simples.
fonte
Java - 27.817.255
A maioria é igual ao que Ell mostra , e o restante é baseado
(j,0) (k,l)
. Para cada umaj
, eu recuo alguns quadrados e verifico se o restante dá um falso positivo. Isso ocupa basicamente o tempo todo com apenas um ganho de 25k (cerca de 0,1%)(j,0) (j,1)
, mas um ganho é um ganho.Isso terminará em menos de dez minutos na minha máquina, mas não sei o que você tem. Por razões, se não terminar antes que o tempo se esgote, terá uma pontuação drasticamente pior. Nesse caso, você pode ajustar o divisor na linha 8 para que ele termine no tempo (isso simplesmente determina a que distância ele caminha para cada um
j
). Para alguns vários divisores, as pontuações são:Para ativar a saída para cada correspondência (e, Deus, é lento se você o fizer), apenas descomente as linhas 10 e 19.
Para referência, as primeiras 20 saídas fornecidas (para o divisor = 7, excluindo
(j,0)(j,1)
tipos) são:fonte
Julia, 530 falsos positivos
Aqui está uma pesquisa de força bruta muito ingênua, que você pode ver como uma implementação de referência.
Você pode imprimir os pares (e suas magnitudes quadradas exatas) descomentando a
@printf
linha.Basicamente, isso inicia a busca no
x = y = 6e7
primeiro par de coordenadas e escaneia cerca de 1% do caminho até o eixo x antes de decrementar x. Em seguida, para cada par de coordenadas, ele verifica o arco inteiro da mesma magnitude (arredondando para cima e para baixo) em busca de uma colisão.O código assume que ele é executado em um sistema de 64 bits, para que os tipos inteiros padrão e de ponto flutuante são aquelas de 64 bits (se não, você pode criá-los com
int64()
efloat64()
construtores).Isso produz apenas 530 resultados.
fonte