Um número inteiro positivo \(n\) é primo se for divisível apenas por \(1\) e por \(n\):
2, 3, 5, 7, 11, 13 …
Vamos especificar um algoritmo para testar se um número é primo.
Dado: \(n\) inteiro.
Se \(n\leq 1\) então não é primo e terminamos imediatamente.
Se \(n>1\) tentamos para \(d = 2, 3, \ldots, n-1\):
Se chegamos ao final sem encontrar um divisor: concluimos que \(n\) é primo.
#define FALSE 0
#define TRUE 1
/* Testar se um número inteiro é primo */
int primo(int n) {
int d;
if(n <= 1) return FALSE;
for (d = 2; d < n; d++) {
if (n%d == 0) // d divide n?
return FALSE;
}
return TRUE;
}
/* Testar se um número é primo;
versão mais eficiente */
int primo(int n)
{
int d;
if(n <= 1) return FALSE;
for (d = 2; d*d <= n; d++) {
if (n%d == 0) // d divide n
return FALSE;
}
return TRUE;
}
O máximo divisor comum (mdc) de dois inteiros \(a, b\) é o maior número inteiro que divide \(a\) e \(b\).
Exemplo:
\[ \begin{eqnarray} 252 &=& 21 \times 12 \\ 105 &=& 21 \times 5 \end{eqnarray} \]
Dados: \(a,\, b\) dois números inteiros positivos.
Calcular o mdc de 252 e 105.
iter | a | b |
---|---|---|
0 | 252 | 105 |
1 | 147 | 105 |
2 | 42 | 105 |
3 | 42 | 63 |
4 | 42 | 21 |
5 | 21 | 21 |
R: 21
/* Calcular o mdc de dois inteiros positivos
pelo Algoritmo de Euclides (1ª versão)
*/
int mdc(int a, int b)
{
while (a != b) {
if(a > b)
= a - b;
a else
= b - a;
b }
return a; // a, b são iguais
}
OK:
Questões:
Propriedade da divisão inteira:
Se \(d\) divide \(a\) e \(b\), então \(d\) divide \(a-b\) e \(d\) divide \(b-a\).
Em cada iteração:
\[ \begin{aligned} \text{se}~a>b: \quad & (a,b) \longrightarrow (a-b, b) \\ \text{se}~a<b: \quad & (a,b) \longrightarrow (a, b-a) \end{aligned} \]
(Versão usando resto da divisão.)
Dados: \(a,\, b\) dois inteiros não-negativos.
Calcular o mdc de 252 e 105.
iter | a | b | resto a ÷ b |
---|---|---|---|
0 | 252 | 105 | 42 |
1 | 105 | 42 | 21 |
2 | 42 | 21 | 0 |
3 | 21 | 0 |
R: 21
/* Calcular o mdc de dois inteiros usando
o algoritmo de Euclides (2ª versão)
*/
int mdc(int a, int b)
{
int r;
while(b != 0) {
= a%b;
r = b;
a = r;
b }
return a;
}
\[ \begin{eqnarray*} \text{fact}(0) &=& 1\\ \text{fact}(n) &=& n \times \text{fact}(n-1)\,, \quad \text{se}~n>0 \end{eqnarray*} \]
A definição anterior define um algoritmo: permite calcular o factorial de qualquer inteiro não negativo.
Exemplo:
\[ \begin{eqnarray*} \text{fact}(4) &=& 4 \times \text{fact}(3) \\ &=& 4 \times (3 \times \text{fact}(2))\\ &=& 4 \times (3 \times (2\times \text{fact}(1)))\\ &=& 4 \times (3 \times (2\times (1\times \text{fact}(0)))\\ &=& 4 \times (3 \times (2\times (1\times 1))) \\ &=& 24 \end{eqnarray*}\]
Podemos implementar este processo defindo a função recursiva em C:
int fact(int n)
{
if (n == 0)
return 1; // caso base
else
return n * fact(n-1); // caso recursivo
}
Há dois casos na definição anterior:
o factorial de zero é 1 (sem mais chamadas recursivas)
calculamos o factorial do natural anterior e multiplicamos o resultado por \(n\)
Para que uma definição recursiva termine é suficiente que:
Exemplo: na função de fact
n == 0
n-1
n
)Logo: fact
termina para qualquer n
maior ou
igual a 0.
int fact(int n) {
int r = 1; // resultado
for(int i = 1; i<=n; i++)
= r*i;
r return r;
}
int
e float
int
têm
sinal: podem ser negativos, positivos ou zerounsigned int
para inteiros sem sinal: apenas positivos ou zeroint i; // com sinal
unsigned int j; // sem sinal
unsigned
:unsigned j; // unsigned int
int
e unsigned int
são ambos representados por palavras de comprimento fixo
\[ \begin{array}{ccc} \text{mínimo} & 0 & (0000000000000000)_2 \\ & 1 & (0000000000000001)_2 \\ & 2 & (0000000000000010)_2 \\ & \vdots & \vdots \\ \text{máximo} & {2^{16} -1} & (1111111111111111)_2 \end{array} \]
\[ \begin{array}{ccc} \text{mínimo} & {-2^{15}} & (1000000000000000)_2 \\ & \vdots & \vdots \\ & -1 & (1111111111111111)_2 \\ & 0 & (0000000000000000)_2 \\ & +1 & (0000000000000001)_2 \\ & \vdots & \vdots \\ \text{máximo} & {+2^{15} -1} & (0111111111111111)_2 \end{array} \]
int
é tipicamente representado usando 32-bits (pode ser menor em
CPUs de 8 e 16-bits)long
e short
para
especificar tamanhos maiores ou menoresunsigned
temos 6
tipos diferentes de inteiros:short int unsigned short int
int unsigned int
long int unsigned long int
int
short
\(\leq\) int
\(\leq\) long
<limits.h>
define os limites de cada tipo#include <stdio.h>
#include <limits.h>
int main(void) {
("SHRT_MIN = %d\n", SHRT_MIN);
printf("SHRT_MAX = %d\n", SHRT_MAX);
printf
("INT_MIN = %d\n", INT_MIN);
printf("INT_MAX = %d\n", INT_MAX);
printf
("LONG_MIN = %ld\n", LONG_MIN);
printf("LONG_MAX = %ld\n", LONG_MAX);
printf// %ld para formatar long int
}
Execução no meu portátil (GNU/Linux X86-64):
$ ./tamanhos
SHRT_MIN = -32768
SHRT_MAX = 32767
INT_MIN = -2147483648
INT_MAX = 2147483647
LONG_MIN = -9223372036854775808
LONG_MAX = 9223372036854775807
int
long int
long
terminando com
L
ou l
e unsigned
com
U
ou u
:17 // int
-1000L // long int
2500UL // unsigned long int
long int i = 17; // 17 -> 17L
Para ler ou escrever inteiros short
, long
ou unsigned
devemos
usar formatos específicos em scanf
e
printf
.
Casos mais comuns:
"%u"
inteiro decimal unsigned
"%ld"
inteiro decimal long
"%lu"
inteiro decimal unsigned long
int
para as
dimensõesl = w = h = 1500
resulta em overflow (inteiros de 32-bits)unsigned
unsigned long
#include <stdio.h>
int main(void) {
unsigned l, w, h, v;
("L=?"); scanf("%u", &l);
printf("W=?"); scanf("%u", &w);
printf("H=?"); scanf("%u", &h);
printf= l*w*h; // cálculo do volume
v
("LxWxH: %u*%u*%u (cm)\n", l,w,h);
printf("Volume: %u (cm^3)\n", v);
printf}
Esta versão calcula o volume correto para
= w = h = 1500 l
mas obtemos de novo overflow para
= w = h = 2000 l
#include <stdio.h>
int main(void) {
unsigned long l, w, h, v;
("L=?"); scanf("%lu", &l);
printf("W=?"); scanf("%lu", &w);
printf("H=?"); scanf("%lu", &h);
printf= l*w*h; // cálculo do volume
v
("LxWxH: %lu*%lu*%lu (cm)\n", l,w,h);
printf("Volume: %lu (cm^3)\n", v);
printf}
l = w = h = 2000
l = w = h
para os quais não ocorre overflow (assumindo
unsigned long
com 64-bits)float
para
precisão simples;double
para precisão dupla.tipo | menor positivo | maior valor | precisão |
---|---|---|---|
float | \(\approx 1.17\times 10^{-38}\) | \(\approx 3.40\times 10^{38}\) | 6 algarismos |
double | \(\approx 2.22\times 10^{-308}\) | \(\approx 1.79\times 10^{308}\) | 15 algarismos |
double
é
usado para a maior parte das aplicaçõesfloat
é
usando apenas se a precisão for pouco importante ou para poupar
memória57.0 57. 57E0 5.7e1
E
(ou e
)5.7e1 |
\(5.7\times 10^1\) |
5.7E-3 |
\(5.7\times 10^{-3}\) |
"%lf"
para ler valores double
float
ou double
devemos usar
apenas "%f"
"%g"
para formatar usando
notação científica#include <stdio.h>
int main(void) {
double l, w, h, v;
("L=?"); scanf("%lf", &l);
printf("W=?"); scanf("%lf", &w);
printf("H=?"); scanf("%lf", &h);
printf= l*w*h; // cálculo do volume
v
("LxWxH: %.3f*%.3f*%.3f (cm)\n",
printf, w, h);
l("Volume: %.3g (cm^3)\n", v);
printf}
Conversão explicita de tipos (“cast”):
(int) expr |
converter para inteiro |
(float) expr |
converter para vírgula flutuante |
(tipo) expr |
forma geral |
int k = 2, n = 3;
("%f\n", (float)k/(float)n); // 0.66666
printf("%f\n", (float)k/n); // 0.66666
printf("%f\n", (float)(k/n)); // 0.00000 printf
Também podemos converter entre tamanhos:
int i = 1500;
long j;
= (long)i; j
Devemos efetuar conversões antes de operações que possam causar overflow:
int i = 1500;
long j;
= (long)(i*i*i); // "overflow"(?)
j = (long)i*i*i; // OK j