Propriedades dos números
Autor(es): Pedro Henrique Paiola, Rene Pegoraro, Wilson M Yonezawa, Arissa Yoshida, Nicolas Barbosa Gomes, Luis Henrique Morelli
Certos problemas da Maratona de Programação recebem como entrada números inteiros que extrapolam o limite de variáveis do tipo long long int
Tamanho de uma variável long long int: 8 bytes
Intervalo de números que podem ser armazenados em uma variável desse tipo:
-9.223.372.036.854.775.808 à 9.223.372.036.854.775.807
0 à 18.446.744.073.709.551.615 (unsigned long long int)
Certos problemas da Maratona de Programação recebem como entrada números inteiros que extrapolam o limite de variáveis do tipo long long int
Exemplo: 2667 - Jogo de Boca
1ª Situação: dependendo das operações necessárias de se fazer com o número, podemos ler o número como sendo uma string e trabalhar com essa string.
Exemplos:
2ª Situação: se precisarmos fazer operações com esse número como soma, subtração, multiplicação e divisão, o problema se torna mais complexo.
Nesses casos, não recomendamos usar a linguagem cpp. É possível trabalhar com BigInteger em cpp (a biblioteca do Thiago traz códigos para isso), porém a quantidade de código necessária é relativamente grande.
Sugestões: Java ou Python
Em Java podemos usar a classe BigInteger da biblioteca java.math
String Num;
BigInteger NumGrande;
Scanner S = new Scanner(System.in);
Num = S.nextLine();
NumGrande = new BigInteger(Num);
NumGrande = NumGrande.mod(new BigInteger("3"));
System.out.println(NumGrande);
Em Python, não precisamos nos preocupar muito com o tamanho de um inteiro, a memória é alocada conforme o necessário para comportar o tamanho do número.
Em Python, não precisamos nos preocupar muito com o tamanho de um inteiro, a memória é alocada conforme o necessário para comportar o tamanho do número.
U = int(input())
print(U % 3)
Diversos problemas envolvem o uso de números primos.
Dessa forma, precisamos, inicialmente, de uma forma de testar se um número é primo ou não.
Recordando: números primos são números naturais que têm apenas dois divisores: 1 e ele mesmo.
Algoritmo ingênuo 𝑂(𝑛)
bool ehPrimo(int n)
{
for(int i = 2; i < n; i++)
if (n % i == 0)
return false;
return true;
}
Porém, na verdade só precisamos testar até 𝒏
Demonstração:
Algoritmo 𝑂( sqrt(𝑛))
bool ehPrimo(int n)
{
for(int i = 2; i*i <= n; i++)
if (n % i == 0)
return false;
return true;
}
O Crivo de Eratóstenes é um método de encontrar os números primos até um certo valor limite.
Útil em casos que faremos vários testes de primalidade e na fatoração de números.
Ideia geral: dado que um número 𝒑 é primo, marcamos os múltiplos de 𝒑 como não sendo números primos.
Algoritmo:
Cria-se uma lista de 2 a MAX, marcando todos como primos
Para cada número i de 2 até sqrt(MAX)
Se i está marcado como primo
Marcar todos os números múltiplos de i a partir de i . i
como compostos (não primos)
bool ehPrimo[MAX];
vector<int> primos;
void crivo(int n){
memset(ehPrimo, true, sizeof(ehPrimo));
for(int p = 2; p * p <= n; p++){
if (ehPrimo[p]){
primos.push_back(p); //Lista incompleta, primos até sqrt(n)
for(int i = p*p; i <= n; i += p)
ehPrimo[i] = false;
}
}
}
vector<int> fatorar(int n) {
vector<int> fator;
for (int i = 2; i*i <= n; i++){
while (n % i == 0){
fator.push_back(i);
n /= i;
}
}
if (n > 1)
fator.push_back(n);
return fator;
}
Também é possível obter um algoritmo de fatoração com complexidade 𝑂(log 𝑛), baseando-se no Crivo de Eratóstenes.
Primeiramente, ao invés de utilizarmos o crivo para descobrirmos todos os primos, faremos uma pequena alteração para computar para cada número o seu Menor Fator Primo (Shortest Prime Factor - SPF).
Crivo para Fatoração
int spf[MAXN];
void crivo(){
for(int i=2; i < MAXN; i++){
if(spf[i] == 0){
spf[i] = i;
for(int j=i*i; j<MAXN; j+=i){
if(spf[j] == 0) spf[j] = i;
}
}
}
}
fatores = []
enquanto n > 1
Inserir spf[n] em fatores
n = n/spf[n]
vector<int> fatorar(int n){
vector<int> fator;
while(n > 1){
fator.push_back(spf[n]);
n /= spf[n];
}
return fator;
}
int primos[] = {2, 3, 5, 7, 11, 13, … }
“The judge can’t look into your heart or your program to see your intentions - it only checks the results.” (Skiena & Revilla, 2003; p. 129)
int gcd(int a, int b){
if (a == 0)
return b;
return gcd(b % a, a);
}
𝑚𝑚𝑐(𝑥, 𝑦) ∗ 𝑚𝑑𝑐(𝑥, 𝑦) = 𝑥 ∗ 𝑦
Ou seja:
𝑚𝑚𝑐(𝑥, 𝑦) = 𝑥 ∗ 𝑦 / 𝑚𝑑𝑐(𝑥, 𝑦)
int lcm(int a, int b){
return a * (b / gcd(a, b));
}
𝒂𝟏𝒙𝟏 + 𝒂𝟐𝒙𝟐 + ⋯ + 𝒂𝒏 𝒙𝒏 = 𝒄
𝒂𝒙 + 𝒃𝒚 = 𝒄
𝑡 = 𝑡0 + 𝑘. 𝑖
==>
𝑐 = 𝑎𝑥0 + 𝑏𝑦0 = 𝐴𝑑 𝑥0 + 𝐵𝑑 𝑦0
𝑐 = 𝑑(𝐴𝑥0 + 𝐵𝑦0)
Denotando 𝑞 = 𝐴𝑥0 + 𝐵𝑦0
𝑐 = 𝑑𝑞 Portanto, 𝑑|𝑐
<==
•- Por hipótese, 𝑑|𝑐 ⇒ ∃𝑡 / 𝑐 = 𝑑𝑡
𝑐 = 𝑑𝑡
𝑐 = (𝑎𝑥0 + 𝑏𝑦0)𝑡
𝑐 = 𝑎(𝑥0𝑡) + 𝑏(𝑦0𝑡)
Portanto, se 𝑑 | 𝑐, então a equação 𝑎𝑥 + 𝑏𝑦 = 𝑐 admite solução
Como determinar uma solução?
Solução base: (0, 1)
Solução para 𝑎𝑥 + 𝑏𝑦 = gcd(𝑎, 𝑏)
Temos 𝑎𝑥 + 𝑏𝑦 = gcd(𝑎, 𝑏)
Pelo Algoritmo de Euclides, sabemos que gcd 𝑎, 𝑏 = gcd 𝑏%𝑎, 𝑎 = 𝑑
Logo, podemos obter outra equação diofantina: 𝑏%𝑎 𝑥1 + 𝑎𝑦1 = 𝑑 (∗)
Solução para 𝑎𝑥 + 𝑏𝑦 = gcd(𝑎, 𝑏)
Considerando o resultado da divisão inteira, podemos dizer que:
𝑏 = 𝑏
𝑎 𝑎 + 𝑏%𝑎
𝑏%𝑎 = 𝑏 − 𝑏
𝑎 𝑎
𝑏 − 𝑏
𝑎 𝑎 𝑥1 + 𝑎𝑦1 = 𝑑
𝑏𝑥1 − 𝑏
𝑎 𝑎𝑥1 + 𝑎𝑦1 = 𝑑
𝑎 𝑦1 − 𝑏
𝑎 𝑥1 + 𝑏𝑥1 = 𝑑
𝒂𝒙 + 𝒃𝒚 = 𝒅
int gcd(int a, int b, int &x, int &y){
if (a == 0){
x = 0;
y = 1;
return b;
}
int x1, y1;
int d = gcd(a, b % a, x1, y1);
x = y1 - x1 * (b/a);
y = x1;
return d;
}
bool solve(int a, int b, int c, int &x0, int &y0, int &g) {
g = gcd(abs(a), abs(b), x0, y0);
if (c % g) {
return false;
}
x0 *= c / g;
y0 *= c / g;
if (a < 0) x0 = -x0;
if (b < 0) y0 = -y0;
return true;
}
Em vários problemas precisamos operar com os restos de divisões de inteiros.
A aritmética modular permite fazer cálculos com restos de divisões de modo eficiente, e é especialmente útil quando estamos trabalhando com números grandes (BigInteger).
Na verdade, a Aritmética Modular pode nos ajudar a evitar ter que trabalhar com números muito grandes.
A aritmética modular se baseia nas seguintes propriedades:
(𝑥 + 𝑦) % 𝑛 = ((𝑥 % 𝑛) + (𝑦 % 𝑛)) % 𝑛
(𝑥 − 𝑦) % 𝑛 = ((𝑥 % 𝑛) − (𝑦 % 𝑛)) % 𝑛
(𝑥 ∗ 𝑦) % 𝑛 = ((𝑥 % 𝑛) ∗ (𝑦 % 𝑛)) % 𝑛
(𝑥 ^ 𝑦) % 𝑛 = ((𝑥 % 𝑛) ^ 𝑦) % 𝑛
UVa 374 - Big Mod
Calcule 𝑅 = 𝐵𝑃 𝑚𝑜𝑑 𝑀
0 ≤ 𝐵, 𝑃 ≤ 2147483647 e 1 ≤ 𝑀 ≤ 46340
Parte da solução do problema UVA 374 – Big Mod
long long pow(long long x, long long y, long long mod) {
if (y == 0)
return 1;
long long p = pow(x, y/2, mod);
if (y % 2 == 0)
return (p * p) % mod;
else
return (((p * p) % mod) * (x % mod)) % mod;
}
𝐴
𝑋 ∗ 𝐴−1 = 𝑋 ∗ 1
𝐴 = 𝑋
𝐴
O inverso modular de 𝐴 (𝑚𝑜𝑑 𝐶) é 𝐴−1.
𝐴 ∗ 𝐴−1 ≡ 1 (𝑚𝑜𝑑 𝐶) ou de modo equivalente 𝐴 ∗ 𝐴−1 𝑚𝑜𝑑 𝐶 = 1
OBS: Apenas os números coprimos de C têm um inverso modular (mod C)o
Exemplo: A=3 e C=7
3 ∗ 5 𝑚𝑜𝑑 7 = 15 𝑚𝑜𝑑 7 = 1
∴ 3 ∗ 5 ≡ 1 (𝑚𝑜𝑑 7)
Logo, 5 é o inverso modular de 3 (mod 7) .
Como encontrar um inverso multiplicativo?
Determinar um 𝑥 ∈ ℤ tal que 𝐴𝑥 ≡ 1 𝑚𝑜𝑑 𝐶 ⇒ 𝑥 = 𝐴−1
Da congruência, temos que
C | (𝐴𝑥 − 1)
Logo, ∃y ∈ ℤ| 𝐴𝑥 − 1 = 𝐶𝑦
𝐴𝑥 − 𝐶𝑦 = 1
Biblioteca de códigos de Thiago Alexandre Domingues de Souza.
Matemática Discreta e Suas Aplicações. Kenneth H. Rosen.
Programming Challenges: The Programming Contest Training Manual. Stevem S. Skiena e
Miguel A. Revilla.
https://www.geeksforgeeks.org/sieve-of-eratosthenes/
http://www.lcad.icmc.usp.br/~jbatista/scc210/AulaTeoriadosNumeros1.pdf
http://www.lcad.icmc.usp.br/~jbatista/scc210/AulaTeoriadosNumeros2.pdf
https://www.ufsj.edu.br/portal2-repositorio/File/comat/tcc_Ricardo.pdf
https://cp-algorithms.com/algebra/linear-diophantine-equation.html
https://noic.com.br/materiais-informatica/curso/math-02/
https://noic.com.br/materiais-informatica/curso/math-03/
https://pt.khanacademy.org/computing/computer-
science/cryptography/modarithmetic/pi/fast-modular-exponentiation
https://www.cin.ufpe.br/~gdcc/matdis/aulas/aritmeticaModular_parte2.pdf