123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- <!--
- Introducao 'a Programacao - desde 2017 - Prof. Leo^nidas de Oliveira Branda~o
- Por que evitar entrada/saida de dados em funcoes?
- LInE (Laboratory of Informatics in Education) - http://www.usp.br/line
- IME - USP
- Material didatico
- Pode usar livrevemente este material para fins nao comerciais, devendo sempre fazer referencia `a autoria.
- Sugestões/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "material de introducao 'a programacao")
- Autor: Prof. Leo^nidas de Oliveira Branda~o
- http://www.ime.usp.br/~leo
- http://line.ime.usp.br
- http://www.matemtica.br
- -->
- <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
- <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
- <script src="css_img_js_conf/defineLInE.js"></script> <!-- para referencias 'a documentos internos -->
- <div class="pagina">
- <p class="secao">Por que evitar entrada/saida de dados em funções</p>
- <p>
- A razão principal para se <b>evitar comandos de entrada e de saída dentro da definição de uma função</b> é conseguir
- maior flexibilidade para uso dessas funções, como explico a seguir.
- Evidentemente isso não se aplica quando a função for desenhada especificamente para providenciar a entrada (leitura) ou
- para a saída (impressão de dados. Por exemplo, o segundo caso ocorre na função <tt>imprime_comb</tt>
- Entretanto,
- Para explicar as razões pelas quais deve-se <b>evitar comandos de entrada e de saída dentro da definição de uma função</b>,
- devemos primeiro enfatizar o que é a uma função em linguagens como <i>C</i> ou <i>Python</i>:
- <ul>
- <li>é um código que recebe seus <em>dados de entrada</em> a partir de uma <i>lista de parâmetros</i> (<i class="def">parâmetros formais</i>);</li>
- <li>é um código que processa os dados de entrada e geralmente devolve algum valor como resposta (em seu comando <tt class="cmd">return</tt>);</li>
- <li>é um código que pode ser invocado a partir de outros trechos do programa, que providenciam os valores para os parâmetros
- (<i class="def">parâmetros efetivos</i>).</li>
- </ul>
- </p>
- <p>
- Desse modo, usando como exemplo a <i>função fatorial</i> (<i>fat(0)=1</i> e <i>fat(n)=n*fat(n-1), n>0</i>), a razão para
- se evitar comandos de entrada e de saída dentro da definição da função pode ser resumida em:
- <ul>
- <li><i>Facilita o reúso de código</i>, ou seja, sempre que precisar de um algoritmo que compute o fatorial,
- basta copiar o código que já implementou (e testou);</li>
- <li><i>Facilita o desenvolvimento de programas maiores</i>, pois pode-se agrupar os blocos lógicos que tenham um propósito bem definido como
- uma função.</li>
- </ul>
- </p>
- <p>
- A fim de ficar mais clara a vantagem de <i>passar os valores via parâmetro</i>, vamos examinar um exemplo matemático,
- <b style="color:#0000aa">computar o número de combinações de <i>n</i>, tomados <i>k</i> a <i>k</i></b>
- <a href="#comb" title="ver referência sobre combinação">[1]</a>.
- Vamos lembrar a combinatória usualmente estudada no <i>Ensino Médio</i>.
- Este problema pode ser traduzido em um exemplo do tipo "loteria":
- de quantas maneiras distintas é possível combinar <i>n=99</i> números (dentre <i>01</i> e <i>99</i>) tomando-se
- <i>k=5</i> deles? A resposta geral é dada pela expressão <i>C<sub>n,k</sub>=n!/((n-k)!k!)</i>, que com os dados de interesse
- resultaria em <i>C<sub>100,5</sub>=100!/((100-5)!5!) = 75.287.520</i>.
- <br/>
- Portanto, se você comprar um único bilhete dessa loteria por semana, em média precisaria de mais de 75 milhões de semanas para conseguir
- ter um bilhete premiado.
- <br/>
- <sabermais title="para saber um pouco mais">
- Como seria dificílimo tentar enumerar as mais de 75 milhões de possibilidade para verificar a corretuda da fórmula
- acima estão corretos, vamos testar com uma combinção menor, como combinar
- <i>n=5</i> valores <i>3</i> a <i>3</i>, neste caso a fórmula diz que teríamos<br/>
- <i>C<sub>5,3</sub>=5!/((5-3)!3!) = 5*4/2 = 10</i>
- <br/>
- e, de fato, existem <i>10</i> combinações:
- <i>{1,2,3}, {1,2,4}, {1,2,5},
- {1,3,4}, {1,3,5},
- {1,4,5},
- {2,3,4}, {2,3,5},
- {2,4,5},
- {3,4,5}</i>.
- </sabermais>
- </p>
- <p>
- Nesse contexto, imaginemos o uso
- <i style="color:#aa5555" title="conceitualmente errônea, como tento explicar a seguir">errôneo</i> de uma leitura do valor <i>n</i>
- dentro da função <tt class="var">fat</tt>.
- Então, se precisar computar a combinação de <tt>n</tt>, tomados <tt>k</tt> a <tt>k</tt> (<i>C<sub>n,k</sub>=n!/((n-k)!k!)</i>),
- esse valor <b>não</b> poderá ser computado usando a expressão seguinte (com chamada à função <tt class="var">fat</tt>):
- <tt class="var">fat(n)/(fat(n-k)fat(k))</tt>.
- <br/>
- <b style="color:#0000aa">Por que mesmo?</b>
- <br/>
- <porque>
- Se não percebe o <i style="color:#aa5555" title="erro conceitual">erro de programação</i>, vale a pena fazer uma simulação.
- Suponha que você tenha implementado o fatorial como uma função com comando de leitura, em "Portugol" <br/>
-
- <tt><verm>inteiro</verm> fat (n) {
- <verm>inteiro</verm> i=2, f=1; <verd>leia</verd>(n); <azul2>repita_enquanto</azul2> (i<=n) { f = f*i; i = i+1; } devolva f; }</tt>
- <br/>
- e deseje saber <i>C<sub>5,2</sub>=5!/((5-2)!2!)</i>.
- <br/>
- Então, ao entrar na função para computar <tt>fat(5)</tt> o usuário terá que digitar o valor <tt>5</tt>, para computar
- <tt>fat(3)</tt>, será obrigado a digitar o valor 3 e, para computar <tt>fat(2)</tt>, terá também que digitar o valor <tt>2</tt>.
- <br/>
- Mas vamos tornar o <i style="color:#aa5555">erro</i> (ou <i style="color:#aa5555">problema</i>) mais óbvio.
- Vamos supor que desejamos imprimir várias combinações (<i title="do Latin, exempli gratia">e.g.</i>, para computar o triângulo de Pascal).
- Por exemplo, examine como ficaria o calculo de <i>C<sub>10,1</sub>, C<sub>10,2</sub></i>, assim por diante até <i>C<sub>10,10</sub></i>
- (vide a função <tt>fat</tt> da tabela 1).
- Seria necessário que o "sofrido" usuário tenha que ficar digitando 10 triplas! (<i>10,9,1</i>; <i>10,8,2</i>; <i>10,7,3</i>;
- <i>10,6,4</i>; <i>10,5,5</i>; <i>10,4,6</i>; <i>10,3,7</i>; <i>10,2,8</i>; <i>10,1,9</i>; e <i>10,0,10</i>).
- Mas bastaria exigir que o usuário digitasse apenas um valor, <i>10</i>, se o programador (você) tivesse implementado o fatorial via
- parâmetro e sem leituras dentro dessa função. Bastaria usar um laço (vide a função <tt>main</tt> da tabela 3).
- <br/>
- Ou seja, não faz sentido obrigar o usuário a digitar dezenas, centenas, milhares, ou mais vezes, quando bastaria digitar um único valor.
- Se não estiver convencido da inviabilidade do uso de leitura dentro da função fatorial, copie os códigos da tabela 1 e o experimente.
- </porque>
- </p>
- <p>
- Assim, em exercícios que apresentam o conceito de função (portanto envolvendo um bloco lógico com propósito bem definido),
- procure, sempre que possível, <b>não</b> usar comandos de entrada ou de saída de dados <b>dentro da função</b>.
- Esse é um <i>bom hábito de programação</i>, no sentido de reduzir erros e facilitar que reaproveite sues códigos em outras
- situações.
- <br/>
- Mas vale lembrar que existem exceções: em uma função desenhada especificamente para "ler" dados, é necessá comandos para entrada e
- uma função específica para imprimir o <i>Triângulo de Pascal</i> precisa de comando de impressão.
- </p>
- <p>
- <!-- F<sub>0</sub>=1, F<sub>1</sub>=1, F<sub>k</sub> = F<sub>k-1</sub> + F<sub>k-2</sub>, k>2 -->
- Entretanto, existem as exceções, exemplos nos quais é útil usar comando de entrada ou de saída dentro da função. Ilustraremos
- o caso de utilidade de um comando de saída dentro da função, isso ocorre se desejamos computar (e imprimir) os <tt>n</tt> primeiros termos da
- <i title="F_{0}=1, F_{1}=1, F_{k} = F_{k-1} + F_{k-2}, k>1">sequência de Fibonacci</i>.
- Então, naturalmente precisaremos do comando de impressão/saída e como isso pode ser feito para qualquer
- <tt>n</tt> é adequado agrupar esse código na forma de um procedimento separado (no caso uma função <b>sem</b> valor de retorno).
- <br/>
- Porém se o objetivo fosse conhecer apenas o termo <tt>n</tt> da sequência, a função <b>não deveria ter o comando de saída</b>.
- </p>
- <p>
- <center>
- <i>Tab. 1. Códigos "errôneos" para imprimir o Triângulo de Pascal via fórmula de combinação</i>
- <table class="tableCd">
- <tr><td></td><td bgcolor="8aaada"><i>C</i> <td bgcolor="8aaada"><i>Python</i></td></tr>
- <tr><td><table class=""><tr class="trCd" valign="top"><td><pre style="font-size:0.8em"> 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 20
- </pre></td></tr></table></td>
- <td><table class=""><tr class="trCd" valign="top"><td><pre style="font-size:0.8em"><verm>int</verm> fat (<verm>int</verm> n) { <cyan>// define funcao com 1 parametro</cyan>
- <verm>int</verm> ft = 1, i=2;
- <cyan>// Experimente com esta linha,</cyan>
- <verd>scanf</verd>("%d", &n); <cyan>// depois sem ela</cyan>
- <azul2>while</azul2> (i <= n) {
- i = i + 1;
- ft = ft * i;
- }
- return ft;
- }
- <verm>void</verm> imprime_comb (<verm>int</verm> n) {
- <verm>int</verm> i=0, aux;
- <cyan>// Com mais a leitura abaixo o codigo ficaria</cyan>
- <cyan>// scanf("%d", &n); // "absurdo" ao quadrado!</cyan>
- <azul2>while</azul2> (i <= n) {
- aux = fat(n)/(fat(n-i)*fat(i));
- <verd>printf</verd>("%3d ", aux);
- i++;
- }
- <verd>printf</verd>("\n");
- }
- <verm>void</verm> main (void) {
- <verm>int</verm> i=0, n;
- <cyan>// Experimente com n pequeno, digamos n=3, assim ja'</cyan>
- <verd>scanf</verd>("%d", &n); <cyan>// percebera' o absurdo da leitura</cyan>
- <azul2>while</azul2> (i<=n) {
- imprime_comb(i);
- i++;
- }
- }</pre></td></tr></table></td>
- <td><table class=""><tr class="trCd" valign="top"><td><pre style="font-size:0.8em"><cyan># Python2 para 'print' sem pular linha : print("*" , end = "");</cyan>
- from __future__ import print_function
- <verm>def</verm> fat (n) :
- <cyan># Experimente com esta linha,</cyan>
- n = int(<verd>input</verd>()); <cyan># depois sem ela</cyan>
- f = 1;
- i = 2;
- <azul2>while</azul2> (i<=n) :
- f *= i;
- i += 1;
- return f;
- <verm>def</verm> imprime_comb (n) :
- <cyan># Com mais a leitura abaixo o codigo ficaria</cyan>
- <cyan># n = int(<verd>input</verd>()); # "absurdo" ao quadrado!</cyan>
- i=0;
- <azul2>while</azul2> (i<=n) :
- <cyan># C(n,k) = n!/((n-k)!*k!)</cyan>
- val = fat(n)/(fat(n-i)*fat(i));
- <cyan># print("C(%d,%d)=%d" % (n,i,val));</cyan>
- <verd>print</verd>("%3d " % (val), end="");
- i += 1;
- <verm>def</verm> main () :
- n = int(<verd>input</verd>());
- for i in range(n+1) :
- imprime_comb(i);
- <verd>print</verd>();
- main();
- </pre></td></tr></table></td></tr>
- </table>
- </center>
- </p>
- <span style="color: #0055AA;font-size:1.1em">Exemplo 1. Dados naturais <tt>n</tt> e <tt>k</tt>, computar a combinação de <tt>n</tt> <tt>k-a-k</tt></span>
- <p>
- Suponha que precisemos fazer um programa para computar
- quantas são as combinações de <tt>n</tt> termos combinados <tt>k</tt>-a-<tt>k</tt>, ou seja, tendo <tt>n</tt> elementos,
- desejamos saber de quantos modos distintos podemos formar grupos com <tt>k</tt> dos elementos.
- </p>
- <p>
- Esse conceito aparece em <i>probabilidade</i> e em <i>combinatória</i>, sabemos que esta combinação corresponde ao agrupamentos de
- elementos nos quais a ordem não importa (tanto faz a ordem dos <tt>k</tt> elementos do tipo <tt>a</tt>) e que a fórmula que fornece
- o total desses subconjuntos é a seguinte:
- <center><a href="https://en.wikipedia.org/wiki/Binomial_theorem" title="explicacao mais detalhada na WikiPedia">C(n,k) = n! / ((n-k)! k!)</a>.</center>
- </p>
- <p>
- Por exemplo, combinar <tt>6</tt> valores distintos, tomados <tt>2</tt> a <tt>2</tt> resulta <tt>15=6!/((6-2)!2!)=6!/(4!2!)</tt>, que
- são os <tt>15</tt> subconjuntos:
- <tt>(6,5)</tt>, <tt>(6,4)</tt>, <tt>(6,3)</tt>, <tt>(6,2)</tt>, <tt>(6,1)</tt>,
- <tt>(5,4)</tt>, <tt>(5,3)</tt>, <tt>(5,2)</tt>, <tt>(5,1)</tt>,
- <tt>(4,3)</tt>, <tt>(4,2)</tt>, <tt>(4,1)</tt>,
- <tt>(3,2)</tt>, <tt>(3,1)</tt>,
- <tt>(2,1)</tt>.
- </p>
- <p>
- Assim, existem duas possibilidades de implementar esse código, uma implementando a função fatorial (<tt>fat(n)</tt>) que seria
- invocada três vezes pelo programa principal (com algo como <tt><verd>imprima</verd>(fat(n) / (fat(k) * fat(n-k))</tt>.
- Outra possibilidade é implementar também o cômputo de <tt>fat(n) / (fat(k) * fat(n-k))</tt> também como uma função.
- Apesar dessa segunda possibilidade não ajudar tanto na organização do código (pois a segunda função (<tt>comb(n,k)</tt>) seria muito
- simples, ilustraremos dessa forma por ela usar função chamando função que chama função (a principal chama <tt>comb(n,k)</tt> e essa
- chama <tt>fat(n)</tt>).
- Veja como ficaria o código em uma <i>pseudo-linguagem</i> de programação:
- <pre style="font-size:0.8em"><verm>inteiro</verm> fat(n) { <cyan>//# estou supondo que o valor digitado pelo usuario seja um natural maior ou igual a 0 (n>=0)</cyan>
- <verm>inteiro</verm> valfat = 1;
- <verm>inteiro</verm> cont = 2;
- <azul2>repita_enquanto</azul2> (cont<=n) { <cyan>//# continue computando enquanto nao chegar ao valor cont==n</cyan>
- valfat = valfat * cont;
- cont = cont + 1;
- }
- devolva valfat;
- }
- <verm>inteiro</verm> comb(n, k) {
- devolva fat(n) / (fat(n-k)*fat(k));
- }
- <verm>vazio</verm> principal() {
- <verm>inteiro</verm> N, k; <cyan>//# note que os nomes de variaveis daqui NAO tem qualquer relacao com aqueles em 'fat'</cyan>
- <verm>inteiro</verm> a, b, c; <cyan>//# para uma segunda versao</cyan>
- <verd>leia</verd>(N, k); <cyan>//# leia 2 valores naturais e armazene-os respectivamente em N e em k (espera-se que digite N>=k)</cyan>
- <cyan>//# pode-se invocar seguidamente 'fat' 3 vezes como abaixo:</cyan>
- <verd>imprima</verd>(comb(N,k);
- <cyan>//# pode-se invocar a funcao 'fat' quanta vezes forem necessarias, aqui fazermos 3 vezes, armazenando cada resultado</cyan>
- a = fat(N);
- b = fat(k);
- c = fat(N-k);
- <verd>imprima</verd>(a/( b * c))); <cyan>//# usamos os 3 resultados de chamada de 'fat' para compor a resposta final N!/(k!*(N-k)!)</cyan>
- }</pre>
- </p>
- <p>
- A figura 1 ilustra os desvios no <i>fluxo de execução</i> do código acima.
- Note o destaque da instrução 2, que é uma atribuição que define o valor para a variável <i>c</i>, a ordem de construção é a
- usual de expressão, primeiro computa-se o <i>lado direito</i> da expressão e o resultado dela é armazenado na variável
- (que é o <i>lado esquerdo</i> da atribuição). Como o <i>lado direito</i> contém uma chamada de função, para computá-lo,
- desvia-se o <i>fluxo de execução</i> (seta que "sai" de <i>fat(k);</i>) para a execução da função <i>fat(.)</i>, inicia-se
- a variável local (na verdade declarada como parâmetro) <i>n</i> com o valor vindo de <i>k</i> (<i>parâmetro efetivo</i>),
- segue-se executando as instrução de <i>fat(.)</i> e, no exemplo, a última instrução indica que deve-se devolver para local
- que <i>invocou</i> <i>fat(.)</i> o valor atual na variável (local) <i>ft</i>.
- </p>
- <center>
- <img src="img/exemplo_exec_funcao.jpg" title="exemplo de execucao da funcao"/>
- <br/>
- <i>Fig. 1. Diagrama esquemática indicando desvio de execução função e local de retorno.</i>
- </center>
- <p>
- Novamente para enfatizar as desvantagens de usar leitura ou impressão dentro de funções,
- procure implementar o código acima, em sua linguagem favorita, e colocar nele comandos para
- leitura ou saída dentro da função <tt>fat(n)</tt>, como discutido abaixo.
- </p>
- <span style="color: #0055AA;font-size:1.1em">Observação 1. Vantagem de uso de função sem comandos de entrada/saída - combinação n, k-a-k</span>
- <p>
- Note que, <b>se</b> dentro da função <tt>fat</tt> exitisse um comando do tipo <tt><verd>leia</verd>(n);</tt>,
- a função NÃO poderia ser usada para computar o fatorial de algum valor já registrado em alguma variável, pois ao ser invocada,
- digamos <tt>fat(n)</tt>, seria necessário que o usuário <i style="color: #AA0000;">digitasse novamente</i> o valor para calculo
- do fatorial e valor para o qual desejava-se o fatorial (na variável <tt>n</tt>) seria descartado.
- No exemplo do código associado à figura 1, na primeira chamada para <i>N</i>, o usuário teria que digitar algum valor
- para calcular fatorial (e provavelmente nem saberia qual o valor guardado em <i>N</i>),
- na segunda chamada, para o valor para <i>k</i> ocorreria o mesmo, e por fim, o mesmo para o valor <i>N-k</i>.
- </p>
- <p>
- De forma análoga, se a função <i>fat</i> tivesse algum comando de saída, ao usar a função 3 vezes, o usuário
- receberia informações confusas: ele deseja a combinação de <i>n</i>, tomado <i>k</i>-a-<i>k</i>, mas
- receberia 4 infomações: na primeira chamada <i>fat(.)</i> imprimiria <i>n!</i>,
- segunda imprimiria <i>k!</i>, na terceira imprimiria o resultado de <i>(n-k)!</i> e, ao voltar
- para o código que invocou <i>fat(.)</i>, imprimiria finalmente a única coisa que o usuário desejava saber,
- <i>n!/(k! (n-k)!)</i>.
- </p>
- <span style="color: #0055AA;font-size:1.1em">Observação 2. Propriedade interessante de C(n,k) - Triângulo de Pascal</span>
- <p>
- Vamos aproveitar o código que computa a combinação de <i>n</i>, <i>k-a-k</i>, <i>C(n,k) = n!/(k!*(n-k)!)</i>, para computar
- a seguinte sequência de valores: <i>C(n,0)</i>, <i>C(n,1)</i>, <i>C(n,2)</i> e assim por diante, até <i>C(n,n)</i>.
- Mais ainda, vamos fazer isso para <i>n=1</i>, <i>n=2</i>, <i>n=3</i> e <i>n=4</i>.
- </p>
- <center>
- <i>Tab. 2. Triângulo de Pascal, como combinação e de modo direto</i>
- <table class="tableCd"><tr><td>
- <pre>Linha | Combinacoes C(n,0) ate C(n,n) | As saidas
- 0 | C(0,0) | 1
- 1 | C(1,0) C(1,1) | 1 1
- 2 | C(2,0) C(2,1) C(2,2) | 1 2 1
- 3 | C(3,0) C(3,1) C(3,2) C(3,3) | 1 3 3 1
- 4 | C(4,0) C(4,1) C(4,2) C(4,3) C(4,4) | 1 4 6 4 1
- 5 | C(5,0) C(5,1) C(5,2) C(5,3) C(5,4) C(5,5) | 1 5 10 10 5 1
- 6 | C(6,0) C(6,1) C(6,2) C(6,3) C(6,4) C(6,5) C(6,6) | 1 6 15 20 15 6 1</pre>
- </td></tr></table>
- </center>
- <p>
- Ou seja, na tabela acima, a linha <i>0</i>, tem uma única saída, a saber <i>C(0,0)</i>.
- A linha <i>1</i> tem duas saídas, <i>C(1,0)</i> e <i>C(1,1)</i>.
- A linha <i>2</i> tem três saídas, <i>C(2,0)</i>, <i>C(2,1)</i> e <i>C(2,2)</i>.
- Assim, por diante, até a linha <i>6</i> que tem sete saídas.
- </p>
- <p>
- O que é interessante, é que no <i>triângulo</i> (na coluna das saídas) aparece uma propriedade interessante:
- o elemento da linha <i>k</i>, na coluna <i>j</i> é igual à soma do elemento da
- linha <i>k-1</i>, na coluna <i>j-1</i>, com o elemento da
- linha <i>k-1</i>, na coluna <i>j</i>.
- Ou seja, vale a propriedade
- <i>C(k,j) = C(k-1,j-1) + C(k-1,j)</i> (sempre que <i>k>0</i> e <i>j</i> estiver entre <i>1</i> e <i>k</i>,
- essa é a relação de <i title="Michael Stifel (ca. 1487 1567)">Stifel</i>
- [<a href="#boyer" title="examinar referencia 2 - Boyer">2</a>, <a href="#dantzig" title="examinar referencia 2 - Dantzig">3</a>],
- e aquele é o
- <b title="Atribuido a Blaise Pascal (1623 1662) mas já conhecido por Yang Hui (fl. ca. 1261 1275) e Michael Stifel (ca. 1487 1567)">
- Triângulo de Pascal</b>.
- Veja por exemplo o
- <a href="https://en.wikipedia.org/wiki/Pascal%27s_triangle" title="seguir para WikiPedia">Triângulo de Pascal</a>
- <a href="#boyer" title="examinar referência Boyer">[2]</a>
- na <i>WikiPedia</i>.
- </p>
- <!--
- <pre>1 0
- ---
- 1 1
- 1 0
- ---
- 2 0
- 2 1
- 2 2
- ---
- 3 0
- 3 1
- 3 2
- 3 3
- ---
- 4 0
- 4 1
- 4 2
- 4 3
- 4 4</pre>
- <p>
- Examinando as saídas (última coluna) notamos uma propriedade interessante:
- o elemento da linha <tt>n+1</tt>, coluna <tt>k+1</tt> = o elemento da linha <tt>n</tt>, coluna <tt>k</tt> + o elemento da linha <tt>n</tt>, coluna </tt>k+1</tt>.
- Veja por exemplo, a última linha, linha <tt>4</tt>: o segundo elemento = <tt>4</tt> = <tt>1 + 3</tt> (da linha acima).
- </p>
- -->
- <!-- https://pt.wikipedia.org/wiki/Produtos_not%C3%A1veis -->
- <!--
- <span style="color: #0055AA;font-size:1.1em">Exemplo 2. Coeficientes da expansão do produto notável <tt>(a+b)^n</tt></span>
- <p>
- Suponhamos que precisemos fazer um programa que computa os termos dos coeficientes da expansão de <tt>(a+b)^n</tt>.
- Ou seja, desejamos um programa que dado <tt>n</tt> liste quantos termos do tipo <tt>a^i x b^j</tt> existe, para cada
- <tt>i</tt> e cada <tt>j</tt> tais que <tt>i+j=n</tt>.
- o valor da combinação de <i>N</i> tomado <i>k</i> a <i>k</i>, ou seja,
- C(N,k) = N! / ( k! (N-k)!).
- Para isso percebe-se que é necessário implementar o cômputo de fatorial que será utilizado 3 vezes.
- Para facilitar a compreensão, podemos escrever um código com 3 variáveis auxiliares para armazenar, respectivamente,
- <i>N!</i>, <i>k!</i> e <i>(N-k)!</i>.
- </p>
- <pre>(a+b)^0 = 1 a^0 b^0
- (a+b)^1 = (a+b) (0 a^0 b^0 + 0 a^0 b^0) = 1 a^1 b^0 + 1 a^0 b^0
- (a+b)^2 = (a+b) (1 a^1 b^0 + 0 a^0 b^1) = 1 a^2 b^0 + 2 a^1 b^1 + 1 a^0 b^2
- (a+b)^3 = (a+b) (1 a^2 b^0 + 2 a^1 b^1 + 1 a^0 b^2) = 1 a^3 b^0 + 3 a^2 b^1 + 3 a^1 b^2 + 1 a^0 b^3
- (a+b)^4 = (a+b) (1 a^3 b^0 + 3 a^2 b^1 + 3 a^1 b^2 + 1 a^0 b^3) = 1 a^4 b^0 + 4 a^3 b^1 + 6 a^2 b^2 + 4 a^1 b^3 + 1 a^0 b^3a
- </pre>
- <pre>1
- 1 1
- 1 2 1
- 1 3 3 1
- 1 4 6 4 1
- </pre>
- -->
- <span style="color: #0055AA;font-size:1.1em;">Implementações para gerar o "Triângulo de Pascal" em <i>C</i> e em <i>Python</i></span>
- <p>
- Abaixo apresento uma implementação para gerar o "Triângulo de Pascal" (como apresentado acima), nas linguagens
- <i>C</i> e <i>Python</i>.
- <center>
- <i>Tab. 3. Outra versão de códigos para imprimir o Triângulo de Pascal via fórmula de combinação.</i>
- <br/>
- <table>
- <tr><td>Gerando "Triângulo de Pascal" em <i>C</i></td> <td> </td> <td>Gerando "Triângulo de Pascal" em <i>Python</i></td></tr>
- <tr>
- <td><pre style="font-size:0.8em">#include <stdio.h>
- <verm>int</verm> fat (<verm>int</verm> n) { <cyan>// supondo usuario digite natural n>=0)</cyan>
- <verm>int</verm> valfat = 1;
- <verm>int</verm> cont = 2;
- <azul2>while</azul2> (cont<=n) { <cyan>// continue enquanto nao chegar a cont==n</cyan>
- valfat = valfat * cont;
- cont++;
- }
- return valfat;
- }
- <verm>int</verm> comb (<verm>int</verm> n, <verm>int</verm> k) { return fat(n) / (fat(n-k)*fat(k)); }
- <verm>void</verm> main (void) {
- <verm>int</verm> N, k; <cyan>// usa inteiros N, k</cyan>
- <verm>int</verm> a, b, c, n;
- <verd>printf</verd>("Digite N - para computar C(N,0), C(N,1), ..., C(N,N): ");
- <verd>scanf</verd>("%d", &N);
- <cyan>// pode-se invocar seguidamente 'fat' 3 vezes como abaixo:</cyan>
- for (n=0; n>N; n++) {
- for (k=0; k<=n; k++) {
- a = fat(n);
- b = fat(k);
- c = fat(n-k);
- <cyan>// usamos resultados de 'fat' para resposta N!/(k!*(N-k)!)</cyan>
- <verd>printf</verd>("%3d ", a/(b * c));
- }
- <verd>printf</verd>("\n");
- }
- }
- </pre></td> <td> </td>
- <td><pre style="font-size:0.8em"><cyan># Python2 para 'print' sem pular linha : print("*" , end = "");</cyan>
- from __future__ import print_function
- <verm>def</verm> fat (n) : <cyan># supondo usuario digite natural n>=0)</cyan>
- valfat = 1;
- cont = 2;
- <azul2>while</azul2> (cont<=n) : <cyan># continue enquanto nao chegar a cont==n</cyan>
- valfat = valfat * cont;
- cont = cont + 1;
- return valfat;
- <verm>def</verm> comb(n, k) :
- return fat(n) / (fat(n-k)*fat(k));
- <verm>def</verm> main () :
- <cyan># usa inteiros N, k</cyan>
- <verd>print</verd>("Digite N - para computar C(N,0), C(N,1), ..., C(N,N): ");
- N = int(<verd>input</verd>());
- <cyan># pode-se invocar seguidamente 'fat' 3 vezes como abaixo:</cyan>
- for n in range(N) :
- for k in range(n+1) :
- a = fat(n);
- b = fat(k);
- c = fat(n-k);
- <cyan># usamos resultados de 'fat' para resposta N!/(k!*(N-k)!)</cyan>
- <verd>print</verd>("%3d " % (a/(b * c)), end="");
- <verd>print</verd>();
- main();
- </pre></td>
- </tr></table>
- </center>
- </p>
- <span style="color: #0055AA;font-size:1.1em">Referências bibliográficas sobre "Triângulo de Pascal"</span>
- <ol type="1">
- <li> <a name="comb">[1]</a>
- <a href="https://en.wikipedia.org/wiki/Binomial_coefficient" title="examinar combinação na WikiPedia"
- target="_blank">https://en.wikipedia.org/wiki/Binomial_coefficient</a>.</li>
- <li> <a name="boyer">[2] Uta C. Merzbach e Carl B. Boyer</a>,
- "A History of Mathematics", third edition, John Wiley & Sons, Inc., 2011 (primeira edicao de 1968)
- </li>
- <li> <a name="dantzig">[3] Tobias Dantzig</a>, "NUMBER: The Language of Science", Pi Press, New Yourk, 2005
- </li>
- </ol>
- <!--
- Boyer, Carl B. (Carl Benjamin), 1906 1976.
- pg 184. "The Jade Mirror opens with a diagram of the arithmetic triangle,
- inappropriately known in the West as “Pascal’s triangle.” (See the fol-
- lowing illustration.) In Zhu’s arrangement, we have the coefficients of"
- -->
- <!--
- republicacao da 4a edicao de 1954
- (primeira edicao de 1930)
- pg. 71. "It is significant that we owe the first explicit formulation of
- the principle of recurrence to the genius of Blaise Pascal, a contem-
- porary and friend of Fermat. Pascal stated the principle in a tract
- called The Arithmetic Triangle which appeared in 1654. Yet it was"
- pg 279. "FIG 2. THE ARITHMETIC TRIANGLE OF PASCAL"
- -->
- <p class="autoria">
- <a href="https://www.ime.usp.br/~leo" target="_blank" title="seguir para a pagina do prof. Leônidas">Leônidas de Oliveira Brandão</a><br/>
- <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
- </p>
- <p class="rodape">
- <b>Alterações</b>:<br/>
- 2020/08/09: revisão do formato e ampla revisão do texto;<br/>
- 2019/06/06: vários pequenos acertos ao longo do texto;<br/>
- 2019/04/07: versao 2 (acrescentado novos paragrafos, melhorando explicacoes e frases);<br/>
- 2017/04/06: primeira versao.
- </p>
- </div>
|