http://carol.dimap.ufrn.br/logicwiki/api.php?action=feedcontributions&user=Clah&feedformat=atomLogic Wiki - User contributions [en]2024-03-28T14:31:38ZUser contributionsMediaWiki 1.34.2http://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=784Álgebra Booleana2016-06-03T12:38:22Z<p>Clah: </p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem2.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles.<br />
<br />
Construa a tabela da função booleana de grau 3.<br />
'''Solução'''<br />
<br />
Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas.<br />
<br />
Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso.<br />
Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a<br />
<pre>Dom3 := [false,false,false],<br />
[false,false,true],<br />
[false,true,false],<br />
[true,false,false],<br />
[true,false,true],<br />
[true,true,false],<br />
[false,true,true],<br />
[true,true,true];</pre><br />
Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''.<br />
<pre>with(combinat):</pre><br />
Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada<br />
<pre>writeto(terminal);</pre><br />
Use a facilidade help do Maple para saber mais sobre essas funções, se necessário.<br />
<br />
Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los.<br />
<br />
'''Solução'''<br />
<br />
Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos.<br />
Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado.<br />
<pre>with(logic):</pre><br />
Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar<br />
<pre>randbool(a,b);</pre><br />
ou<br />
<pre>randbool([a,b]);</pre><br />
Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''':<br />
<pre>for i from 1 to 10 do<br />
randbool(a,b);<br />
od;</pre><br />
Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes.<br />
<pre>randbool(x,y,z, CNF);<br />
randbool(u,v, MOD2);</pre><br />
Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema.<br />
A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores.<br />
O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada<br />
<pre>readlib(trace);</pre><br />
Agora, se gerarmos um expressão aleatória booleana com quatro variáveis<br />
<pre>e := randbool(a,b,c,d);</pre><br />
poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo<br />
<pre>bsimp(e);</pre><br />
O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui.<br />
O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada.<br />
Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''':<br />
<pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre><br />
Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1.<br />
<pre>Printlevel;</pre><br />
Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle.<br />
<pre>for i from 1 to 5 do<br />
sqrt(i);<br />
od;</pre><br />
Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple.<br />
Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto.<br />
<pre>for i from 1 to 5 do<br />
sqrt(sin(abs(i)));<br />
od;</pre><br />
Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000.<br />
Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados.<br />
<pre>with(logic): # esteja certo que 'bsimp' está definida<br />
e := x &and y &or &not z;<br />
bsimp(e);</pre><br />
Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte<br />
<pre>interface(verboseproc = 2);<br />
eval(bsimp); # assuma 'bsimp' carregada</pre><br />
Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui).<br />
Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas.<br />
Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média.<br />
<br />
== '''Exercícios e Projetos''' ==<br />
<br />
<br />
01) Use o Maple para verificar a Lei DeMorgan e as leis de comutatividade e associatividade:<br />
<br />
.: Leis de DeMorgan :. <br />
<br />
Equivalent(`&not`(`&or`(A, B)), `&and`(`&not`(A), `&not`(B)));<br />
true<br />
Equivalent(`&not`(`&and`(A, B)), `&or`(`&not`(A), `&not`(B)));<br />
true<br />
<br />
.: Comutatividade :.<br />
<br />
Equivalent(`&and`(A, B), `&and`(B, A));<br />
true<br />
Equivalent(`&or`(A, B), `&or`(B, A));<br />
true<br />
<br />
.: Associatividade :. <br />
<br />
exp1 := `&and`(A, `&and`(B, C));<br />
exp2 := `&and`(`&and`(A, B), C);<br />
Equivalent(exp1, exp2);<br />
true<br />
<br />
exp3 := `&or`(A, `&or`(B, C));<br />
exp4 := `&or`(`&or`(A, B), C);<br />
Equivalent(exp3, exp4);<br />
true<br />
<br />
<br />
02) Use Maple para construir as tabelas verdades para cada um dos seguintes pares de expressões booleanas.<br />
<br />
<br />
a) a -> b and b -> a<br />
with(Logic):<br />
TruthTable(((a &implies b)&and(b &implies a)), [a,b], output = Matrix);<br />
<br />
table([<br />
(true, true) = true,<br />
(false, true) = false,<br />
(false, false) = true,<br />
(true, false) = false ])<br />
<br />
b) a -> ¬b and b -> ¬a<br />
with(Logic): <br />
TruthTable(`&and`(`&implies`(a, `&not`(b)), `&implies`(b, `&not`(a))), [a, b], output = Matrix);<br />
<br />
table([<br />
(true, true) = false,<br />
(false, true) = true,<br />
(false, false) = true,<br />
(true, false) = true ])<br />
<br />
c) a + (b(¬c)) and (a + b +d)(a + c + d)<br />
with(Logic):<br />
TruthTable(`&and`(`&or`(a, `&and`(b, `&not`(c))), (`&or`(`&or`(a, b), c))*(a+c+d)), [a, b, c, d], output = Matrix);<br />
<br />
table([<br />
(false, false, true, true) = false,<br />
(true, false, false, true) = false,<br />
(false, false, false, false) = false,<br />
(true, false, true, true) = false,<br />
(false, false, false, true) = false,<br />
(true, true, false, true) = false,<br />
(false, false, true, false) = false, <br />
(true, true, false, false) = false,<br />
(true, false, false, false) = false,<br />
(false, true, true, false) = false,<br />
(true, true, true, true) = false,<br />
(false, true, true, true) = false, <br />
(true, true, true, false) = false,<br />
(false, true, false, false) = false,<br />
(true, false, true, false) = false,<br />
(false, true, false, true) = false ])<br />
<br />
== Referências ==<br />
[http://www.mhhe.com/math/advmath/rosen/r5/student/ch10/maple.html Maple: Chapter 10. Boolean Algebra, Kenneth H. Rosen]</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=781Relações2016-06-01T12:08:38Z<p>Clah: /* Cálculos e explorações */</p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''<br />
<br />
Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito.<br />
<br />
<pre><br />
AllRelations := proc(S::set)<br />
local s, t, # indices into S<br />
C; # Cartesian product SxS<br />
C := {};<br />
for s in S do<br />
for t in S do<br />
C := C union [s,t];<br />
od;<br />
od;<br />
combinat[powerset](C);<br />
end:<br />
</pre><br />
<br />
Nós agora testar o nosso procedimento em um conjunto menor, de modo a manter a saída de um comprimento razoável. O leitor é convidado para determinar o tempo de execução e o comprimento de saída para o processo, quando o conjunto de entrada tem cardinalidade 4 ou 5. Tenha em mente que existem <math>2^{n^{2}}</math> relações em um conjunto com n membros.<br />
<br />
<pre><br />
AllRelations(1,2);<br />
</pre><br />
<br />
2- Determine quantas relações transitivo existem em um conjunto com ''n'' elementos para todos os inteiros positivos ''n'' com ''n'' ≤ 7.<br />
<br />
'''Solução:'''<br />
<br />
Vamos construir cada possíveis <math>N x N</math> da matriz zero-um, usando um algoritmo semelhante ao de contagem binária. O pseudocódigo é:<br />
<br />
1- Para cada valor de 1 até <math>2^{(n ^{2})}</math>, criamos uma lista que é a representação de base 2 desse inteiro.<br />
<br />
2- Nós criamos uma matriz M com elementos sendo a lista de valores. Estes são todos os possíveis <math>n^{2}</math> de matriz zero-um (o leitor pode provar esta afirmação).<br />
<br />
3- Avaliar o fechamento transitivo de cada uma das matrizes que criamos, e retornar o conjunto de fechos transitivos dessas matrizes.<br />
<br />
A implementação é como se segue:<br />
<br />
<pre><br />
FindTransitive := proc(S::set)<br />
local i, j, T, P;<br />
P := {};<br />
for i from 0 to 2^(nops(S)^2)-1 do<br />
T := convert(i, base, 2);<br />
while nops(T) &lt; nops(S)^2 do<br />
T := [op(T), 0];<br />
od;<br />
P := P union matrix(nops(S), nops(S), T);<br />
od;<br />
P;<br />
end:<br />
</pre><br />
<br />
Mais uma vez, utilizamos o nosso processo em valores relativamente pequenos (devido ao comprimento da saída), e deixamos a exploração adicional para o leitor.<br />
<br />
<pre><br />
P := FindTransitive(1,2):<br />
Q := {}:<br />
for i from 1 to nops(P) do<br />
Q := Q union Warshall(P[i]):<br />
od:<br />
Q;<br />
</pre><br />
<br />
3- Encontre o fecho transitivo de uma relação em um conjunto com pelo menos 200 elementos.<br />
<br />
'''Solução:'''<br />
<br />
Vamos gerar aleatoriamente uma matriz zero-um com dimensão 10x10, e em seguida, aplicar o algoritmo de \textbf{Warshall} para deduzir o fecho transitivo matriz.<br />
<br />
Para gerar uma matriz zero-um aleatória, usamos a função '''randmatrix''' do pacote '''linalg''', e fornecer seu terceiro argumento, opcional com um procedimento que gera uma sequência aleatória 0's (zero) e 1's (uns). Em seguida, aplicamos o algoritmo de '''Warshall''' a esta matriz aleatória, obtendo o resultado. Aqui, para economizar espaço, podemos aplicar este procedimento para uma matriz de 10x10 como um exemplo.<br />
<br />
<pre><br />
Q := randmatrix(10, 10, entries=rand(2));<br />
Warshall(Q);<br />
</pre><br />
<br />
== Referências ==<br />
[http://www.mhhe.com/math/advmath/rosen/r5/student/ch07/maple.html Maple: Chapter 7. Relations]</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=780Relações2016-06-01T12:07:44Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''<br />
Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito.<br />
<br />
<pre><br />
AllRelations := proc(S::set)<br />
local s, t, # indices into S<br />
C; # Cartesian product SxS<br />
C := {};<br />
for s in S do<br />
for t in S do<br />
C := C union [s,t];<br />
od;<br />
od;<br />
combinat[powerset](C);<br />
end:<br />
</pre><br />
<br />
Nós agora testar o nosso procedimento em um conjunto menor, de modo a manter a saída de um comprimento razoável. O leitor é convidado para determinar o tempo de execução e o comprimento de saída para o processo, quando o conjunto de entrada tem cardinalidade 4 ou 5. Tenha em mente que existem <math>2^{n^{2}}</math> relações em um conjunto com n membros.<br />
<br />
<pre><br />
AllRelations(1,2);<br />
</pre><br />
<br />
2- Determine quantas relações transitivo existem em um conjunto com ''n'' elementos para todos os inteiros positivos ''n'' com ''n'' ≤ 7.<br />
<br />
'''Solução:'''<br />
Vamos construir cada possíveis <math>N x N</math> da matriz zero-um, usando um algoritmo semelhante ao de contagem binária. O pseudocódigo é:<br />
<br />
1- Para cada valor de 1 até <math>2^{(n ^{2})}</math>, criamos uma lista que é a representação de base 2 desse inteiro.<br />
<br />
2- Nós criamos uma matriz M com elementos sendo a lista de valores. Estes são todos os possíveis <math>n^{2}</math> de matriz zero-um (o leitor pode provar esta afirmação).<br />
<br />
3- Avaliar o fechamento transitivo de cada uma das matrizes que criamos, e retornar o conjunto de fechos transitivos dessas matrizes.<br />
<br />
A implementação é como se segue:<br />
<br />
<pre><br />
FindTransitive := proc(S::set)<br />
local i, j, T, P;<br />
P := {};<br />
for i from 0 to 2^(nops(S)^2)-1 do<br />
T := convert(i, base, 2);<br />
while nops(T) &lt; nops(S)^2 do<br />
T := [op(T), 0];<br />
od;<br />
P := P union matrix(nops(S), nops(S), T);<br />
od;<br />
P;<br />
end:<br />
</pre><br />
<br />
Mais uma vez, utilizamos o nosso processo em valores relativamente pequenos (devido ao comprimento da saída), e deixamos a exploração adicional para o leitor.<br />
<br />
<pre><br />
P := FindTransitive(1,2):<br />
Q := {}:<br />
for i from 1 to nops(P) do<br />
Q := Q union Warshall(P[i]):<br />
od:<br />
Q;<br />
</pre><br />
<br />
3- Encontre o fecho transitivo de uma relação em um conjunto com pelo menos 200 elementos.<br />
<br />
'''Solução:'''<br />
Vamos gerar aleatoriamente uma matriz zero-um com dimensão 10x10, e em seguida, aplicar o algoritmo de \textbf{Warshall} para deduzir o fecho transitivo matriz.<br />
<br />
Para gerar uma matriz zero-um aleatória, usamos a função '''randmatrix''' do pacote '''linalg''', e fornecer seu terceiro argumento, opcional com um procedimento que gera uma sequência aleatória 0's (zero) e 1's (uns). Em seguida, aplicamos o algoritmo de '''Warshall''' a esta matriz aleatória, obtendo o resultado. Aqui, para economizar espaço, podemos aplicar este procedimento para uma matriz de 10x10 como um exemplo.<br />
<br />
<pre><br />
Q := randmatrix(10, 10, entries=rand(2));<br />
Warshall(Q);<br />
</pre><br />
<br />
<br />
== Referências ==<br />
[http://www.mhhe.com/math/advmath/rosen/r5/student/ch07/maple.html Maple: Chapter 7. Relations]</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=779Relações2016-06-01T11:57:37Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''<br />
Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito.<br />
<br />
<pre><br />
AllRelations := proc(S::set)<br />
local s, t, # indices into S<br />
C; # Cartesian product SxS<br />
C := {};<br />
for s in S do<br />
for t in S do<br />
C := C union [s,t];<br />
od;<br />
od;<br />
combinat[powerset](C);<br />
end:<br />
</pre><br />
<br />
Nós agora testar o nosso procedimento em um conjunto menor, de modo a manter a saída de um comprimento razoável. O leitor é convidado para determinar o tempo de execução e o comprimento de saída para o processo, quando o conjunto de entrada tem cardinalidade 4 ou 5. Tenha em mente que existem <math>2^{n^{2}}</math> relações em um conjunto com n membros.<br />
<br />
<pre><br />
AllRelations(1,2);<br />
</pre></div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=778Relações2016-06-01T11:56:21Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''<br />
Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito.<br />
<br />
<pre><br />
AllRelations := proc(S::set)<br />
local s, t, # indices into S<br />
C; # Cartesian product SxS<br />
C := {};<br />
for s in S do<br />
for t in S do<br />
C := C union [s,t];<br />
od;<br />
od;<br />
combinat[powerset](C);<br />
end:<br />
</pre></div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=777Relações2016-06-01T11:55:58Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''<br />
Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito.<br />
<br />
<pre><br />
AllRelations := proc(S::set)<br />
local s, t, # indices into S<br />
C; # Cartesian product SxS<br />
C := {};<br />
for s in S do<br />
for t in S do<br />
C := C union [s,t];<br />
od;<br />
od;<br />
combinat[powerset](C);<br />
end:<br />
</pre></div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=776Relações2016-06-01T11:55:08Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=775Relações2016-06-01T11:54:53Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre><br />
<br />
== Diagramas de Hasse ==<br />
A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento.<br />
<br />
<pre><br />
HasseDiagramFirstTry := proc(R::po)<br />
local C;<br />
C := CoveringRelation(R);<br />
networks[draw](MakeDigraph(DomainRelation(C),C));<br />
end:<br />
</pre><br />
<br />
Por exemplo, aqui está uma imagem do divisor reticulado de 2100.<br />
<br />
<pre><br />
HasseDiagramFirstTry(DivisorLattice(2*3*5*7));<br />
</pre><br />
<br />
Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde.<br />
<br />
<pre><br />
IsAtom := proc(CR::rel, D::set, a::anything)<br />
local d;<br />
for d in D do<br />
if member([d,a], CR) then<br />
</pre><br />
encontrou um predecessor:<br />
<pre><br />
RETURN(false);<br />
fi;<br />
od;<br />
</pre><br />
deve ser um átomo:<br />
<pre><br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura.<br />
<br />
<pre><br />
Atoms := proc(CR::rel, D::set)<br />
local A, # set of atoms; returned<br />
d; # index into D<br />
A := {};<br />
for d in D do<br />
if IsAtom(CR, D, d) then<br />
A := A union d;<br />
fi;<br />
od;<br />
RETURN([op(A)]);<br />
end:<br />
</pre><br />
<br />
Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''.<br />
<br />
<pre><br />
HasseDiagram := proc(R::po)<br />
local L, C, G, A, D;<br />
C := CoveringRelation(R);<br />
D := DomainRelation(C); # = DomainRelation(R)<br />
G := MakeDigraph(D, R);<br />
L := NULL;<br />
while D &lt;&gt; {} do<br />
A := Atoms(C, D);<br />
L := L, sort(A);<br />
D := D minus op(A);<br />
od;<br />
networks[draw](Linear(L), G);<br />
end:<br />
</pre><br />
<br />
Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado.<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(4));<br />
HasseDiagram(DivisorLattice(6));<br />
HasseDiagram(DivisorLattice(81));<br />
</pre><br />
<br />
Os dois seguintes são especialmente bonitos!<br />
<br />
<pre><br />
HasseDiagram(DivisorLattice(2*3*5*7));<br />
HasseDiagram(DivisorLattice(2*3*5*2));<br />
</pre><br />
<br />
== Cálculos e explorações ==<br />
Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações.<br />
<br />
1- Exibir todas as diferentes relações em um conjunto com 4 elementos.<br />
<br />
'''Solução:'''</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=774Relações2016-06-01T11:49:21Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==<br />
Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo).<br />
<br />
Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados.<br />
<br />
<pre><br />
DivisorLattice := proc(n::posint)<br />
DividesRelation(numtheory[divisors](n));<br />
end:<br />
</pre><br />
<br />
O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto.<br />
<br />
<pre><br />
type(DivRel(6), po);<br />
</pre><br />
<br />
Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas.<br />
<br />
<pre><br />
IsLattice := proc(R::po)<br />
IsMeetSemiLattice(R) and IsJoinSemiLattice(R);<br />
end:<br />
</pre><br />
<br />
Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''.<br />
<br />
<pre><br />
IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation;<br />
</pre><br />
<br />
Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum.<br />
<br />
<pre><br />
IsMeetSemiLattice := proc(R::po)<br />
local DomR, # the domain of R<br />
r,s; # indices into R<br />
DomR := DomainRelation(R);<br />
for r in DomR do<br />
for s in DomR do<br />
if lub(R, r, s) = NULL then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa.<br />
<br />
<pre><br />
IsLattice(DivisorLattice(24));<br />
</pre><br />
<br />
Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}.<br />
<br />
<pre><br />
IsLattice(DividesRelation(seq(i, i=1..24)));<br />
</pre><br />
<br />
== Relações de abrangência ==<br />
É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação.<br />
<br />
Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro.<br />
<br />
<pre><br />
Covers := proc(R::po, a, b)<br />
local u; # index into Dom(R)<br />
if a = b then<br />
RETURN(false);<br />
fi;<br />
if not member([a,b], R) then<br />
RETURN(false);<br />
fi;<br />
for u in DomainRelation(R) minus a, b do<br />
if member([a,u], R) and member([u,b], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple:<br />
<br />
<pre><br />
CoveringRelation := proc(R::po)<br />
local C, # covering relation; returned<br />
DomR, # the domain of R<br />
r,s; # indices into DomR<br />
DomR := DomainRelation(R);<br />
C := {};<br />
for r in DomR do<br />
for s in DomR do<br />
if Covers(R, r, s) then<br />
C := C union [r,s];<br />
fi;<br />
od;<br />
od;<br />
RETURN(C);<br />
end:<br />
</pre><br />
<br />
Vejamos alguns pequenos exemplos.<br />
<br />
<pre><br />
CoveringRelation(DivisorLattice(6));<br />
CoveringRelation(DividesRelation(1,3,5,7,11,13,17));<br />
</pre></div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=773Relações2016-06-01T11:39:07Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre><br />
<br />
Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado.<br />
<br />
<pre><br />
mub := proc(R::po, S::set)<br />
MinimalElements(R, UpperBounds(R, S));<br />
end:<br />
</pre><br />
<br />
Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL.<br />
<br />
<pre><br />
lub := proc(R::po, S::set)<br />
local M; # set of minimal upper bounds of S<br />
M := mub(R, S);<br />
if nops(M) &lt;&gt; 1 then<br />
RETURN(NULL);<br />
fi;<br />
RETURN(op(M));<br />
end:<br />
</pre><br />
<br />
A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima.<br />
<br />
<pre><br />
TopSort := proc(R::po, T::set)<br />
local i, k, S, A;<br />
k := 1;<br />
S := T;<br />
A := [];<br />
while S &lt;&gt; {} do<br />
A := [op(A), MinimalElements(R, S)[1] ];<br />
S := S minus A[k];<br />
k := k+1;<br />
od;<br />
A;<br />
end:<br />
R := DivisorLattice(12);<br />
TopSort(R, DomainRelation(R));<br />
R := DivisorLattice(2*3*5);<br />
TopSort(DualRelation(R), DomainRelation(R));<br />
R := DivisorLattice(2*3*5*7);<br />
TopSort(R, numtheory[divisors](2*3*7));<br />
</pre><br />
<br />
== Grades ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=772Relações2016-06-01T11:36:35Z<p>Clah: </p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===<br />
Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção.<br />
<br />
Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso.<br />
<br />
<pre><br />
with(linalg):<br />
int_to_bool(0) := false:<br />
int_to_bool(1) := true:<br />
bool_to_int(true) := 1:<br />
bool_to_int(false) := 0:<br />
</pre><br />
<br />
Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3.<br />
<br />
<pre><br />
BoolJoin := proc(A::matrix, B::matrix)<br />
local i, j, C;<br />
C := matrix(rowdim(A), coldim(A), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(A) do<br />
C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]);<br />
od;<br />
od;<br />
map(bool_to_int,C);<br />
end:<br />
</pre><br />
<br />
Após isso, nós construímos o produto booleano.<br />
<br />
<pre><br />
BoolProd := proc(A::matrix, B::matrix)<br />
local i, j, k, C;<br />
C := matrix(rowdim(A), coldim(B), zeroes);<br />
for i from 1 to rowdim(A) do<br />
for j from 1 to coldim(B) do<br />
C[i,j] := false;<br />
for k from 1 to coldim(A) do<br />
C[i,j] := C[i,j]<br />
or (int_to_bool(A[i,k])<br />
and int_to_bool(B[k,j]));<br />
od;<br />
od;<br />
od;<br />
map(bool_to_int, C);<br />
end:<br />
</pre><br />
<br />
Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto.<br />
<br />
<pre><br />
TransClosure := proc(M::matrix)<br />
local i, A, B;<br />
A := M;<br />
B := A;<br />
for i from 2 to coldim(M) do<br />
A := BoolProd(A, M);<br />
B := BoolJoin(B, A);<br />
evalm(A);<br />
evalm(B);<br />
od;<br />
evalm(B);<br />
end:<br />
</pre><br />
<br />
Nós testamos nosso procedimento de fecho transitivo em um exemplo.<br />
<br />
<pre><br />
T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]):<br />
TransClosure(T1);<br />
</pre><br />
<br />
Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple.<br />
<br />
<pre><br />
Warshall := proc(M::matrix)<br />
local i, j, k, W, n;<br />
W := map(int_to_bool,M);<br />
n := coldim(M);<br />
for k from 1 to n do<br />
for i from 1 to n do<br />
for j from 1 to n do<br />
W[i,j] := W[i,j] or (W[i,k] and W[k,j]);<br />
od;<br />
od;<br />
od;<br />
evalm(map(bool_to_int, W));<br />
end:<br />
Warshall(T1);<br />
</pre><br />
<br />
Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}.<br />
<br />
<pre><br />
T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]);<br />
st:=time():Warshall(T2):time()-st;<br />
st:=time():TransClosure(T2):time()-st;<br />
</pre><br />
<br />
A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este.<br />
<br />
<br />
== Relações de equivalência ==<br />
Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito.<br />
<br />
Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples.<br />
<br />
<pre><br />
IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive;<br />
</pre><br />
<br />
Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto.<br />
<br />
<pre><br />
EqClass := proc(R::set, a::anything)<br />
local i, S;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
if R[i][1] = a then<br />
S := S union R[i][2];<br />
fi;<br />
od;<br />
RETURN(S);<br />
end:<br />
EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0);<br />
</pre><br />
<br />
Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto.<br />
<br />
<pre><br />
DetermineEqClass := proc(A::set)<br />
local P, Q, S, E, i, j, p;<br />
S := {};<br />
E := {};<br />
for i from 1 to nops(A) do<br />
for j from 1 to nops(A) do<br />
S := S union [A[i], A[j] ] ;<br />
od;<br />
od;<br />
P := combinat[powerset](S);<br />
for p in P do<br />
if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then<br />
E := E union p;<br />
fi;<br />
od;<br />
RETURN(E);<br />
end:<br />
DetermineEqClass(1,2);<br />
DetermineEqClass(1,2,3);<br />
</pre><br />
<br />
Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R:<br />
<br />
<br />
1- Criar o fecho reflexivo da relação R; chamam isso de P.<br />
<br />
2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q.<br />
<br />
3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica.<br />
<br />
<br />
Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''.<br />
<br />
<pre><br />
EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix;<br />
R2;<br />
EqContainment(R2, 1,2,3,4);<br />
</pre><br />
<br />
== Ordenamento parcial e elementos mínimos ==<br />
Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor.<br />
<br />
Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity'').<br />
<br />
<pre><br />
`type/po` := proc(obj)<br />
type(obj, rel) and IsReflexive(obj)<br />
and IsAntiSymmetric(obj)<br />
and IsTransitive(obj);<br />
end:<br />
</pre><br />
<br />
Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente.<br />
<br />
Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''.<br />
<br />
<pre><br />
MinimalElements := proc(R::po, S::set)<br />
local M, # set of minimal elements of S; returned<br />
s,t; # indices into S<br />
if S minus DomainRelation(R) &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
M := S;<br />
for s in S do<br />
for t in S minus s do<br />
if member([t,s], R) then<br />
M := M minus s;<br />
fi;<br />
od;<br />
od;<br />
RETURN(M);<br />
end:<br />
R := DividesRelation(1,2,3,6);<br />
MinimalElements(R, 1,2,3,6);<br />
MinimalElements(R, 2,3,6);<br />
</pre><br />
<br />
Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements'').<br />
<br />
<pre><br />
MaximalElements := proc(R::po, S::set)<br />
MinimalElements(DualRelation(R), S);<br />
end:<br />
MaximalElements(R, 1,2,3,6);<br />
MaximalElements(R, 1,2,3);<br />
</pre><br />
<br />
Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto.<br />
<br />
<pre><br />
IsUpperBound := proc(R::po, S::set, u::anything)<br />
local s; # index into S<br />
</pre><br />
verificação de sanidade:<br />
<pre><br />
if not member(u, DomainRelation(R)) then<br />
ERROR(`bad arguments`);<br />
fi;<br />
for s in S do<br />
if not member([s, u], R) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
UpperBounds := proc(R::po, S::set)<br />
local U, # set of upper bounds of S; returned<br />
DomR, # domain of R<br />
d; # index into DomR<br />
DomR := DomainRelation(R);<br />
</pre><br />
verificação de erros:<br />
<pre><br />
if S minus DomR &lt;&gt; {} then<br />
ERROR(`set must be contained in the domain of the relation`);<br />
fi;<br />
U := {};<br />
for d in DomR do<br />
if IsUpperBound(R, S, d) then<br />
U := U union d;<br />
fi;<br />
od;<br />
RETURN(U);<br />
end:<br />
</pre></div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Rela%C3%A7%C3%B5es&diff=764Relações2016-05-30T13:26:41Z<p>Clah: Created page with "Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando co..."</p>
<hr />
<div>Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações.<br />
<br />
Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais.<br />
<br />
<br />
== Uma Introdução às Relações em Maple ==<br />
O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado.<br />
<br />
Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição.<br />
<br />
<pre><br />
`type/pair` := [anything, anything];<br />
`type/rel` := set(pair);<br />
</pre><br />
<br />
Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível.<br />
<br />
Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''.<br />
<br />
<pre><br />
DividesRelation := proc(A::set(integer))<br />
local i, j, temp, R;<br />
R := {};<br />
for i in A do<br />
for j in A do<br />
if (gcd(i,j) = i) then<br />
R := R union [i, j];<br />
fi;<br />
od;<br />
od;<br />
RETURN(R);<br />
end:<br />
DividesRelation(1,2,3,4);<br />
</pre><br />
<br />
Por conveniência, nós também definir a seguinte variação.<br />
<br />
<pre><br />
DivRel := proc(n::posint)<br />
local i;<br />
DividesRelation(seq(i,i=1..n));<br />
end:<br />
</pre><br />
<br />
Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}.<br />
<br />
Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação.<br />
<br />
<pre><br />
DualRelation := proc(R::rel)<br />
local u;<br />
map(u -&gt; [ u[2], u[1] ], R);<br />
end:<br />
</pre><br />
<br />
Isto simplesmente inverte todos os pares que pertencem à relação.<br />
<br />
<br />
== Determinar Propriedades de Relações usando Maple ==<br />
Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada.<br />
<br />
Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação.<br />
<br />
<pre><br />
DomainRelation := proc(R::rel)<br />
RETURN(map(u-&gt;op(1,u), R)<br />
union map(u-&gt;op(2,u), R));<br />
end:<br />
</pre><br />
<br />
Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva.<br />
<br />
<pre><br />
IsReflexive := proc(R::rel)<br />
local is_reflexive, # return value<br />
u; # index into Dom(R)<br />
is_reflexive := true;<br />
for u in DomainRelation(R) do<br />
is_reflexive := is_reflexive and member([u,u], R);<br />
od;<br />
RETURN(is_reflexive);<br />
end:<br />
</pre><br />
<br />
<pre><br />
R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]:<br />
R2 := [1,1], [1,2], [2,1]:<br />
IsReflexive(R1);<br />
IsReflexive(R2);<br />
</pre><br />
<br />
Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte.<br />
<br />
<pre><br />
IsSymmetric := proc(R::rel)<br />
local i, is_symmetric;<br />
is_symmetric := true;<br />
for i from 1 to nops(R) do<br />
if not member( [ R[i][2], R[i][1] ], R) then<br />
is_symmetric := false;<br />
fi;<br />
od;<br />
RETURN(is_symmetric);<br />
end:<br />
</pre><br />
<br />
Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é.<br />
<br />
<pre><br />
IsAntiSymmetric := proc(R::rel)<br />
local u; # index into R<br />
for u in R do<br />
if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric(R1); IsSymmetric(R2);<br />
IsAntiSymmetric(R1); IsAntiSymmetric(R2);<br />
R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]:<br />
R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]:<br />
IsAntiSymmetric(R3); IsAntiSymmetric(R4);<br />
</pre><br />
<br />
Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será.<br />
<br />
<pre><br />
IsTransitive := proc(R::rel)<br />
local DomR, # domain of R<br />
u,v,w;# indices into DomR<br />
for u in DomR do<br />
for v in DomR do<br />
for w in DomR do<br />
if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
<br />
IsTransitive(R1); IsTransitive(R2);<br />
IsTransitive(R3); IsTransitive(R4);<br />
</pre><br />
<br />
<br />
== Relações n-árias em Maple ==<br />
Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos.<br />
<br />
<pre><br />
M1:=[Adams, 9012345,`Politics`, 2.98],<br />
[Woo, 9100055, `Film Studies`, 4.99],<br />
[Warshall, 9354321, `Mathematics`, 3.66]:<br />
</pre><br />
<br />
O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante.<br />
<br />
Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas.<br />
<br />
<pre><br />
MakeProjection := proc(R::set, P::list)<br />
local i, j, S, temp_list;<br />
S := {};<br />
for i from 1 to nops(R) do<br />
temp_list := [];<br />
for j from 1 to nops(P) do<br />
temp_list := [ op(temp_list), R[ i ][ P[j] ] ];<br />
od;<br />
S := S union temp_list;<br />
od;<br />
S;<br />
end:<br />
</pre><br />
<br />
Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção.<br />
<br />
<pre><br />
MakeProjection(M1, [3,4,1]);<br />
MakeProjection(M1, [2,4]);<br />
</pre><br />
<br />
Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo:<br />
<br />
<br />
1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p;<br />
<br />
2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento;<br />
<br />
3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x;<br />
<br />
4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída.<br />
<br />
<br />
<pre><br />
MakeJoin := proc(p, A, B)<br />
local i, j, k, C, list_A, list_B, x,<br />
ret_elem, is_done;<br />
list_A := [];<br />
list_B := [];<br />
C := {};<br />
for i from 1 to p do<br />
list_B := [op(list_B), i];<br />
list_A := [nops(B[1])-i, op(list_A)];<br />
od;<br />
for i from 1 to nops(A) do;<br />
is_done := false;<br />
x := MakeProjection(A[i], list_A);<br />
j := 1;<br />
while j &lt;= nops(B) and is_done = false do<br />
if MakeProjection(B[j], list_B) = x then<br />
ret_elem := A[i];<br />
for k from p+1 to nops(B[j]) do<br />
ret_elem := [op(ret_elem), B[j][k] ];<br />
od;<br />
is_done := true;<br />
fi;<br />
j := j+1;<br />
od;<br />
C := C union ret_elem;<br />
od;<br />
C;<br />
end:<br />
</pre><br />
<br />
Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus.<br />
<br />
<pre><br />
A:=[Cruz, Zoology, 335],<br />
[Cruz, Zoology, 412],<br />
[Farber, Psychology, 501],<br />
[Farber, Psychology, 617],<br />
[Grammer, Physics, 544],<br />
[Grammer, Physics, 551],<br />
[Rosen, Computer, 518],<br />
[Rosen, Mathematics, 575]:<br />
B:=[Computer, 518, N521, 14],<br />
[Mathematics, 575, N502, 15],<br />
[Mathematics, 611, N521, 16],<br />
[Physics, 544, B505, 16],<br />
[Psychology, 501, A100, 15],<br />
[Psychology, 617, A110, 11],<br />
[Zoology, 335, A100, 9],<br />
[Zoology, 412, A100, 8]:<br />
MakeJoin(2, A, B);<br />
</pre><br />
<br />
<br />
== Representar relações como dígrafos e Matrizes Zero-Um ==<br />
Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações.<br />
<br />
<br />
=== Representando Relações Usando Grafos Dirigidos ===<br />
Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''.<br />
<br />
<pre><br />
with(networks):<br />
</pre><br />
<br />
Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''.<br />
<br />
<pre><br />
MakeDigraph := proc(A::set,R::set)<br />
local G;<br />
new(G);<br />
addvertex(A, G);<br />
addedge(R, G);<br />
RETURN(G);<br />
end:<br />
G1 := MakeDigraph(1,2,3,4,R1);<br />
R1;<br />
</pre><br />
<br />
O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.)<br />
<br />
Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo.<br />
<br />
1- Entrada: um gráfico G, que representa uma relação R;<br />
<br />
2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer;<br />
<br />
3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo;<br />
<br />
4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R.<br />
<br />
5- Saída: o valor da decisão a partir das etapas 3 e 4.<br />
<br />
<br />
A implementação deste pseudocódigo é a seguinte;<br />
<br />
<pre><br />
IsTransitive_G := proc(G::graph)<br />
local i, j, S, T, is_trans;<br />
is_trans := true;<br />
T := allpairs(G);<br />
S := vertices(G);<br />
for i from 1 to nops(S) do;<br />
for j from 1 to nops(S) do;<br />
if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then<br />
is_trans := false<br />
fi;<br />
od;<br />
od;<br />
is_trans;<br />
end:<br />
IsTransitive_G(G1);<br />
R2;<br />
IsTransitive_G(MakeDigraph(1,2,3,4,R2));<br />
</pre><br />
<br />
Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles.<br />
<br />
=== Relações representando Usando Matrizes Zero-One ===<br />
Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''.<br />
<br />
<pre><br />
with(linalg):<br />
</pre><br />
<br />
Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue;<br />
<br />
<pre><br />
\item{Entrada: o conjunto R e domínio D.}<br />
\item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.}<br />
\item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.}<br />
\item{Saída: M.}<br />
</pre><br />
<br />
Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento.<br />
<br />
<pre><br />
MakeMatrix := proc (R::set, D::set)<br />
local i, j, L;<br />
L := [];<br />
for i from 1 to nops(D) do<br />
for j from 1 to nops(D) do<br />
if member([i,j],R) then<br />
L := [op(L), 1] else L := [op(L),0];<br />
fi;<br />
od;<br />
od;<br />
evalm(matrix(nops(D), nops(D), L));<br />
end:<br />
</pre><br />
<br />
Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um.<br />
<br />
<pre><br />
m1:=MakeMatrix(R1,1,2,3,4);<br />
m2:=MakeMatrix(R2,1,2,3,4);<br />
m3:=MakeMatrix(R3, 1,2,3,4);<br />
m4:=MakeMatrix(R4,1,2,3,4);<br />
</pre><br />
<br />
Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um.<br />
<br />
<pre><br />
IsReflexive_M:= proc(M::matrix)<br />
local i, is_reflex;<br />
is_reflex := true;<br />
for i from 1 to coldim(M) do<br />
if M[i,i] = 0 then<br />
is_reflex := false;<br />
fi;<br />
od;<br />
is_reflex;<br />
end:<br />
IsReflexive_M(m1);<br />
IsReflexive_M(m3);<br />
</pre><br />
<br />
Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''.<br />
<br />
<pre><br />
IsSymmetric_M := proc(M::matrix)<br />
local i,j; # row and column indices<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i-1 do<br />
if M[i,j] &lt;&gt; M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Deve ser quadrada.<br />
O código para verificar antissimetria é semelhante.<br />
<br />
<pre><br />
IsAntiSymmetric_M := proc(M::matrix)<br />
local i,j;<br />
if rowdim(M) &lt;&gt; coldim(M) then<br />
RETURN(false);<br />
fi;<br />
for i from 1 to rowdim(M) do<br />
for j from 1 to i - 1 do<br />
if M[i,j] = 1 and M[i,j] = M[j,i] then<br />
RETURN(false);<br />
fi;<br />
od;<br />
od;<br />
RETURN(true);<br />
end:<br />
</pre><br />
<br />
Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas.<br />
<br />
<pre><br />
IsSymmetric_M(m1); IsAntiSymmetric_M(m1);<br />
IsSymmetric_M(m2); IsAntiSymmetric_M(m2);<br />
IsSymmetric_M(m4); IsAntiSymmetric_M(m4);<br />
</pre><br />
<br />
<br />
== Fecho Computacional de Relações ==<br />
Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação.<br />
<br />
=== Fecho reflexivo ===<br />
O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação.<br />
<br />
<pre><br />
RefClose := proc(M::matrix)<br />
local i;<br />
for i from 1 to coldim(M) do<br />
M[i,i] := 1;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo.<br />
<br />
<pre><br />
RefClose(m1); RefClose(m4);<br />
</pre><br />
<br />
=== Fecho simétrico ===<br />
Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é:<br />
<br />
<pre><br />
SymmClose := proc(M::matrix)<br />
local i, j;<br />
for i from 1 to coldim(M) do<br />
for j from 1 to rowdim(M) do<br />
if M[i,j] = 1 then<br />
M[j,i] := 1;<br />
fi;<br />
od;<br />
od;<br />
evalm(M);<br />
end:<br />
</pre><br />
<br />
Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue.<br />
<br />
<pre><br />
SymmClose(m1); SymmClose(m4);<br />
</pre><br />
<br />
=== Fecho transitivo ===</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=Fundamentos_Matem%C3%A1ticos_da_Computa%C3%A7%C3%A3o_2&diff=760Fundamentos Matemáticos da Computação 22016-05-30T12:38:56Z<p>Clah: </p>
<hr />
<div><br />
[[Álgebra Booleana]]<br />
<br />
[[Árvores]]<br />
<br />
[[Indução e Recursão Matemática]]<br />
<br />
[[Relações]]</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=571Álgebra Booleana2016-05-27T03:46:45Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem2.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles.<br />
<br />
Construa a tabela da função booleana de grau 3.<br />
'''Solução'''<br />
<br />
Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas.<br />
<br />
Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso.<br />
Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a<br />
<pre>Dom3 := [false,false,false],<br />
[false,false,true],<br />
[false,true,false],<br />
[true,false,false],<br />
[true,false,true],<br />
[true,true,false],<br />
[false,true,true],<br />
[true,true,true];</pre><br />
Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''.<br />
<pre>with(combinat):</pre><br />
Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada<br />
<pre>writeto(terminal);</pre><br />
Use a facilidade help do Maple para saber mais sobre essas funções, se necessário.<br />
<br />
Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los.<br />
<br />
'''Solução'''<br />
<br />
Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos.<br />
Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado.<br />
<pre>with(logic):</pre><br />
Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar<br />
<pre>randbool(a,b);</pre><br />
ou<br />
<pre>randbool([a,b]);</pre><br />
Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''':<br />
<pre>for i from 1 to 10 do<br />
randbool(a,b);<br />
od;</pre><br />
Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes.<br />
<pre>randbool(x,y,z, CNF);<br />
randbool(u,v, MOD2);</pre><br />
Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema.<br />
A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores.<br />
O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada<br />
<pre>readlib(trace);</pre><br />
Agora, se gerarmos um expressão aleatória booleana com quatro variáveis<br />
<pre>e := randbool(a,b,c,d);</pre><br />
poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo<br />
<pre>bsimp(e);</pre><br />
O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui.<br />
O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada.<br />
Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''':<br />
<pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre><br />
Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1.<br />
<pre>Printlevel;</pre><br />
Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle.<br />
<pre>for i from 1 to 5 do<br />
sqrt(i);<br />
od;</pre><br />
Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple.<br />
Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto.<br />
<pre>for i from 1 to 5 do<br />
sqrt(sin(abs(i)));<br />
od;</pre><br />
Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000.<br />
Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados.<br />
<pre>with(logic): # esteja certo que 'bsimp' está definida<br />
e := x &and y &or &not z;<br />
bsimp(e);</pre><br />
Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte<br />
<pre>interface(verboseproc = 2);<br />
eval(bsimp); # assuma 'bsimp' carregada</pre><br />
Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui).<br />
Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas.<br />
Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média.<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=File:Imagem2.png&diff=570File:Imagem2.png2016-05-27T03:46:09Z<p>Clah: MsUpload</p>
<hr />
<div>MsUpload</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=569Álgebra Booleana2016-05-27T03:41:24Z<p>Clah: /* Cálculos e Explorações */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles.<br />
<br />
Construa a tabela da função booleana de grau 3.<br />
'''Solução'''<br />
<br />
Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas.<br />
<br />
Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso.<br />
Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a<br />
<pre>Dom3 := [false,false,false],<br />
[false,false,true],<br />
[false,true,false],<br />
[true,false,false],<br />
[true,false,true],<br />
[true,true,false],<br />
[false,true,true],<br />
[true,true,true];</pre><br />
Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''.<br />
<pre>with(combinat):</pre><br />
Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada<br />
<pre>writeto(terminal);</pre><br />
Use a facilidade help do Maple para saber mais sobre essas funções, se necessário.<br />
<br />
Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los.<br />
<br />
'''Solução'''<br />
<br />
Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos.<br />
Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado.<br />
<pre>with(logic):</pre><br />
Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar<br />
<pre>randbool(a,b);</pre><br />
ou<br />
<pre>randbool([a,b]);</pre><br />
Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''':<br />
<pre>for i from 1 to 10 do<br />
randbool(a,b);<br />
od;</pre><br />
Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes.<br />
<pre>randbool(x,y,z, CNF);<br />
randbool(u,v, MOD2);</pre><br />
Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema.<br />
A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores.<br />
O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada<br />
<pre>readlib(trace);</pre><br />
Agora, se gerarmos um expressão aleatória booleana com quatro variáveis<br />
<pre>e := randbool(a,b,c,d);</pre><br />
poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo<br />
<pre>bsimp(e);</pre><br />
O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui.<br />
O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada.<br />
Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''':<br />
<pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre><br />
Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1.<br />
<pre>Printlevel;</pre><br />
Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle.<br />
<pre>for i from 1 to 5 do<br />
sqrt(i);<br />
od;</pre><br />
Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple.<br />
Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto.<br />
<pre>for i from 1 to 5 do<br />
sqrt(sin(abs(i)));<br />
od;</pre><br />
Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000.<br />
Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados.<br />
<pre>with(logic): # esteja certo que 'bsimp' está definida<br />
e := x &and y &or &not z;<br />
bsimp(e);</pre><br />
Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte<br />
<pre>interface(verboseproc = 2);<br />
eval(bsimp); # assuma 'bsimp' carregada</pre><br />
Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui).<br />
Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas.<br />
Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média.<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=568Álgebra Booleana2016-05-27T03:40:10Z<p>Clah: /* Cálculos e Explorações */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
<math>2^{2^{3}}</math><br />
<br />
Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles.<br />
<br />
Construa a tabela da função booleana de grau 3.<br />
'''Solução'''<br />
<br />
Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas.<br />
<br />
Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso.<br />
Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a<br />
<pre>Dom3 := [false,false,false],<br />
[false,false,true],<br />
[false,true,false],<br />
[true,false,false],<br />
[true,false,true],<br />
[true,true,false],<br />
[false,true,true],<br />
[true,true,true];</pre><br />
Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''.<br />
<pre>with(combinat):</pre><br />
Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada<br />
<pre>writeto(terminal);</pre><br />
Use a facilidade help do Maple para saber mais sobre essas funções, se necessário.<br />
<br />
Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los.<br />
<br />
'''Solução'''<br />
<br />
Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos.<br />
Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado.<br />
<pre>with(logic):</pre><br />
Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar<br />
<pre>randbool(a,b);</pre><br />
ou<br />
<pre>randbool([a,b]);</pre><br />
Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''':<br />
<pre>for i from 1 to 10 do<br />
randbool(a,b);<br />
od;</pre><br />
Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes.<br />
<pre>randbool(x,y,z, CNF);<br />
randbool(u,v, MOD2);</pre><br />
Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema.<br />
A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores.<br />
O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada<br />
<pre>readlib(trace);</pre><br />
Agora, se gerarmos um expressão aleatória booleana com quatro variáveis<br />
<pre>e := randbool(a,b,c,d);</pre><br />
poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo<br />
<pre>bsimp(e);</pre><br />
O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui.<br />
O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada.<br />
Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''':<br />
<pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre><br />
Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1.<br />
<pre>Printlevel;</pre><br />
Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle.<br />
<pre>for i from 1 to 5 do<br />
sqrt(i);<br />
od;</pre><br />
Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple.<br />
Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto.<br />
<pre>for i from 1 to 5 do<br />
sqrt(sin(abs(i)));<br />
od;</pre><br />
Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000.<br />
Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados.<br />
<pre>with(logic): # esteja certo que 'bsimp' está definida<br />
e := x &and y &or &not z;<br />
bsimp(e);</pre><br />
Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte<br />
<pre>interface(verboseproc = 2);<br />
eval(bsimp); # assuma 'bsimp' carregada</pre><br />
Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui).<br />
Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas.<br />
Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média.<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=567Álgebra Booleana2016-05-27T03:35:12Z<p>Clah: /* Cálculos e Explorações */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
<math>2^{2^{3}}</math><br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=566Álgebra Booleana2016-05-27T03:34:31Z<p>Clah: /* Cálculos e Explorações */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
<math>2^({2^{3}})</math><br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=565Álgebra Booleana2016-05-27T03:31:09Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=564Álgebra Booleana2016-05-27T03:29:22Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=563Álgebra Booleana2016-05-27T03:28:44Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|200px]].<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre>x y z<br />
<br />
false false false true<br />
false false true false<br />
false true false d<br />
true false false d<br />
false true true true<br />
true false true false<br />
true true false false<br />
true true true true</pre><br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=File:Imagem.png&diff=562File:Imagem.png2016-05-27T03:21:27Z<p>Clah: Clah uploaded a new version of &quot;File:Imagem.png&quot;: MsUpload</p>
<hr />
<div>MsUpload</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=561Álgebra Booleana2016-05-27T03:20:55Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes.<br />
Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo.<br />
Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta ______<br />
<br />
Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições.<br />
A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas.<br />
Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso.<br />
<pre>dcmin := proc(pt::set,list, dc::set,list)<br />
local e, # expression to return<br />
te, # temporary expression<br />
i, # index<br />
s, # the size of the smallest expression so far<br />
PT, # pt as a set<br />
DC, # dc as a set<br />
PDC, # power set of DC<br />
T, # temporary set (loop variable)<br />
S; # temporary domain for well-defined functions<br />
PT := op(pt);<br />
DC := op(dc);<br />
PDC := combinat[powerset](DC);<br />
s := infinity;<br />
for T in PDC do<br />
S := T union PT;<br />
te := SumOfProductsExpansion(op(S));<br />
if evalb(length(te) < s) then<br />
e := te;<br />
s := length(e);<br />
fi;<br />
od;<br />
if s = infinity then<br />
ERROR(`can't happen`);<br />
else<br />
RETURN(e);<br />
fi;<br />
end:</pre><br />
Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada.<br />
Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho.<br />
Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada.<br />
Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões.<br />
Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente.<br />
<pre><br />
<br />
<br />
x<br />
y<br />
z<br />
<br />
<br />
<br />
<br />
false<br />
false<br />
false<br />
true<br />
<br />
<br />
false<br />
false<br />
true<br />
false<br />
<br />
<br />
false<br />
true<br />
false<br />
d<br />
<br />
<br />
true<br />
false<br />
false<br />
d<br />
<br />
<br />
false<br />
true<br />
true<br />
true<br />
<br />
<br />
true<br />
false<br />
true<br />
false<br />
<br />
<br />
true<br />
true<br />
false<br />
false<br />
<br />
<br />
true<br />
true<br />
true<br />
true</pre><br />
<br />
<br />
A entrada é o conjunto<br />
<pre>\{false, false, false, false, true, true, true, true, true\}</pre><br />
De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto<br />
<pre>\{false, true, false, true, false, false\}</pre><br />
de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir.<br />
<pre>dcmin(<br />
[false, false, false],<br />
[false, true, true],<br />
[true, true, true],<br />
[false, true, false],<br />
[true, false, false]);</pre><br />
Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas:<br />
<pre>dcmin(<br />
[[false, false, false],<br />
[false, true, true],<br />
[true, true, true]],<br />
[[false, true, false],<br />
[true, false, false]]);</pre><br />
(O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion''').<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=560Álgebra Booleana2016-05-27T03:17:47Z<p>Clah: /* Representação de Funções Booleanas */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=559Álgebra Booleana2016-05-27T03:17:30Z<p>Clah: /* Representação de Funções Booleanas */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x, y, z)'', de três variáveis, com a seguinte tabela de verdade.<br />
<pre>x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1</pre><br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=558Álgebra Booleana2016-05-27T03:06:52Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=557Álgebra Booleana2016-05-27T03:00:57Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
{0, 1}^n<br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=556Álgebra Booleana2016-05-27T03:00:28Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
<math>'{'{0, 1}'}'^{n}</math><br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=555Álgebra Booleana2016-05-27T02:59:36Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
<math>{{0, 1}}^{n}</math><br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=554Álgebra Booleana2016-05-27T02:59:16Z<p>Clah: /* Cálculos e Explorações */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
<math>{0, 1}^{n}</math><br />
<br />
== '''Cálculos e Explorações''' ==<br />
<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=553Álgebra Booleana2016-05-27T02:59:04Z<p>Clah: /* Condições Indiferentes */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
<br />
<math>{0, 1}^{n}</math><br />
<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=552Álgebra Booleana2016-05-27T02:50:12Z<p>Clah: /* Minimização de Expressões Booleanas e Circuitos */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito.<br />
Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas.<br />
Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível.<br />
A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''.<br />
Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
pode ser minimizada atarvés da utilização de Maple da seguinte forma<br />
<pre>with(logic):<br />
distrib(x &and (y &or &not y));</pre><br />
que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y<br />
<pre>bequal(y &or &not y, true);</pre><br />
de modo que ''x ( y + ȳ )'' simplifica ainda mais para x.<br />
O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima?<br />
Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''.<br />
Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente.<br />
Por exemplo, para simplificar o exemplo anterior<br />
<pre>e := (x &and y) &or (x &and &not y);</pre><br />
você pode digitar<br />
<pre>with(logic): # load 'bsimp'<br />
bsimp(e);</pre><br />
Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados.<br />
<pre>with(logic):<br />
e := (w &and x &and y &and (&not z))<br />
&or (w &and (&not x) &and y &and z)<br />
&or (w &and (&not x) &and y &and (&not z))<br />
&or ((&not w) &and x (&not y) &and z)<br />
&or ((&not w) &and (&not x) &and y &and z)<br />
&or ((&not w) &and (&not x) &and (&not y) &and z);</pre><br />
O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto.<br />
<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=551Álgebra Booleana2016-05-27T02:36:59Z<p>Clah: /* Minimização de Expressões Booleanas e Circuitos */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
<math>2^{2^{n}}</math><br />
<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=550Álgebra Booleana2016-05-27T02:36:21Z<p>Clah: /* Minimização de Expressões Booleanas e Circuitos */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
<math>2^2^n</math><br />
<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=549Álgebra Booleana2016-05-27T02:35:41Z<p>Clah: /* Minimização de Expressões Booleanas e Circuitos */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
<math>{2_{2_{n}}}</math><br />
<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=548Álgebra Booleana2016-05-27T02:34:46Z<p>Clah: /* Minimização de Expressões Booleanas e Circuitos */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
<br />
<math>2_{2_{n}}</math><br />
<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=547Álgebra Booleana2016-05-27T02:31:24Z<p>Clah: /* Representação de Funções Booleanas */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
<br />
Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais.<br />
<br />
Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.)<br />
<br />
Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.)<br />
<br />
Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões.<br />
<br />
Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''.<br />
<br />
O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''.<br />
<br />
Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade.<br />
x y z <br />
0 0 0 1<br />
0 0 1 0<br />
0 1 0 1 <br />
1 0 0 0 <br />
0 1 1 1 <br />
1 0 1 0 <br />
1 1 0 1 <br />
1 1 1 1<br />
<br />
Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista<br />
(0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1)<br />
de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes<br />
(0 0 1), (1 0 0), (1, 0, 1)<br />
no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente.<br />
Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal<br />
<pre>getMinTerm := proc(ll)<br />
local e, # the minterm, to be returned<br />
t, # temporary variable<br />
i; # loop index</pre><br />
checar argumento,<br />
<pre> if not type(ll, list(boolean)) then<br />
ERROR(`expecting a list of boolean values`);<br />
fi;</pre><br />
fazer a construção,<br />
<pre> for i from 1 to nops(ll) do<br />
if ll[i] = true then<br />
t := `x`.i;<br />
else<br />
t := `not x`.i;<br />
fi;<br />
if i = 1 then<br />
e := t;<br />
else<br />
e := cat(e, ` and `, t);<br />
fi;<br />
od;</pre><br />
e adicionar parênteses para melhorar a legibilidade.<br />
<pre>RETURN(cat(`(`, e, `)`));<br />
end:</pre><br />
Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos<br />
<pre>getMinTerm([true, false, true]);</pre><br />
Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa<br />
<pre>SumOfProductsExpansion := proc()<br />
local e, # expression to return<br />
i; # loop variable</pre><br />
se não há argumentos, a função é identicamente ‘false’<br />
<pre> if nargs = 0 then<br />
RETURN(`false`);<br />
fi;<br />
for i from 1 to nargs do<br />
if not type(args[i], list(boolean)) then<br />
ERROR(`arguments must be lists of booleans`);<br />
fi;<br />
if i = 1 then<br />
e := getMinTerm(args[i]);<br />
else<br />
e := cat(e, ` or `, getMinTerm(args[i]));<br />
fi;<br />
od;<br />
RETURN(e);<br />
end:</pre><br />
Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir.<br />
<pre>SumOfProductsExpansion(<br />
[false, false, false],<br />
[false, true, false],<br />
[false, true, true],<br />
[true, true, false],<br />
[true, true, true]<br />
);</pre><br />
<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=546Álgebra Booleana2016-05-27T01:57:00Z<p>Clah: /* Forma Normal Disjuntiva */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
<br />
O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe<br />
<pre>with(logic):<br />
canon((a &or b) &and (c &and d), a,b,c,d);<br />
canon((a &or b) &and (&not a &or b), a,b);<br />
canon((a &or b) &and (&not a &or b), b);</pre><br />
O último exemplo mostra que deve haver pelo menos variáveis suficientes para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis que aparecem na forma normal disjuntiva . É possível especificar as variáveis que não aparecem na equação original.<br />
<pre>canon(a, a,b);</pre><br />
Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica).<br />
<pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, CNF);<br />
canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre><br />
<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=545Álgebra Booleana2016-05-27T01:53:28Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=544Álgebra Booleana2016-05-27T01:52:00Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta -- isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=543Álgebra Booleana2016-05-27T01:51:04Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta -- isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
e formular nossas expressões usando operadores inertes.<br />
<pre>with(logic): # don't forget to define 'bequal'.<br />
left := (x &and &not y)<br />
&or (y &and &not z)<br />
&or (z &and &not x);<br />
right := (&not x &and y)<br />
&or (&not y &and z)<br />
&or (&not z &and x);<br />
bequal(left, right);</pre><br />
Agora, nos usamos esta afirmação à vontade.<br />
<pre>dual(left);<br />
dual(right);<br />
bequal(%, %%);</pre><br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=542Álgebra Booleana2016-05-27T01:49:00Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana.<br />
Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente.<br />
Para usar essa procedure você deve carregar o pacote '''logic''';<br />
<pre>with(logic):</pre><br />
A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos.<br />
<pre>dual(false);<br />
dual(true);<br />
dual(x &and y);<br />
dual(x &or (&not y &or &not x and &not (&not z)));</pre><br />
<br />
A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade!<br />
Enquanto é possível usar Maple para provar uma identidade pela força bruta -- isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante.<br />
Como um exemplo disto, vamos usar o Maple para provar a identidade<br />
[[File:imagem.png|200px]]<br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=541Álgebra Booleana2016-05-27T01:48:13Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
[[File:imagem.png|200px]]<br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=540Álgebra Booleana2016-05-27T01:47:18Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
[[File:imagem.png|400px]]<br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=File:Imagem.png&diff=539File:Imagem.png2016-05-27T01:47:05Z<p>Clah: MsUpload</p>
<hr />
<div>MsUpload</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=538Álgebra Booleana2016-05-27T01:41:24Z<p>Clah: /* Dual */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=537Álgebra Booleana2016-05-27T00:58:05Z<p>Clah: /* Verificando Identidades Booleanas */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
<br />
É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir:<br />
<pre>with(logic); # for 'bequal()'<br />
left := x &or (y &and z);<br />
right := (x &or y) &and (x &or z);<br />
bequal(left, right);</pre><br />
Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função.<br />
Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado.<br />
<br />
=== '''Dual''' ===<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=536Álgebra Booleana2016-05-26T18:04:51Z<p>Clah: /* Representando Funções Booleanas */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
=== '''Dual''' ===<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clahhttp://carol.dimap.ufrn.br/logicwiki/index.php?title=%C3%81lgebra_Booleana&diff=535Álgebra Booleana2016-05-26T18:02:18Z<p>Clah: /* Representando Funções Booleanas */</p>
<hr />
<div>Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados.<br />
Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você).<br />
<br />
<br />
== '''Funções Booleanas''' ==<br />
<br />
No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante.<br />
<pre>a := true;<br />
b := false;</pre><br />
Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. <br />
Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo:<br />
<pre>true and false;<br />
true &and false;</pre><br />
O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor.<br />
Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''')<br />
<pre>a := 'a';</pre><br />
Enquanto<br />
<pre>not not a;</pre><br />
é um código ''Maple'' perfeitamente válido, a construção<br />
<pre>&not &not a;</pre><br />
conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <br />
<pre>&not (&not a);</pre><br />
provendo nível extra com os parênteses.<br />
O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''.<br />
<pre>true and true;<br />
true and false;<br />
false and true;<br />
false and false;</pre><br />
Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato.<br />
Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!)<br />
O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos.<br />
<pre>not true;<br />
not false;</pre><br />
Os resultados não são muito surpreendentes.<br />
Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''.<br />
A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor.<br />
Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como<br />
<pre>a and b and c and d;</pre><br />
são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''.<br />
Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <br />
<pre>not not a;</pre><br />
Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira.<br />
Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. <br />
Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão<br />
<pre>&not( &not ( a ) );</pre><br />
Não é simplesmente '''a'''.<br />
<br />
=== '''Um Avaliador Booleano''' ===<br />
<br />
Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple.<br />
<pre>evalb(2 = 3);<br />
evalb(3.14 = 3.14);</pre><br />
É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple.<br />
<br />
Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal.<br />
<br />
=== '''Representando Funções Booleanas''' ===<br />
<br />
Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como:<br />
<br />
''F(z,y,z) = xy + yz + zx''<br />
<br />
pode ser escrita no Maple com o seguinte procedimento<br />
<pre>F := proc(x, y, z)<br />
RETURN((x and y) or (y and z) or (z and x));<br />
end:</pre><br />
A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a sum é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como, então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajuda na leitura do programa. (Porque parênteses bem usados nunca doem).<br />
<br />
=== '''Verificando Identidades Booleanas''' ===<br />
=== '''Dual''' ===<br />
=== '''Forma Normal Disjuntiva''' ===<br />
== '''Representação de Funções Booleanas''' ==<br />
== '''Minimização de Expressões Booleanas e Circuitos''' ==<br />
== '''Condições Indiferentes''' ==<br />
== '''Cálculos e Explorações''' ==<br />
== '''Exercícios e Projetos''' ==</div>Clah