Física Computacional A
DFAT/
FiscompFA
Sistemas Lineares
Professor: Anibal Leonardo Pereira
última atualização: março 2011
Estagiários:
2004/1 a 2005/2
Luciana Conceição Iecker Lima
2010/1
Magali dos Santos Leodato
2009/1 a 2010/2
Filipe da Fonseca Cordovil
Monitores:
2001/1
Diego Chagas Garcia
2002/2
Erick Azevedo Meirelles
2003/1 a 2003/2
Luciana Maria dos Santos Azevedo
2003/1 a 2003/2
Tatiana Gonçalves Martins
2003/1 a 2005/2
Renato Nascente Júnior
2004/1 a 2005/2
Públio Martins Romano M. Carreiro
2006/1 a 2007/2
Luiz Fernando Rosalba Telles Souza
2006/1 a 2007/2
Paulo Henrique Pfitzner
2008/1 a 2008/2
Filipe da Fonseca Cordovil
2008/1 a 2009/2
Magali dos Santos Leodato
2011/1 a
Filipe da Fonseca Cordovil
1. Sistemas Lineares
Problemas de interesses práticos em Física e Engenharia muitas vezes acabam levando a um conjunto de
equações lineares
(sistema de equações lineares). De posse do sistema linear, a solução do problema que está sendo
tratado é obtida resolvendo-se o sistema de equação. Por exemplo, considere o circuito elétrico mostrado a seguir:
deseja-se calcular as voltagens nos nós 1, 2, 3 e 4. Para isto procede-se assim:
Lei de Kirchhoff (lei dos nós)
Em qualquer nó, a soma das correntes que entram (
apontam para o nó) é igual
à soma das correntes que partem dele:
∑
n entrami
n=
∑
p saem
i
pNó é um ponto onde elementos são ligados
Admitindo que as correntes, em cada nó, estejam entrando, pode-se escrever as seguintes
equações:
nó 1
:
i
a1
i
21
i
41=
0
nó 2
:
i
12
i
32=
0
nó 3
:
i
23
i
43=
0
Usando a lei de Ohm e substituindo nas equações dos nós obtidas anteriormente, tem-se:
nó 1:
100
−
V
1R
1
V
2−
V
1R
2
V
4−
V
1R
5=
0
nó 2:
V
1−
V
2R
2
V
3−
V
2R
3=
0
nó 3:
V
2−
V
3R
3
V
4−
V
3R
4=
0
nó 4:
V
1−
V
4R
5
V
3−
V
4R
4
b
−
V
4R
6=
0
Se as resistências têm valores
R
1=
10
, R
2=
2
, R
3=
4
, R
4=
6
, R
5=
8
, R
6=
10
chega-se ao
seguinte sistema de equações:
−
2940
V1 +
1
2
V2 +
1
8
V4 = −10
12
V1 -
6
8
V2 +
1
4
V3 = 0
14
V2 -
10
24
V3 -
1
6
V4 = 0
18
V1 +
1
6
V3 -
47
120
V4 = 0Observe que este sistema de equações é composto por 4 equações e 4 incógnitas (
V
1,
V
2,
V
3,
V
4). A
solução deste sistema de equações leva às voltagens em cada um dos nós.
De uma forma geral um sistema de 4 equações e 4 incógnitas pode ser escrito assim:
{
a
11x
1
a
12x
2
a
13x
3
a
14x
4=
b
1a
21x
1
a
22x
2
a
23x
3
a
24x
4=
b
2a
31x
1
a
32x
2
a
33x
3
a
34x
4=
b
3a
41x
1
a
42x
2
a
43x
3
a
44x
4=
b
4Sob a forma matricial, este sistema de equações pode ser escrito como
A
x
=
b
[
a
11a
12a
13a
14a
21a
22a
23a
24a
31a
32a
33a
34a
41a
42a
43a
44]
⋅
[
x
1x
2x
3x
4]
=
[
b
1b
2b
3b
4]
onde
A é a matriz quadrada de ordem n
(
no exemplo n=4, mas n pode ser qualquer valor, para o caso geral)
b
e
x
são matrizes n x 1 (
isto é, uma matriz com n linhas e uma coluna).
Os valores
a
ijsão chamados de coeficientes das incógnitas
x
jenquanto os
b
isão chamados de termos
independentes.
Uma forma bastante compacta de escrever as
n equações
com
n incógnitas
, é:
∑
j=1
n
a
ijx
j=
b
i,
i
=
1 , 2, 3 ,
⋯
,
n
A matriz A é chamada matriz dos coeficientes enquanto a matriz
B
=
[
a
11a
11...
a
1nb
1a
21a
22...
a
2nb
2...
...
... ...
...
a
n1a
n2...
a
nnb
n]
é chamada de
matriz aumentada
ou matriz estendida do sistema.
Os números
x
1,
x
2,
⋯
,
x
nconstituem uma solução do sistema de equação. Isto significa que ao substituir
estes números (
a solução) no sistema de equação, as equações se transformam em igualdades numéricas.
Os números
x
1,
x
2,
⋯
,
x
nformam uma matriz coluna
x
=
[
x
1
x
2⋮
x
n]
que é chamada de matriz solução do
sistema de equação.
2. Sistema Determinado e Sistema Singular
Um sistema de equações lineares (
sistema linear) é classificado como sendo:
•
determinado
quando apresenta solução única (
ou não singular)
•
indeterminado
se existem inúmeras soluções
•
impossível
ou
singular
se não existe solução
Para facilitar o entendimento do sistema linear:
considere o sistema linear:
{
20
x
1
10
x
2=
15
16
x
1
6
x
2=
11
ele é um sistema
determinado
pois tem solução única
x
1=
x
2=
0.5
.
Geometricamente, pode-se interpretar o sistema de
equações lineares como sendo retas no plano
x
10
x
2,
como mostrado na figura ao lado.
Sistema singular
:
Neste exemplo pode-se ver na figura que o sistema (
composto por duas retas) não possui solução (
não se cruzam), sendo portanto
um sistema linear
impossível
(
sem solução).
Sistema singular
:
Agora as duas equações são paralelas e estão colocadas uma
sobre a outra, por isto o sistema linear é um sistema
indeterminado
(
infinitas soluções).
Sistema Homogêneo
O sistema linear
{
a
11x
1
a
12x
2⋯
a
1nx
n=
b
1a
21x
1
a
22x
2⋯
a
2nx
n=
b
2⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
a
n1x
1
a
n2x
2⋯
a
nnx
n=
b
nque tem
b
i=
0,
i
=
1,2
,
⋯
, n
, isto é, a matriz
b
=
0
, é dito homogêneo.
Todo sistema homogêneo é determinado
, pois sempre admite a solução zero, isto é,
x
i=
0,
i
=
1,2
,
⋯
, n
ou seja, a matriz
x
=
0
.
Sistema Singular
Um sistema linear do tipo
{
a
11x
1
a
12x
2⋯
a
1nx
n=
b
1a
21x
1
a
22x
2⋯
a
2nx
n=
b
2⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯
a
n1x
1
a
n2x
2⋯
a
nnx
n=
b
né dito singular se o determinante da matriz A
é nulo
det A
=
0
.
Característica importante:
Sistemas indeterminados e impossíveis têm determinante nulo.
3. Sistemas Triangulares
Uma matriz é chamada de triangular quando os elementos acima ou abaixo da diagonal principal são zeros.
Veja o exemplo, considere o sistema linear:
{
3
x
1+
4
x
2-
5
x
3+
x
4=
−
10
x
2+
x
3- 2
x
4=
−
1
4
x
3-
5
x
4=
3
2
x
4=
2
na forma matricial
A
x
=
b
este sistema fica assim:
[
3 4
−
5
1
0 1
1
−
1
0 0
4
−
5
0 0
0
2
]
⋅
[
x
1x
2x
3x
4]
=
[
−
10
−
1
3
2
]
A solução deste sistema triangular superior é encontrada por
substituições retroativas
, então:
x
4=
1
4
x
3–
5
⋅
1
=
3
x
3=
2
x
2
2
–
2
⋅
1
=−
1
x
2=−
1
2
x
1
4
−
1
–
5
⋅
2
1
=−
10
x
1=
1
então a
solução
deste sistema linear é:
x
=[
1
−
1 2 1
]
T
x
=
[
1
−
1
2
1
]
. Portanto o sistema tem solução, o
sistema é determinado.
Analogamente os sistemas lineares que tem a
forma triangular inferior
podem ser resolvidas por
substituição
progressiva
. Veja o exemplo.
{
2
x
1=
1
8
x
1−
1
x
2=
5
−
5
x
1
3
x
2
2
x
3= 10
ou na forma matricial
[
2
0 0
8
−
1 0
−
5
3 2
]
⋅
[
x
1x
2x
3]
=
[
1
5
10
]
as
substituições progressiva
são:
x
1=
0.5
8
⋅
0.5
– x
2=
5
x
2=−
1
−
5
⋅
0.5
3
−
1
2
x
3=
10
x
3=
7.75
portanto a
solução
do sistema linear é:
x
=[
0.5
−
1 7.75
]
T4. Propriedades das Matrizes
•
matriz m x n
uma matriz pode ter um número qualquer de linhas e colunas.
O número de linhas não necessariamente deve ser igual ao número de colunas
m x n → (
lê-se matriz m por n) indica uma matriz com m linhas e n colunas
matriz quadrada → n x n
matriz linha
→ m x 1
matriz coluna
→ 1 x n
•
igualdade de matriz
duas matrizes são iguais se e somente se
•
tem a mesma forma
•
cada elemento correspondente nas duas matrizes são iguais
por exemplo, as matrizes A e B são iguais
A
=
[
2
0 0
8
−
1 0
−
5
3 2
]
e
B
=
[
2
0
0
2
×
4
−
1
0
−
5
3 8
÷
4
]
•
soma de duas matrizes
só é possível quando as matrizes têm a mesma forma
matriz cujos elementos é a soma dos dois elementos correspondentes nas matrizes originais
a matriz
C
=
[
6
0 9
8
1 7
3
−
3 1
]
pode ter sido obtida da adição de suas matrizes: C = A + B
[
6
0 9
8
1 7
3
−
3 1
]
=
[
2
0 15
7
2
5
2
−
5
1
]
[
4
0
−
6
1
−
1
2
1
2
0
]
•
diferença de duas matrizes
só é possível quando as matrizes têm a mesma forma
matriz cujos elementos é a diferença dos dois elementos correspondentes nas matrizes originais
exemplo: C = A - B
[
3
0 6
8
1 7
3
−
3 1
]
=
[
5
0 10
9
2
9
4
−
5
1
]
−
[
2
0 4
1
1 2
1
−
2 0
]
•
produto de número escalar por matriz
C
=
2
×
A
[
6
0 9
8
1 7
3
−
3 1
]
=
2
×
[
3
0
4.5
4
0.5 3.5
1.5
−
1.5 0.5
]
•
matriz identidade
matriz quadrada cujos elementos da diagonal principal são unitários e todos os outros elementos da matriz
são zero
I
=
[
•
transposta de uma matriz
a matriz
A
Tonde as linhas e colunas de
A
trocam de posição, então:
A
=
[
1 2 3
4
5 6
7 8 9
]
;
A
T=
[
1 4 7
2
5 8
3 6 9
]
•
produto de matriz
o produto de duas matrizes está definido se o número de colunas da primeira coluna é igual ao número de
linhas da segunda matriz:
matriz A com (m x
n)
matriz B com (
n
x p)
gerando uma matriz do tipo (
m x p
)
cada termo da matriz produto é definido por:
c
ij=
∑
k=1
n
a
ikb
ikexemplo: A x B = C
A
(2 x 3)
B
(3 x 2) →
C
(2 x 2)
[
1
2 0
3
−
4 1
]
×
[
1 2
−
1 1
0 1
]
=
[
−
1 4
7
3
]
observe como é feita a operação:
Importante:
5. Número Aleatório
O padrão Fortran permite o uso de dois esquemas diferentes para gerar números aleatórios.
Esquema com mesma semente
Este é o
esquema mais utilizado
pelos compiladores. Nele, o processador inicializa a semente do gerador de
número pseudo-aleatório sempre com o mesmo valor no início da execução. Isto produzirá a mesma sequência de
números aleatórios cada vez que o programa é executado.
Esquema com semente diferente
O segundo esquema permitido pelo padrão (
utilizado por poucos compiladores) é aquele em que o gerador de
número pseudo-aleatório é inicializado com uma semente diferente no início da cada execução, produzindo uma
sequência de números aleatórios diferentes cada vez que o programa é executado. Neste caso, se houver a
necessidade de se gerar a mesma sequência de números aleatórios é necessário guardar (
salvar) a semente
(
geralmente num arquivo de dados) para que ela possa ser reutilizada quando necessário.
O compilador
gfortran
, utilizado na disciplina, gera sempre a mesma semente para o gerador de número
aleatório. O programa
mesma_sequencia. Permite evidenciar isto.
program mesma_sequencia
!--- !Este programa gera sempre a mesma sequência de números aleatórios ! Arquivo: mesma_sequencia.f03
! AUTOR: Anibal L. Pereira 17/02/2011
!--- --- implicit none
real:: x integer::i print* do i=1,10
call random_number(x)
print"(a,i2,a,f5.3)"," i = ",i," --> ", x end do
end program mesma_sequencia
Execute algumas vezes o programa e veja as saídas !
Para gerar uma sequência diferente é necessário iniciar a semente do gerador de número aleatório com um valor
diferente. Uma forma prática de fazer isto é, usar o relógio do computador. Pegar-se o tempo que o relógio registra
para gerar a semente que inicia o gerador de número aleatório. O programa
sequencia_diferentemostra isto.
A sub-rotina intrínseca system_clock conta o tempo em segundos de um relógio de parede
desde a zero hora de primeiro de janeiro de 1970 (
00:00:00 UTC, january 1 1970).
O resultado é escrito num inteiro de precisão simples (
count).
Informações sobre “
Unix Time”
, para ajudar em um melhor entendimento da sub-rotina
system_clock, pode ser obtido em:
Declarações para gerar sequência de números aleatórios diferentes
program sequencia_diferente
!--- !Este programa gera uma sequência de números aleatórios diferente !cada vez que é executado
! Arquivo: sequencia_diferente.f03 ! AUTOR: Anibal L. Pereira 17/02/2011
!--- implicit none
real:: x integer::i
integer:: NRI, NRN, NRR !número aleatório – semente aleatória----+ integer, allocatable, dimension(:):: NRS ! !---+ !---número-aleatório --- gera semente aleatória ---+ call random_seed(size = NRN) ! allocate( NRS(NRN) ) ! call system_clock(count = NRR) ! NRS = NRR + 37 * [ (NRI - 1, NRI = 1, NRN) ] ! call random_seed(put = NRS) ! deallocate(NRS) ! !---+ !--- executa a sub-rotina com a semente aleatória
print* do i=1,10
call random_number(x)
print"(a,i2,a,f5.3)"," i = ",i," --> ", x end do
end program sequencia_diferente
Forma simplificada
program NR_forma_simples
!--- !Gera sequência de números aleatórios diferente quando é executado ! Arquivo: NR_forma_simples.f03
! AUTOR: Anibal L. Pereira 17/02/2011
!--- implicit none
real:: x integer::i
integer:: NRI, NRN, NRR !número aleatório – semente aleatória----+ integer, dimension(8):: NRS ! !---+ !---nova semente ---+ call system_clock(count = NRR) ! NRS = NRR + 37 * [ (NRI - 1, NRI = 1, 8) ] ! call random_seed(put = NRS) ! !---+ print*
do i=1,10
call random_number(x)
print"(a,i2,a,f5.3)"," i = ",i," --> ", x end do
Melhor forma
A forma mais conveniente de gerar sequências de números aleatórios diferentes em cada execução é utilizando
uma sub-rotina que gere as sementes diferentes. Exemplo:
program sequencia_diferente_com_modulo
!--- !Gera sequência de números aleatórios
!diferente
! Arquivo: sequencia_diferente_com_modulo.f03 ! AUTOR: Anibal L. Pereira 17/02/2011 !--- use m_numero_aleatorio
implicit none real:: x integer::i
call semente_diferente() print*
do i=1,10
call random_number(x)
print"(a,i2,a,f5.3)"," i = ",i," --> ", x end do
end program sequencia_diferente_com_modulo
module m_numero_aleatorio
!--- !Contém sub-rotina semente_diferente
! Arquivo: m_numero_aleatorio.f03
! AUTOR: Anibal L. Pereira 17/02/2011 !--- contains
subroutine semente_diferente() integer:: i, n, relogio
integer, allocatable, dimension(:):: semente
call random_seed(size = n) allocate(semente(n))
call system_clock(count = relogio) semente=relogio + 37 * [ (i-1, i=1,n) ] call random_seed(put = semente)
deallocate(semente)
end subroutine semente_diferente end module m_numero_aleatorio
Usando sub-rotina interna
program sequencia_diferente_sri
!--- !Gera sequência de números aleatórios diferente ! Arquivo: sequencia_diferente_sri.f03
! AUTOR: Anibal L. Pereira 17/02/2011
!--- implicit none
real:: x integer::i
call semente_diferente() print*
do i=1,10
call random_number(x)
print"(a,i2,a,f5.3)"," i = ",i," --> ", x end do
contains
!--- subroutine semente_diferente()
integer:: i, n, relogio
integer, dimension(8):: semente call random_seed(size = n)
call system_clock(count = relogio)
semente=relogio + 37 * [ (i - 1, i = 1, n) ] call random_seed(put = semente)
end subroutine semente_diferente
Números no Fortran, por default, são definidos e utilizados como sendo de precisão simples. Números reais
(
aqueles que possuem parte decimal) possuem uma representação interna diferente dos números inteiros, no
computador. Este conhecimento é muito importante e não pode ser esquecido ou desconsiderado.
Números do tipo inteiro
Qualquer número é representado internamente no computador na forma binária. Números binários só pode
conter dois valores (
0
ou
1
) em cada dígito utilizado. Veja a representação dos números 0 a 5 na forma binária:
binário decimal
00000000 0
00000001 1
00000010 2
00000011 3
00000100 4
00000101 5
O número binário foi escrito usando somente um
byte
(
octeto) [
para ser mais facilmente visualizado].
Um octeto possuí 8 posições (
8 bits) onde se pode escrever um dígito binário (
digito 0 ou digito 1), portanto é do
tipo:
00000000
Entretanto, quando o fabricante projeta uma CPU (
central processor unit) ele tem que tomar uma decisão de
quantos bits a sua CPU vai utilizar para trabalhar com os números. Esta decisão é importante porque ela afeta o
projeto e construção dos circuitos eletrônicos utilizados na CPU. Ao fazer esta escolha o tamanho da palavra do
computador (
da CPU) fica definido. O tamanho da palavra define quantos bits (
portanto quantos octetos – bytes) serão
utilizados na representação dos números (
4 octetos= 32 bits, 8 octetos = 64 bits, etc.).
Atualmente as CPU's modernas utilizam palavras de 8 octetos (
64 bits). Entretanto, como muitos dos softwares
ainda em uso foram escritos para computadores de palavra de 32 bits, mesmo os computadores modernos tendo
palavra de 64 bits trabalham como se tivessem palavra de 32 bits (
por causa do software utilizado). Tudo isto muda
quando se utiliza softwares escritos em 64 bits para serem utilizados com computadores com palavra de 64 bits.
Para os computadores que utilizam softwares de 32 bits ou foram construídos com palavra de 4 octetos (
32 bits)
os números são escritos internamente assim (
apresenta-se a palavra de 32 bits por que é visualmente mais agradável de ser vista):
00000000000000000000000000000000 (com 32 bits)
Por isto a representação dos primeiros 5 inteiros são escritos (
internamente ao computador) assim:
binário
decimal
00000000000000000000000000000000 0
00000000000000000000000000000001 1
00000000000000000000000000000010 2
00000000000000000000000000000011 3
00000000000000000000000000000100 4
00000000000000000000000000000101 5
Isto é o que se entende por precisão simples. Nos computadores de 32 bits, precisão simples significa números
inteiros representados internamente com 32 bits.
Ao utilizar 32 bits e esta forma de representação dos números pode-se utilizar números inteiros positivos que
vão de 0 a +4294967296.
00000000000000000000000000000000
zero
11111111111111111111111111111111
+4294967296
Por este motivo, a técnica utilizada para escrever números inteiros negativos é separar o primeiro bit para ser
utilizado como sinal. Isto resolve o problema, mas deixa somente 31 bits para serem utilizados no número, o que
faz com que menos números inteiros possam ser representados de -2.147.483.648 a +2.147.483.647. Com esta
técnica, os números inteiros são representados internamente assim:
s
0000000000000000000000000000000
números inteiros positivos
00000000000000000000000000000000
0 com o bit do sinal s = 0
00000000000000000000000000000001
+1
00000000000000000000000000000010
+2
00000000000000000000000000000011
+3
00000000000000000000000000000011
+4
números inteiros negativos
10000000000000000000000000000000
-0 com o bit do sinal s = 1
10000000000000000000000000000001
-1
10000000000000000000000000000010
-2 (
geralmente o valor -0 é igual ao +0)
10000000000000000000000000000011
-3
10000000000000000000000000000011
-4
Caso queira representar números inteiros maiores de +2147483647 ou menores que -2147483648 é necessário
utilizar o que se chama precisão dupla.
precisão simples
= 32 bits (4 octetos) [
para os computadores com palavra de 32 bits]
precisão dupla
= 64 bits (8 octetos) [
para os computadores com palavra de 32 bits]
A representação interna do número -5 será assim:
precisão simples:
10000000000000000000000000000100
precisão dupla:
1000000000000000000000000000000000000000000000000000000000000100
Utilizando a precisão dupla pode-se utilizar números inteiros na faixa:
−9223372036854775808 a +9223372036854775807
Números do tipo real
Nos computadores de 32 bits a precisão simples utiliza 32 bits
A representação interna de números reais é completamente diferente da representação interna dos números
inteiros. A representação interna de números do tipo
real de precisão simples
com sinal é feita assim:
Figura 01
– Representação interna do número real precisão simples +0.15625
Observe as seguintes características:
sinal:
1 bit
O sinal é guardado no bit 31 (
dos 32 bits – 4 octetos utilizados para representar internamente o número real)
expoente:
8 bits
O expoente é guardado em 8 bits que vai do bit 23 a bit 30
fração:
23 bits
A fração [
também chamado de: significando (signiticand), coeficiente, principal ou mantissa]O número real de precisão simples utiliza 1 bit para o sinal, 8 bits para o expoente e 24 (
apenas 23 são explicitamente registrados) na fração (
no significando).
A precisão (
quantidade de dígitos decimais usados no número decimal) obtida com um número real de precisão
simples representado internamente segundo este padrão (
padrão IEEE 754-1985) é de:
log
10(2
24) =7.22472 ≈ 7 dígitos (
porque o número de bits só pode ser inteiro)
Cálculo de um número dada a sua representação interna
A figura 01 mostra como o número real de precisão simples +0.15625 é guardado dentro de um computador de
32 bits. O cálculo do valor guardado com esta representação utiliza a seguinte fórmula:
N
=−
1
s×
1
f
×
2
e−127onde:
s
= sinal
f
= significando (
significand)
e
= expoente
Então o número:
0011110001000000000000000000000tem:
s = 0 em decimal: s = 0e = 0111100 e = 124
f = 01000000000000000000000 f = 0.25
logo
N
=−
1
0×
1
0.25
×
2
124−127=
1.25
×
2
−3=
0.15625
Usando-se números reais de precisão simples pode-se representar números decimais na faixa:
1.18
×
10
⁻38Por sua vez, a representação interna dos números do tipo
real de precisão dupla
com sinal é feita assim:
Figura 02
– Representação interna do número real precisão dupla
Figura obtida de: http://en.wikipedia.org/wiki/Double_precision_floating-point_format (acessado em 10/04/2010)
O mesmo tipo de regra continua valendo, então:
sinal:
1 bit
expoente:
11 bits
significando: 53 bits (
52 explicitamente guardados)
Com isto, o número real tem uma precisão de 15 dígitos (log
10(2
53) =15.954590 ≈ 15 )
Usando-se números reais de precisão dupla pode-se representar números decimais na faixa:
2.225
×
10
⁻308a
1.79769
×
10
308Para calcular um número real de precisão dupla usa-se a fórmula:
N
=−
1
s×
1
f
×
2
e−1023No Fortran
No Fortran, a forma recomendada para definir números inteiros e números reais de precisão dupla é utilizando
as funções intrínsecas:
selected_int_kind
e
selected_real_kind
.
Função intrínseca selected_int_kind
A função intrínseca selected_int_kind retorna um valor inteiro (
que para a maioria dos compiladores) representa a
quantidade de octetos (
bytes) que o computador utiliza para guardar os números inteiros desejado.
Exemplos:
selected_int_kind(2)
Quantidade de octetos (
bytes) usados para guardar um número inteiro que
esteja entre
10
⁻2e 10
2a função retorna o inteiro
1
porque 1 octeto (
byte) pode guardar
2
8=
256
números na faixa -128 e
+127, que (
claramente) abrange a faixa desejada -100 e +100
selected_int_kind(4)
Faixa
10
⁻4e 10
4retorna o inteiro
2
2 octetos (
bytes) →
2
16=
65536
ou seja: -32768 a +32767 que responde
pela faixa desejada -10000 a +10000
selected_int_kind(9)
Faixa
10
⁻9e 10
9selected_int_kind(16)
retorna o inteiro
8
8 octetos (
bytes)→ 64 bits →
precisão dupla
[
para as máquinas que utilizam palavra de 32 bits]
Para definir um inteiro de precisão dupla é necessário usar o especificador de parâmetro de tipo KIND.
program inteiro_simples_dupla
!--- !Exemplo de inteiro com precisão simples e precisão dupla ! Arquivo: inteiro_simples_dupla.f03
! AUTOR: Anibal L. Pereira 17/02/2011
!--- implicit none
integer:: i_simples = 364789776
integer( kind = selected_int_kind(16) ):: i_dupla = 364789776 print*
print"(a)"," ====================================================================" print"(2a)"," Multiplicação dos inteiros: ","3456 x 364789776 = 1260713465856" print"(a)"," ====================================================================" print*
print"(a,i13)","resultado usando precisão simples:", 3456*i_simples print"(a,i13)","resultado usando precisão dupla :", 3456*i_dupla print*
end program inteiro_simples_dupla
Execute este programa para ver como um programa simples, escrito sem cuidado, dá respostas
erradas !
Função intrínseca selected_real_kind
A função intrínseca selected_real_kind retorna um valor inteiro (
que para a maioria dos compiladores) representa a
quantidade de bytes que o computador utiliza para guardar os números reais desejado.
Por exemplo:
real( kind = selected_real_kind(10) ):: x_duplo
declara a variável real
x_duploque tem no mínimo 10 dígitos decimais de precisão e faixa de expoente não
especificada.
real( kind = selected_real_kind(8,70) ):: x_duplo
declara a variável real
x_duploque tem no mínimo 8 dígitos decimais de precisão e faixa de expoente que inclui
valores entre
10
−70e
10
70em magnitude.
program real_simples_dupla
!--- !Exemplo de real com precisão simples e precisão dupla ! Arquivo: real_simples_dupla.f03
! AUTOR: Anibal L. Pereira 17/02/2011
!--- implicit none
real:: x_simples = 364789776.0E20
real( kind = selected_real_kind(16) ):: x_dupla = 364789776.06E20 print*
print"(a)"," ======================================================================" print"(2a)"," Multiplicação dos reais: ","3456.0E34 X 364789776.06E20 = 0.12607E+67" print"(a)"," ======================================================================" print*
print*
end program real_simples_dupla
Execute o programa e veja sua saída !
Forma mais recomendada
Definir uma constante com nome para receber o inteiro que especifica o número de octetos (
bytes) a ser usado
na especificação de parâmetro de tipo é a forma mais adequada de proceder.
Ao se colocar num módulo as seguintes definições, estamos procedendo da forma mais adequada:
!--- Inteiro com 1, 2, 4 ou 8 bytes: ---integer,parameter:: I1B = selected_int_kind(2) ! inteiro 1 byte
integer,parameter:: I2B = selected_int_kind(4) ! inteiro 2 bytes
integer,parameter:: I4B = selected_int_kind(9) ! inteiro 4 bytes - precisão simples integer,parameter:: I8B = selected_int_kind(16) ! inteiro 8 bytes - precisão dupla !
!--- Real precisão simples, dupla e quádrupla ---integer,parameter:: sp = selected_real_kind(6,30) ! real precisão simples
integer,parameter:: dp = selected_real_kind(10,200) ! real precisão dupla integer,parameter:: qp = selected_real_kind(18,200) ! real precisão quádrupla
Parâmetro de tipo para constantes literais
O parâmetro de tipo (KIND) de uma constante numérica literal pode ser mudado pelo uso de um sufixo.
Escreva imediatamente à constante literal uma sublinha (
_
em inglês: underscore
) seguida da constante com
nome que define o parâmetro de tipo desejado.
Então, se
integer, parameter:: i8b = selected_int_kind(16)define a constante com nome que contém o
valor de definição dos inteiros de precisão dupla, procede-se como mostrado no exemplo:
program literal_simples_duplo
!--- !Mostra uso de constante literal numérica inteira de precisão simples e precisão dupla ! Arquivo: literal_simples_duplo.f03
! AUTOR: Anibal L. Pereira 17/02/2011
!--- implicit none
integer, parameter:: i8b = selected_int_kind(16)
integer :: N = 2000000000 !valor máximo é 2147483647 print*
print*,"=============================================================================" print*,"Diferença obtida na multiplicando 6 x 2000000000 com precisão simples e dupla" print*,"=============================================================================" print*
print"(a, i20,a)"," 6 x 2000000000 = ", 6 * N, " <== valor errado!!!!" print"(a, i20)","6_i8b x 2000000000 = ", 6_i8b * N
print*
end program literal_simples_duplo
As informações colocadas aqui são bastante resumidas, mas muito importantes, portanto
leia com atenção.
Conforme destacado anteriormente, aprender a programar envolve duas tarefas distintas e simultâneas:
1. linguagem de programação
Fortran 2003
2. planejamento
análise e projeto
Apesar de serem tarefas distintas, geralmente realizadas por pessoas distintas, no nosso caso, elas são realizadas
por uma única pessoa. Por este motivo
é muito importante ter clareza e delimitar corretamente cada uma
destas tarefas
.
Aproximação ponha-para-funcionar
Esta aproximação possibilita enfatizar as regras de sintaxe e a utilização das estruturas da linguagem.
A aproximação
ponha-para-funcionar
é bastante eficiente para
evidenciar e exemplificar um conceito, uma informação, uma regra,
uma implementação.
Boa para aprender uma linguagem de programação.
Serve de suporte ao item 1 – linguagem de programação
Muito utilizada nos primórdios da computação, entretanto quando utilizada para escrever programas apresenta
deficiências sérias
Aproximação codifica-conserta
Aproximação
codifica-conserta
(
Code-Fix Approach) é uma
melhora na aproximação ponha-para-funcionar.
é a mesma aproximação ponha-para-funcionar com a
diferença de possibilitar a especificação de mais de
um propósito ou requisito (especificação do
sistema)
Nesta aproximação, inicialmente faz-se a especificação do que se deseja (
determinação dos requisitos do sistema)
para então iniciar a codificação e realizar (
executar) vários ciclos da aproximação codifica-conserta. De tempo em
tempo, verifica-se se o programa atende as especificações desejadas. Quando atender, o programa está pronto.
Modelo em Cascata
Um passo importante na evolução da técnica de
desenvolvimento de software foi a criação do
modelo em cascata.
O
modelo em cascata
é um modelo sequencial no
qual o desenvolvimento do software (
sistema) é um
fluir constante para a frente através de fases:
conceito, análise de requisitos, projeto,
implementação e teste (
validação).
No modelo em cascata, mover-se de uma fase à
outra somente depois da fase anterior estar
completa e perfeita. Não há pulo para a frente, para
trás ou sobreposição entre as fases.
O modelo em cascata permite que o
trabalho com sistema de alta complexidade
, que possuem muitos
requisitos para serem satisfeitos. O modelo em cascata responde muito bem pelo item 2 – planejamento.
A grande dificuldade da utilização do método em cascata advêm do fato dele não permitir que se passe de uma
fase à outra sem que a fase anterior esteja completa e correta. Por isto, ele é pouco flexível o que cria dificuldades
para a sua utilização.
Desenvolvimento Rápido de Aplicação
O passo seguinte ao método em cascata foi a criação do
Desenvolvimento Rápido de Aplicação
(Rapid
Application Development
– RAD)
. O desenvolvimento rápido de aplicação é um modelo de desenvolvimento de
software iterativo e incremental que enfatiza um ciclo de desenvolvimento extremamente curto.
I
terativo e Incremental
desenvolvimento iterativo e incremental
(
IID
–
iterative and incremental development) foi um processo
criado para lidar com as fraquezas do modelo em cascata. Neste desenvolvimento, inicia-se com um
plano inicial (planejamento) e segue-se com vários ciclos incrementais (
evolutivos) até terminar com o
produto final.
É muito importante lembrar que o desenvolvimento rápido de aplicativo (RAD) é um processo
iterativo e incremental (IID)
O desenvolvimento rápido de aplicação (RAD) usa planejamento mínimo em favor de rápida prototipagem.
No desenvolvimento rápido de aplicação (RAD) o planejamento é sempre
intercalado com o processo de escrever o software
O desenvolvimento rápido de aplicação foi uma tentativa de encontrar a solução da questão: “
Como se pode
construir sistemas rapidamente, mantendo-se o controle sobre o produto?”.
As informações que seguem ajudarão à uma melhor compreensão do processo:
•
diálogo usuário-desenvolvedor
fundamentalmente, desenvolver um software é estabelecer um diálogo entre o cliente
(
cliente = customer) e o desenvolvedor
Cliente Desenvolvedor
Eu tenho uma ideia.
Boa ideia, o que você deseja?
O que desejo é …
Entendi, se eu construir isto, ele
atende suas necessidades?
Sim, pode fazer.
Pronto, terminei. Está bom?
Sim, perfeito!
•
cliente e desenvolvedor
neste texto, o termo
usuário
será utilizado tanto quanto o termo
cliente
Desenvolvedor (
muita vezes um time composto por analistas e programadores) é o responsável pelo
desenvolvimento do software
usuário e desenvolvedor são “entidades” distintas que devem ser considerado quando se desenvolve
um software
•
dois em um
para o desenvolvimento de software de porte pequeno (
caso dos programas feitos em nossa disciplina) o
usuário (
cliente) e o desenvolvedor serão a mesma pessoa. Esta única pessoa passa então a ser
responsável tanto pela definição dos requisitos (
estabelecido pelo cliente quando diz o que deseja ou espera do software) quanto pelo trabalho (
desenvolvimento do software)
Então, o processo de desenvolvimento rápido de aplicação (RAD)
que é assim:
Fica assim:
•
entendimento claro dos requisitos
é importante ter noção que o método de desenvolvimento rápido de aplicação (RAD) só funciona
corretamente se o desenvolvedor entender com clareza o que o cliente quer, isto é, se o desenvolvedor
entende, sem margem de dúvida, os requisitos desejados. Falha ou incerteza no entendimento dos
requerimentos quebrar o desenvolvimento do software
•
dois problemas
quando uma única pessoa (
dois em um) é responsável por definir os requisitos e desenvolver o software
(
faz o papel de cliente e de desenvolvedor), ainda assim o processo de desenvolvimento rápido de
aplicação (RAD) pode sofrer por causa do:
(1) mal entendido do problema a ser resolvido, o que leva ao estabelecimento de requisitos errados ou
incompletos e
(2) (
caso mais comum) os requisitos são alterados durante a fase de desenvolvimento, fazendo com que
os códigos escritos acabem como um ajuntamento de códigos sem coerência –
caótico
. Neste caso, o
desenvolvimento acaba se transformando na aproximação codifica-conserta
Aproximação Protótipo-Evolutivo
Em nossa disciplina, os programas serão desenvolvidos utilizando a aproximação
protótipo evolutivo
(Evolutionary prototyping –
EP
). Esta aproximação será utilizada porque ela é bastante adequada para nossos
propósitos.
Baseado no planejamento (análise), escreve-se um protótipo, onde os requisitos (pelo menos os
principais) são implementados sem muitos detalhes. Coloca-se o protótipo para funcionar. Faz-se
um primeiro teste (o protótipo responde pelo que se deseja?). Após o teste (avaliação realizada
depois de uma iteração), refina-se os requisitos (ajusta-se e/ou acrescenta-se novos).
Modifica-se o protótipo para responder aos novos e/ou requisitos ajustados (nova iteração). Novo teste.
Novo refinamento! Nova iteração . . . . . . Depois de N iterações o programa vai ganhando
consistência (e geralmente aumentando de tamanho) até que não mais serão necessárias
redefinições ou ajustes. Quando isto ocorrer, finaliza-se o programa!
A aproximação protótipo-evolutivo (EP) é bastante flexível. Ela iterativamente produz evoluções do sistema até
alcançar o produto final. Em cada iteração, as modificações necessárias são implementadas e quando necessário
novas funcionalidades são adicionadas.
Apesar da aproximação protótipo-evolutivo ser adequada para lidar com mudanças e acréscimo de requisitos,
durante uma iteração os requisitos não mudam. Ao final da iteração os requisitos são avaliados e, se
necessário, ajustes e acréscimos são identificados e registrados para serem implementados na nova iteração
.
A aproximação protótipo-evolutivo (EP) é uma boa abordagem, garantido que o processo todo não se reduza à
ciclos de codifica-conserta.
A grande dificuldade é exatamente esta, garantir que este tipo de redução não ocorra
.
Como não reduzir a aproximação protótipo-evolutivo à aproximação codifica-conserta:
Só codifique para implementar requisitos previamente definidos (planejados).
Redefinição e até mesmo acréscimo de requisito podem acontecer a qualquer momento, mas tem
que obrigatoriamente ser registrados para que possam ser submetida a testes (avaliação). Se
mudanças significativas nos requisitos acontecerem, isto obriga que seja feita uma reavaliação do
planejamento, porque a lógica utilizada não está respondendo adequadamente ao problema.
fáceis de serem trabalhados. Quando tudo vai bem, a baixa complexidade do sistema, permite que o protótipo
(
solução inicial) seja muito próximo da solução final e uma ou umas poucas iterações acabam levando ao produto
final desejado.
8. Nossa implementação
Para realizar a aproximação protótipo-evolutivo, faz-se o seguinte:
1. definir requisitos
planejamento
– especifica-se com clareza a tarefa e analisar o problema para quebrá-lo em
elementos fundamentais que facilitarão a solução
2. codificar
protótipo
– (
escrever o protótipo) de acordo com o passo 1
3. testar
validação
– testar o protótipo e (quando necessário) redefinir o planejamento.
4. Repetir os passos 1, 2 e 3
iteração
– repetir tantas vezes quanto necessárias, até que o protótipo funcione corretamente em
todas as situações
Pode-se dizer que escrever um programa significa essencialmente:
(1) Planejamento (definir a tarefa, análise e solução) e (2) Codificação.Em nossa disciplina, o planejamento é feito e documentado utilizando-se a
documentação externa
da atividade
(
ver item 6.3). O seguinte entendimento pode ser feito:
Definir a Tarefa
5) Propósitos:Análise e Solução
8) Método:9) Comentários e Observações:
Algoritmo
10) Pseudocódigo(s) ou Fluxograma(s) ou Diagrama(s) NSA relação não é restritiva, isto é, os itens não são estanques, eles podem se sobrepor em algum grau.
9. Documentação Externa
A documentação externa de um programa é onde se faz o planejamento da atividade e também como
disponibilizamos as informações mais importantes de uma atividade (
tarefa, problema ou programa).
Porque a documentação externa é uma fonte de informação separada do código fonte, ela, frequentemente, é
perdida, o que é péssimo. A documentação externa é muito importante (porque é o planejamento; é o local onde
resolvemos o problema ou tarefa) e deve ser tratada com bastante cuidado.
Em nossa disciplina a
documentação externa
deve ser preparada seguindo as seguintes regras:
•
Capa
todo trabalho entregue tem que ter uma capa (
é obrigatório a existência da capa)
ela é feita em folha de papel A4 sem pauta e visa identificar o trabalho
ela contém: identificação da instituição, instituto, departamento, disciplina, turma, nome do aluno, ano e
semestre
Universidade do Estado do Rio de Janeiro Instituto de Física Armando Dias Tavares
Departamento de Física Aplicada e Termodinâmica Física Computacional A
Atividade 02
Conceitos Iniciais
Turma 03
Anibal Leonardo Pereira
2009 segundo semestre
Figura 04
– Exemplo de capa para a documentação externa
Corpo da documentação externa
Escrito em folhas papel A4, sem pauta e contém os seguintes itens:
1) Atividade
identifica a atividade que está sendo documentada
2) Autor
identifica autor e data
3) Programa(s)
nome ou nomes dos programas
4) Arquivo(s)
nome ou nomes dos arquivos que compõem a atividade
5) Propósito(s)
identifica o fim ou os fins a serem alcançados pela atividade
6) Identificação dos Dados
identifica as variáveis tanto de entrada quanto de saída
7) Subprogramas
lista os subprogramas utilizados (módulos, funções e sub-rotinas)
8) Método
descreve e identifica o problema e (principalmente) o método ou técnica utilizada para resolver o problema
9) Comentários e Observações
quando necessários
10) Pseudocódigo(s) ou Fluxograma(s) ou Diagrama(s) NS
Um exemplo de
documentação externa
é mostrada na sequência. Para uma melhor visualização considere
a borda preta em volta como sendo os limites de uma folha A4, sem pauta.
1) Atividade: f02a5 pg. 01 2) Autor: Anibal L. Pereira 03/01/2009
3) Programa(s): minimo_1
4) Arquivo(s): f02a5.f03
5) Propósito(s): Escrever na tela do micro “Programa minimo 1 !”
6) Identificação dos Dados:
Entrada(s): nenhuma
Saída(s): constante do tipo caractere
7) Subprogramas:
[módulos] nenhum
[funções] nenhuma
[sub-rotinas] nenhuma
8) Método:
A tarefa (propósito) deste programa é realizada utilizando-se a declaração print do Fortran
9) Comentários e Observações:
O programa é bastante simples. Essencialmente ele utiliza a declaração print do Fortran e possuí diversos comentários que fazem a documentação interna do programa
10) Pseudocódigo(s) ou Fluxograma(s) ou Diagrama(s) NS:
algoritmo {programa minimo_1} {cabeçalho do programa}
escreva " Programa minimo 1 ! " fim algoritmo
O programa
minimo_1da atividade 05, salvo no arquivo f02a5.f03, é mostrado a seguir.
program minimo_1 !
!---! Propósito: Programa fonte (Fortran 2003) que escreve um texto na tela do ! micro utilizando a declaração print
!---! Arquivo: f02a5.f03
! Autor: Anibal L. Pereira 08/01/2009 !Revisões:
!---implicit none
print*
print*,"Programa Minimo 1 !" print*
end program minimo_1
Esta Folha contém
13 Atividades
12 atividades exemplos
01 atividade exemplo com ajustes e/ou acréscimos
00 atividades para serem feitas
00 atividades para serem ajustadas e/ou acrescidas
Seu professor de laboratório (e/ou teoria) poderá alterar esta relação !
Código da folha de atividades
Acesse a Home Page da disciplina, entre no link Datas-e-Atividades, para obter o código da
folha de atividades. Toda atividade tem que ter o "xx" substituído pelo código indicado. Exemplos: código 02 ► fxxa3.f03
→
f02a3.f03Atividade 01
Entregar em meio magnético:
1. programa: contagem_regressiva fxxa1.f03
2. Documentação externa: PARTE Entregar em papel o fluxograma do programa
Exemplo:
Exemplifica uso de número aleatório
Faz contagem regressiva iniciando com número entre 0 e 9 na tela do micro Uso da estrutura de repetição com número de repetição definida
O pseudocódigo do programa é mostrado a seguir:
algoritmo
declare i, {contador} na, {número inicial}
num_a {número aleatório} numérico aleatório(num_a)
na
←
10 * num_aescreva " Iniciando a contagem regressiva !" para i de na até 0 passo -1 faça
escreva i fim para fim algoritmo
FAZER:
Escreva o fluxograma do programa. Entregar (em papel) o fluxograma (não esqueça que o trabalho tem que ser entregue com uma capa)
Escreva o programa
contagem_regressivae salve-o no arquivo
fxxa1.f03Não deixe de atualizar o cabeçalho de forma adequada.
►
arquivo: fxxa1.f03◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄►◄
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
program contagem_regressiva !
!--- ! Propósito: Faz uma contagem regressiva na tela do micro
! iniciando em um número qualquer entre 10 e 1
!--- ! Arquivo: fxxa1.f03
! Autor: Anibal L. Pereira 02/10/2002 !Revisões: Anibal L. Pereira 28/09/2006 ! Anibal L. Pereira 17/02/2011 !
!--- implicit none
integer:: i, & ! contador do loop na ! número inicial real:: num_a ! número aleatório call random_number(num_a)
na=10*num_a
print*, " Iniciando a contagem regressiva !" !---
! imprime a contagem regressiva
!--- do i=na,0, -1
print"(t10,i2)", i end do
end program contagem_regressiva
Atividade 02
Entregar em meio magnético:
1. programa: contagem_regressiva_par fxxa2.f03
2. Documentação externa: PARTE Entregar em papel o fluxograma do programa
Exemplo:
Faz uma contagem regressiva pulando números impares Uso da estrutura de repetição com número de repetição definida
Observe que em cada execução do programa a contagem é sempre igual Para pular uma repetição usa-se a declaração cycle
if(condicao) cycle
Pseudocódigo do programa:
algoritmo
declare i, {contador} na, {número inicial}
num_a {número aleatório} numérico aleatório(num_a)
na
←
10 * num_aescreva " Iniciando a contagem regressiva !" para i de na até 0 passo -1 faça
se(modulo(i,2)≠ 0) pul a {pula} escreva i