Na linha de grandes desafios, pensei que este poderia ser interessante.
Neste desafio, usaremos o sistema de número de resíduos (RNS) para executar adição, subtração e multiplicação em números inteiros grandes.
O que é o RNS
O RNS é uma das muitas maneiras que as pessoas desenvolveram para identificar números inteiros. Neste sistema, os números são representados por uma sequência de resíduos (que são os resultados após uma operação de módulo (ou seja, o restante após a divisão inteira)). Neste sistema, cada número inteiro tem muitas representações. Para manter as coisas simples, vamos limitar as coisas para que cada número inteiro seja representado exclusivamente. Eu acho que é mais fácil descrever o que está acontecendo com um exemplo concreto.
Vejamos os três primeiros números primos: 2, 3, 5. No sistema RNS, podemos usar esses três números para representar exclusivamente qualquer número que seja menor que 2 * 3 * 5 = 30 usando resíduos. Tome 21:
21 é menor que 30, então podemos representá-lo usando os resultados após a modificação de 2, 3 e 5. (ou seja, o restante após o número inteiro dividido por 2, 3 e 5)
Identificamos 21 com a seguinte sequência de números inteiros:
21 ~ {21 mod 2, 21 mod 3, 21 mod 5} = {1, 0, 1}
E assim, em nosso sistema RNS, em vez de "21", usaríamos {1,0,1}.
Em geral, dado um número inteiro n , representamos n como { n mod 2, ..., n mod p_k }, em que p_k é o menor primo, de modo que n é menor que o produto de todos os números primos menores ou iguais a p_k .
Outro exemplo, digamos que temos 3412. Precisamos usar 2,3,5,7,11,13 aqui porque 2*3*5*7*11*13=30030
considerando 2*3*5*7*11=2310
que é muito pequeno.
3412 ~ {3412 mod 2, 3412 mod 3, 3412, mod 5, ..., 3412 mod 13} = {0, 1, 2, 3, 2, 6}
Você percebe que, usando esse sistema, podemos representar números muito grandes de maneira relativamente indolor. Usando resíduos {1, 2, 3, 4, 5, 6, 7, 8, ...}, podemos representar números até {2, 6, 30, 210, 2310, 30030, 510510, 9699690 ...} respectivamente. ( Aqui está a série )
Nossa tarefa
Usaremos esses resíduos para executar +, - e * em grandes números. Vou descrever esses processos abaixo. Por enquanto, aqui estão as especificações de entrada e saída.
Entrada
Você receberá dois números (potencialmente muito grandes) por meio de um argumento stdin ou função. Eles serão dados como cadeias de caracteres de base 10.
Para fins de descrever o problema ainda mais, chamamos a primeira entrada n
e a segunda m
. Suponha que n> m> = 0 .
Você também receberá +
ou -
ou *
para indicar a operação a ser executada.
Saída
Seja x um número inteiro. Usaremos [ x ] para nos referir à representação RNS descrita acima de x .
Você deve produzir [n] <operator> [m] = [result]
Como executar as operações no RNS
Essas operações são relativamente simples. Dados dois números na notação RNS, para adicioná-los, subtraí-los ou multiplicá-los, simplesmente execute as operações especificadas em componentes e, em seguida, faça o módulo.
ie
{1, 2, 3} + {1, 1, 4} = {(1 + 1) mod 2, (2 + 1) mod 3, (3 + 4) mod 5} = {0, 0, 2}
Observe que, se o número de resíduos usado para representar dois números diferentes não for o mesmo, ao executar operações, será necessário estender o número "mais curto" para que ele tenha o mesmo número de resíduos. Isso segue o mesmo processo. Veja os casos de teste para um exemplo.
O mesmo acontece se o resultado exigir mais resíduos do que qualquer entrada. Então ambas as entradas precisam ser "estendidas".
Detalhes importantes
Lidaremos com grandes números aqui, mas não arbitrariamente grandes. Seremos responsáveis pelos números até o produto dos 100 primeiros números primos (veja abaixo). Para esse fim, você recebe os primeiros 100 números primos gratuitamente (sem custo de bytes) . Você pode colocá-los em uma matriz chamada
p
ou algo idiomático no seu idioma e subtrair o número de bytes usados para iniciar essa matriz do total final. Obviamente, isso significa que eles podem ser codificados ou você pode usar um built-in para gerá-los.Se, por algum motivo, essa for a representação inteira padrão usada no seu idioma. Está bem.
Você não pode usar nenhum tipo de número inteiro de precisão arbitrária, a menos que seja o padrão do seu idioma. Se for o padrão, você não pode usá-lo para armazenar números inteiros que normalmente não cabem em 64 bits.
Para ficar claro, cada número inteiro sempre será representado com o menor número de resíduos possível. Isso vale para entrada e saída.
Eu acho que as outras especificações devem evitar isso, mas para ser redundante: você pode não executar a operação especificada nas entradas e depois alterar tudo para RNS e, em seguida, saída. Você deve alterar as entradas para RNS e, em seguida, executar as operações para produzir a saída.
Casos de teste
Entrada:
n = 10
m = 4
+
Saída:
{ 0, 1, 0 } + { 0, 1 } = { 0, 2, 4 }
Explicação:
Primeiro, altere cada número para sua representação RNS, conforme descrito acima:
10 ~ {0,1,0}
e 4 ~ {0,1}
. Observe que quando queremos fazer a adição de componentes, isso 10
tem mais componentes que 4
. Portanto, devemos "estender" o número mais curto. Então, escreveremos brevemente 4 ~ {0,1} --> {0,1, 4 mod 5} = {0,1,4}
. Agora prosseguimos com a adição e, em seguida, tomamos o módulo.
- Entrada
n=28
m=18
+
Saída:
[ 0, 1, 3 ] + [0, 0, 3 ] = [ 0, 1, 1, 4 ]
- Entrada (eu esmagando meu rosto no teclado)
n=1231725471982371298419823012819231982571923
m=1288488183
*
Saída (dividida em linhas separadas para facilitar a leitura):
[1, 2, 3, 6, 2, 10, 2, 1, 12, 16, 7, 15, 34, 29, 31, 5, 55, 32, 66, 61, 3, 76, 52, 14, 65, 44, 99, 57 ]
*
[1, 0, 3, 3, 4, 8, 9, 10, 8, 0 ]
=
[1, 0, 4, 4, 8, 2, 1, 10, 4, 0, 17, 7, 27, 21, 44, 51, 56, 9, 6, 9, 12, 0, 52, 36, 43, 68, 99, 24, 96, 39, 96, 66, 125]
n
requer 28 números primos. m
requer 10. n*m
requer 33.
- Entrada
n=8709668761379269784034173446876636639594408083936553641753483991897255703964943107588335040121154680170867105541177741204814011615930342030904704147856733048115934632145172739949220591246493529224396454328521288726490
m=1699412683745170450115957274739962577420086093042490863793456500767137147999161679589295549397604032154933975242548831536518655879433595016
-
Saída:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 509]
-
[0, 2, 1, 6, 1, 12, 11, 18, 14, 28, 21, 36, 37, 42, 16, 52, 41, 60, 16, 70, 49, 78, 80, 88, 49, 100, 13, 106, 4, 112, 68, 130, 36, 138, 37, 150, 0, 162, 8, 172, 163, 180, 18, 192, 129, 198, 135, 222, 78, 228, 90, 238, 57, 250, 36, 262, 87, 270, 206, 280, 193, 292, 253, 310, 224, 316, 57, 336, 48, 348]
=
[0, 1, 4, 1, 10, 1, 6, 1, 9, 1, 10, 1, 4, 1, 31, 1, 18, 1, 51, 1, 24, 1, 3, 1, 48, 1, 90, 1, 105, 1, 59, 1, 101, 1, 112, 1, 0, 1, 159, 1, 16, 1, 173, 1, 68, 1, 76, 1, 149, 1, 143, 1, 184, 1, 221, 1, 182, 1, 71, 1, 90, 1, 54, 1, 89, 1, 274, 1, 299, 1, 266, 1, 228, 1, 340, 1, 170, 1, 107, 1, 340, 1, 88, 1, 157, 1, 143, 1, 22, 1, 22, 1, 58, 1, 296, 1, 371, 1, 140]
n
usa 100 números primos. m
usa 70 números primos. n-m
usa 99 números primos.
Eu os verifiquei usando a ChineseRem
implementação interna do teorema chinês Remainder no GAP (que basicamente pega números RNS e os altera para a base 10 inteiros). Eu acredito que eles estão corretos. Se algo parecer suspeito, entre em contato.
Para quem se importa, o produto dos 100 primeiros números primos é:
471193079990618495316248783476026042202057477340967552018863483961641533584503
422120528925670554468197243910409777715799180438028421831503871944494399049257
9030720635990538452312528339864352999310398481791730017201031090
Esse número é 1 maior que o número máximo que podemos representar usando o sistema fornecido (e a limitação de 100 primos).
(a,b,o)=>a.map((v,i)=>eval(v+o+b[i]))
no ES6, por exemplo. Acho que a parte mais difícil é provavelmente encontrar o número de números primos necessários para representar o resultado sem usar aritmética de precisão arbitrária, embora a conversão subsequente ao RNS não seja exatamente trivial.
1234,1234,+
)?