Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
de Dados
Emerson Shigueo Sugimoto1, Adolfo Neto1
1
Departamento Acadêmico de Informática – Universidade Tecnológica Federal do
Paraná (UTFPR)
Caixa Postal 80230-901, nº 3165 – Curitiba – PR – Brasil
eme.vbnet@gmail.com, adolfo@utfpr.edu.br
Abstract. This paper presents and performs the analysis of different forms of
representation of propositional logic formulas via data structures created
through a language of object-oriented programming, specifically the language
used in this work has been Java. The main goal is to represent logical
formulas with a low memory footprint, efficiently from a computational
standpoint.
1. Introdução
A representação de fórmulas lógicas de uma forma eficiente do ponto de vista
computacional é de fundamental importância para provadores de teoremas, por eficiente
subentende-se o baixo consumo de memória e o menor tempo utilizado na construção
destas fórmulas. Quanto menor o consumo de memória e tempo de criação, maior será o
poder computacional de um provador de teoremas, visto que ele necessita representar
dezenas, centenas ou milhares de fórmulas lógicas durante a sua execução.
Este trabalho tem por objetivo apresentar e analisar soluções computacionais
para representação de fórmulas lógicas proposicionais através de uma estrutura de dados
criada através de uma linguagem de programação orientada a objetos. Para tanto foram
analisados os consumos de memória e tempo de execução de todas as soluções.
2. Materiais e Métodos
Foram desenvolvidas quatro soluções para representação das fórmulas e também
foi feita a análise da representação de fórmulas apresentada no provador de teoremas
KEMS [6], totalizando-se cinco soluções analisadas. Os testes foram realizados em uma
máquina com 512 MB de memória RAM, processador AMD Athlon(tm) 64 3000+ e
sistema operacional GNU/Linux versão 5.0, disponibilizada pela Universidade
Tecnológica Federa do Paraná (UTFPR).
A análise de consumo de tempo foi feita pelo software perf [7], ele permite o
monitoramento de uso de recursos ou eventos acionados à partir da execução de um
processo. Ele realiza uma análise de performance de softwares que funcionam sob o
sistema operacional Linux.
Para o controle de versões das soluções propostas, foi utilizado o sistema de
controle de versões Git [1]. Além disso, o código foi disponibilizado no site Github [2],
que permite de forma gratuita a hospedagem de arquivos e o controle de versões de um
software de forma online e entre diversos colaboradores. As soluções desenvolvidas
estão disponíveis através do link git@github.com:surfx/IC.git [3].
Para análise e testes das soluções apresentadas, a tecnologia JUNIT [5] de testes
padronizados em Java foi utilizada.
Uma fórmula da lógica proposicional pode ser atômica ou uma fórmula composta,
sendo que uma fórmula composta pode formada de fórmulas atômicas ou de outras
fórmulas compostas. Toda fórmula atômica possui um identificador, em nossas
soluções, o identificador é representado através de uma String, assim "A" ou "B3_5" são
exemplos de identificadores. Toda fórmula composta tem um conectivo e zero ou mais
subfórmulas, que podem ser fórmulas atômicas ou outras fórmulas compostas. Todo
conectivo possui uma aridade (um número inteiro maior ou igual a zero), assim a
aridade do conectivo determina a quantidade de subfórmulas, isto é, se em uma fórmula
composta o conectivo tem aridade três, esta fórmula terá três subfórmulas.
Exemplos de fórmulas:
A&B, conectivo: &, aridade: 2. Subfórmulas: A, B
A&(B&C), conectivo: &, aridade: 2. Subfórmulas: A, B&C
!A, conectivo: !, aridade: 1. Subfórmulas: A
TOP, conectivo: TOP, aridade: 0. Subfórmulas: 0
Exemplos de conectivos:
Zero-ários: TOP (veracidade), BOTTOM (falsidade).
Unários: ! (negação, não), @ (consistência), * (inconsistência).
Binários: & (conjunção, e), | (disjunção, ou), -> (implicação; se, então).
3. Resultados e Discussão
O diagrama de classes da solução 1 esta representado na Figura 1. Esta solução
utiliza uma estrutura de dados chamada lista ligada (linked list) de objetos que
implementem a interface IFormula para armazenar as subfórmulas. Assim o objeto
FormulaComposta, que implementa a interface IFormula, possui um objeto conectivo, e
este por sua vez possui a lista de subfórmulas, que podem ser fórmulas atômicas ou
outras fórmulas compostas. O objetivo da criação da lista ligada era o de eliminar a
possibilidade de ponteiros de objetos IFormula nulos, nos casos de conectivos zero-ários
e unários que poderiam existir no objeto Conectivo.
A solução 2 (diagrama de classes representado na Figura 2) especifica mais os
conectivos do que a solução 1, nesta solução a interface IConectivo é especificada em
outras interfaces, a IConectivoUnario e IConectivoBinario, e a interface IFormula é
especificada em outras interfaces, a interface IFormulaComposta e IFormulaAtomica,
eliminando-se desta forma a necessidade da criação de uma lista encandeada para
armazenamento de fórmulas por parte do objeto Conectivo, da solução 1.
Semelhantemente à solução 1, a classe FormulaComposta e FormulaAtomica possuem
um conectivo, e este, que pode ser unário, binário ou zeroário, contém as subfórmulas.
System.out.println("-(f)------------------------");
}
Para cada solução foi criado um arquivo .jar executável para análise de tempo de
execução através do software perf [7], através do comando: perf stat java -jar
ARQJAR.jar.
O resultado das comparações feitas através do perf [7] esta na Tabela 1 e na
Figura 7. A solução 4 apresentada pelo software KEMS [6] não conseguiu terminar sua
execução e o tempo tomado foi o de 22,83263 segundos pelo software perf [7].
A análise de memória consumida foi feita através da criação diferentes
quantidades de fórmulas pelas estruturas sugeridas em cada solução, inicialmente para 1,
5, 30, 50, 2000, 10000, 200000 e 1000000 de fórmulas. A criação destas fórmulas é
igual à criação das fórmulas apresentadas na Figura 6, a única diferença é o número de
fórmulas criadas.
O resultado desta análise esta representada na Figura 8. Observa-se que a
solução 4 conseguiu executar até a criação de 30 fórmulas e usou uma quantidade de
7,69E+007 bytes de memória nesta tarefa.
Tabela 1. Comparações das soluções através do perf [7] *
Soluções Sol1 Sol2 Sol3 Sol 4* Sol 5
Tempo de Execução 2,508751868 0,806576171 1,36524 22,83263 0,570379
Tarefas de Relógio 2400,326848 762,173346 1232,037 21133,91 517,474
Trocas de Contexto 499 238 537 11622 262
Migração de CPU 0 0 0 0 0
Faltas de Página 41987 13682 18133 82811 10437
Ciclos 0 0 0 0 0
Instruções 0 0 0 0 0
Referências à cache 0 0 0 0 0
*solução 4 não chegou a terminar a execução;
Através desta análise, observa-se que a solução 2 foi mais eficiente que a solução
1, principalmente devido ao uso de listas ligadas pela solução 1, que onerou
consideravelmente o seu tempo de execução e uso da memória.
A solução 2 foi mais eficiente que a solução 3, devido ao uso de uma lista
encadeada pela solução 3, para criar um repositório de identificadores de fórmulas
lógicas (A1, A2.., ||, &&..), transformando estas strings em números do tipo short.
A solução 5 foi mais eficiente do que todas as outras soluções propostas, isto
ocorreu principalmente pela especificação da interface IFormula, que é especificada em
4 outras interfaces, a interface IFormulaUnaria, IFormulaBinaria, IFormulaAtomica e
IFormulaZeroaria, que eliminam a necessidade de uma lista para armazenar as
subfórmulas como na solução 4 e solução 1, diminuindo-se desta forma o consumo de
memória. Esta especificação é vantajosa à medida que cria-se uma estrutura de dados e
alocam-se recursos compatíveis à real necessidade das fórmulas, sejam compostas ou
atômicas. A eliminação das classes e interfaces relacionados aos conectivos também foi
um ganho do ponto de vista computacional, pois reduz-se estas estruturas da
representação de fórmulas, na solução 5, os conectivos são representados através de
enums (classe EnumsConectivos), ao invés de identificadores do tipo Strings da
estrutura de conectivos, além da própria instanciação dos objetos conectivos.
Um dos motivos que oneravam a execução da solução 4 é a classe Arity (Figura
7), que cria uma instância dela mesma para cada aridade diferente, já na solução 5, a
aridade é representada através de enums, que utilizam menos memória do que uma
classe. Outro ponto importante era que a solução 4 utiliza uma lista (objeto List no Java)
para armazenar as subfórmulas, o que também onera o consumo de memória.
5. Referências
[1] GIT. Disponível em: <http://git-scm.com/>. Acesso em 29 mar 2011.
[2] GITHUB. Site que oferece um serviço online gratuito de hospedagem de
arquivos e de controle de versões. Disponível em: <http://github.com/>. Acesso
em: 29 mar 2011.
[3] GIT Repositório. Repositório das soluções desenvolvidas. Disponível em:
<git@github.com:surfx/IC.git>. Acesos em: 29 mar 2011.
[4] JAVA. Linguagem de Programação. Disponível em:
<http://java.com/pt_BR/about/>. Acesso em: 29 mar 2011.
[5] JUNIT. Testes automatizados em Java. Disponível em: <http://www.junit.org/>.
Acesso em: 29 mar 2011.
[6] KEMS. Provador de teoremas baseado KE Multi-Estratégia. Disponível em:
<http://adolfoneto.wikidot.com/KEMS>. Acesso em: 29 mar 2011.
[7] PERF. Software que realiza análises de performance de softwares que
funcionam sob o sistema operacional Linux. Disponível em:
<https://perf.wiki.kernel.org/index.php/Main_Page>. Acesso em: 29 mar 2011.