terça-feira, 20 de novembro de 2012

SQL Gaussiano

É bem conhecida a história de quando o professor de matemática de Gauss pediu que a turma somasse todos os números até 100. O mestre esperava ter um pouco de paz enquanto as crianças trabalhassem na soma. O pequeno Gauss, no entanto, resolveu a questão rapidamente.

A solução era simples. Ele somou 1 com 100, depois 2  com 99, então 3 com 98 e assim por diante. Vindo pelas pontas, a soma é sempre 101 e depois de 50 somas, os números encontram-se em 50 e 51. O total, portanto, é 50 vezes 101.

Esse pequeno truque pode ser usado no SQL para encontrar sequências de números ou de datas. Considere os seguintes números: 1; 2; 3; 5; 6; 8; 9; e 10. Se cada um estiver numa linha de uma tabela, eu quero uma consulta que gere o seguinte resultado:

   1   3
   5   6
   8 10

É necessária uma sequência decrescente para fazer o jogo com a nossa sequência crescente. Isso podemos resolver facilmente com funções analíticas. Na consulta abaixo, já somo cada número ao seu equivalente.


select n, n+row_number() over (order by n desc)
from numeros
order by n


Isso vai produzir as seguintes linhas:

19
29
39
510
610
811
911
1011

E todos os números consecutivos compartilham o mesmo valor na segunda coluna. O que nos resta é agrupá-los e encontrar o máximo e mínimo de cada grupo. Assim, chegamos ao nosso objetivo:


select min(n), max(n) from (
  select n, n+row_number() over (order by n desc) grupo
  from numeros
)
group by grupo
order by 1


O resultado é, conforme esperado, este:

   1   3
   5   6
   8 10

O mesmo pode ser feito com datas, basta tratar cada uma como o número de dias a partir da primeira data.

terça-feira, 13 de novembro de 2012

Nau em chamas

Quase sem querer, encontrei um fractal curioso chamado Burning Ship. Ele é uma variação do conjunto de Mandelbrot; a única diferença é que, a cada iteração, são aplicados os valores absolutos dos componentes imaginário e real.

Aproveitei o código de aventuras anteriores. Com o Javascript abaixo é possível analisar este fractal; basta clicar sobre a imagem para inspecionar regiões cada vez menores.

<html>
  <head>
  </head>
  <body>
    <canvas id="canvas" width="256" height="256"></canvas>
    <script>

function mandel(x,y) {
  var c=1;
  var r=0;
  var i=0;
  var MAXITER=512;
  while(r*r+i*i<4 && c<MAXITER) {
    var t=2*Math.atan2(i,r);
    var d=Math.pow(Math.sqrt(r*r+i*i),2);
    var nr=d*Math.cos(t)+x;
    var ni=d*Math.sin(t)+y;
    r=Math.abs(nr);//Aqui ocorre
    i=Math.abs(ni);//a mágica
    c+=1;
  }
  return c;
}

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var imageData=ctx.getImageData(0,0,256,256);
   
var minx=-2;
var miny=-2;
var maxx=2
var maxy=2;
   
  
function plot() {
  var deltax=(maxx-minx)/256;
  var deltay=(maxy-miny)/256;
 
  for(var x=0; x<256; x++) {
    for(var y=0; y<256; y++) {
      var c=mandel(minx+x*deltax, miny+y*deltay);
      var red=(c*8)%255;
      var green=(c*9)%255;
      var blue=(255-c*4)%255;
      var index=(x+y*imageData.width)*4;
      imageData.data[index+0]=red;
      imageData.data[index+1]=green;
      imageData.data[index+2]=blue;
      imageData.data[index+3]=0xff;
    }
  }
  ctx.putImageData(imageData,0,0);
}
plot();
   
function zoom(e) {
  var x=minx+(maxx-minx)*((e.clientX-canvas.offsetLeft)/256);
  var y=miny+(maxy-miny)*((e.clientY-canvas.offsetTop)/256);
  var dx=(maxx-minx)/4;
  var dy=(maxy-miny)/4;
  
  minx=x-dx;
  maxx=x+dx;
  miny=y-dy;
  maxy=y+dy;
  plot();
}
canvas.onclick=zoom;
   
    </script>
  </body>
</html>

Como se espera de um fractal, o tema torna a surgir com formas e cores ligeiramente diferentes.

segunda-feira, 12 de novembro de 2012

O iPad mini e o novo consumidor de TI

Observando alguns comentários e algumas avaliações do novo iPad mini eu percebi um mudança radical em relação aos consumidores de micros de 8 bits na década de 1980. O novo comprador de TI quer a perfeição e não se importa em gastar muito dinheiro para ter o melhor para cada situação.

O novo iPad mini, dizem, é muito melhor para carregar pela rua e ler no ônibus. Os modelos anteriores são grandes e pesados. Mas há pouco tempo tudo era perfeito; surge agora um novo modelo mais perfeito ainda.

O comprador de micros de 8 bits era um sonhador. Ele tinha que enxergar muito longe para ver algum potencial naquelas máquinas lentas e com pouquíssima memória. Os jogos exigiam muita criatividade tanto dos programadores como dos jogadores.

Junto com essa mudança nas expectativas, sumiram aquelas revistas legais para amadores, a Byte sendo a mais interessante (embora um pouco mais técnica que as outras). Hoje em dia temos revistas para profissionais (sobre Java, SQL, e outros assuntos bem específicos) e algumas revistas de consumidor com avaliações superficiais (como a Info Exame).

Eu sinto falta daquelas revistas, porque sempre abriam portas para áreas novas: gerar fractais, simular colisões de galáxias, fazer música. Parece que agora que o micro é poderoso, as pessoas não se preocupam em explorá-lo.

Torço que com o lançamento dessas plataformas pequenas, como o Raspberry Pi, voltem a surgir publicações para amadores inquisitivos. Enquanto isso, vou vasculhando as revistas antigas por projetos interessantes.

terça-feira, 23 de outubro de 2012

Contando linhas com Perl

Eu queria contar as linhas de um arquivo de uns 4GB no Windows. No Linux, o wc resolveria a questão com sobra. Eu tentei usar o GNU Coreutils, mas não achei uma versão binária para o Sistema Operacional de Redmond.

Então, recorri ao camelo e com uma linha de Perl, resolvi o problema:

C:\perl -pe "}{$_=$." meu_arquivo_grande.txt
8738433

Não podia ser mais simples e claro.

quarta-feira, 17 de outubro de 2012

Relatórios no Oracle

Considere uma tabela PESSOAS com estas colunas:

  (NASC DATE, SEXO VARCHAR2(1), NOME VARCHAR2(100))

A coluna NASC guarda a data de nascimento e a coluna SEXO pode conter 'M' ou 'F'. Se eu quiser montar um relatório com o número de aniversários para cada dia do ano, vou ter que fazer uns contorcionismos, mas se eu quiser, ademais, separar o número de aniversários por sexo, a consulta vai ficar ilegível.

Escondido na documentação da Oracle estava um tipo de join que permite resolver esse problema com relativa facilidade: o partitioned outer join. Esse join tem a seguinte forma:

select *
from a 
     left outer join b partition by (cz) on a.ca=b.ca

E isso significa que será executado um cross-join dos valores distintos de CZ com as linhas da tabela A e, a seguir, o resultado desse cross-join será usado no outer-join com a tabela B.

De volta ao problema original, inicio com uma contagem dos aniversários por dia e por sexo:

  select
    to_char(nasc,'MMDD') nasc, 
    sexo, 
    count(1) total
  from pessoas
  group by to_char(nasc,'MMDD'), sexo

Além disso, preciso de uma tabela com todos os dias do ano:

  select to_char(trunc(sysdate, 'YYYY')+rownum-1, 'MMDD') dia
  from all_tables
  where rownum<367

E junto tudo com o partitioned outer join:

select dia, sexo, nvl(total, 0)
from (
  select to_char(trunc(sysdate, 'YYYY')+rownum-1, 'MMDD') dia
  from all_tables
  where rownum<367
) dias
  left outer join (
    select to_char(nasc,'MMDD') nasc, sexo, count(1) total
    from pessoas
    group by to_char(nasc,'MMDD'), sexo) p partition by (sexo) on p.nasc=dia
order by dia, sexo

Essa consulta produz duas linhas para cada dia do ano: uma para o número de aniversários de homens e outro para o número de aniversários de mulheres:

0101F2
0101M0
0102F1
0102M3

Armado desta nova ferramenta, decidi resolver um problema ainda maior: enumerar os aniversariantes de cada dia. Comecei com uma lista dos aniversariantes para cada dia:

  select nasc, sexo, substr(SYS_CONNECT_BY_PATH(nome,', '),3) nomes  
  from (
    select 
      nasc, nome, sexo,
      row_number() over (partition by nasc, sexo order by nome) ordem,
      row_number() over (partition by nasc, sexo order by nome desc) medro 
    from (
      select to_char(nasc, 'MMDD') nasc, sexo, nome from pessoas
    ) 
  )
  where medro=1
  start with ordem=1
  connect by prior nasc=nasc and prior sexo=sexo and prior ordem=ordem-1

Juntando com a lista dos dias do ano, descubro o seguinte:

select dia, sexo, nvl(nomes,'-') nomes
from (
  select to_char(trunc(sysdate, 'YYYY')+rownum-1, 'MMDD') dia
  from all_tables
  where rownum<367
) dias
  left outer join (
    select nasc, sexo, substr(SYS_CONNECT_BY_PATH(nome,', '),3) nomes  
    from (
      select 
        nasc, nome, sexo,
        row_number() over (partition by nasc, sexo order by nome) ordem,
        row_number() over (partition by nasc, sexo order by nome desc) medro 
      from (
        select to_char(nasc, 'MMDD') nasc, sexo, nome from pessoas
      ) 
    )
    where medro=1
    start with ordem=1
    connect by prior nasc=nasc and prior sexo=sexo and prior ordem=ordem-1
  ) p partition by (sexo) on p.nasc=dia
order by dia, sexo

Não é das coisas mais simples, mas ele produz bastante para o seu tamanho. O resultado é parecido com:

0101FHelena, Maria
0101M-
0102FFernanda
0102MHenrique, João, Pedro

Por enquanto, apenas o Oracle oferece esse join.

quinta-feira, 11 de outubro de 2012

Dialética do software corporativo

Durante a implementação de uma série de regras de negócio ocorreu-me que seria muito mais interessante desenvolver software como uma ferramenta: o usuário que decida como usá-lo e o usuário que decida quais são as formas válidas de usá-lo.

Os usuários já se permitem pequenas transgressões como escrever nomes próprios em caixa alta e sem acentos. Na língua portuguesa, só há uma maneira de escrever João da Silva (exatamente essa). Tanto "joao da silva" como "JOAO DA SILVA" ou "XOAU d4 $y)v4" estão errados. Mas como não é prático implementar um conjunto de regras para validar os nomes próprios, essa tarefa raramente é ensaiada, embora frequentemente seja contemplada (principalmente quando alguém quer um relatório esteticamente aceitável).

Esse objetivo pode ser abandonado porque é claramente inatingível, mas por que o software continua sendo escrito sob a ilusão de que é possível (e desejável) limitar o usuário apenas a interações válidas (conforme o arcabouço vigente no momento de sua concepção)?

No momento em que o ambiente do software muda (muitas vezes antes que ele entre em produção), as regras de negócio precisam ser reavaliadas e recodificadas.

Creio que a resposta esteja na dinâmica das empresas. A criação de regras rígidas para a operação de sistemas está assentada sobre as disputas de poder. O analista de sistemas não tenta apenas sintetizar as regras de negócio, ele também frequentemente testemunha o processo de disputa de poder no qual duas ou mais partes tentam impor suas visões de mundo não com o intuito de produzir o melhor para a corporação, mas para validar e avançar seu poder.

Pode-se até dizer que alguns softwares incorporam as contradições internas de suas respectivas corporações e que num momento de realinhamento de forças eles serão, inevitavelmente, redesenhados.

Além disso, existe a falta de confiança explícita no proletariado da empresa. Já se espera que o usuário final vá subverter as regras usando o sistema de forma errônea e que, como acontece com frequência, a culpa será atribuida ao sistema. E isso é visto como natural, mesmo quando for provado que o usuário agiu de má-fé (ninguém culpa o fabricante de martelos quando o marceneiro erra o prego e quebra algo). Por isso, o próprio proletariado abdica de assumir responsabilidade pelo negócio e o delega todo ao sistema (já tratam o trabalhador com desconfiança desde o início e ele sabe igualmente que pode tercerizar qualquer culpa ao software).

Ocorre também que um proletário iluminado tente impor novas regras como forma de abraçar o sistema, tomar posse dele e do poder que ele representa. Já testemunhei, por exemplo, uma tentativa de eliminar colunas desnecessárias de um relatório que, no entanto, eram importantes para outras pessoas. Se o relatório fosse visto apenas como uma ferramenta, as colunas extras poderiam muito bem ser ignoradas, mas a posse do relatório servia também como delimitação de território. Evidentemente, esse tipo de atitude representa uma traição do trabalhador à sua própria classe, numa tentativa de emular a classe dominante e, quem sabe, um dia ser aceito nela.

As metodologias de gerência de projetos apontam como importante a definição dos stakeholders, porque as empresas de software já identificaram o problema dos conflitos internos e de como eles atrapalham o processo de análise. Para a fábrica de software, não é realmente importante qual regra de negócio será implementada, desde que algum representante do cliente aceite a responsabilidade de assinar o cheque.

O que precisamos agora é introduzir o marxismo nos cursos de informática.

terça-feira, 25 de setembro de 2012

Os números da Revolução Farroupilha

As minhas aulas de história no primário e no secundário sofriam de uma falta grave: a falta de números. Sem agregar dados aos acontecimentos históricos, as coisas ficam muito nebulosas.

Já que estamos num setembro mui chuvoso e a gauchada está mais faceira que sapo em banhado, resolvi apontar uns números da Revolução Farroupilha.

Em 1835, a população do estado era de apenas 170 mil. É menos de um paisano por quilômetro quadrado (0,6 para ser mais preciso). Porto Alegre tinha 12 mil habitantes. Imagino que muita gente não tenha tido notícia dos acontecimentos.

Morreram cerca de 3.400 combatentes nos quase 10 anos de luta (em média, menos de 1 por dia). A taxa de mortalidade era de cerca de 30 por mil. Logo, seriam esperadas 5.100 mortes por ano; a guerra, portanto, adicionou cerca de 6% à taxa de mortalidade.

Lutaram mais de 100.000 soldados (pelo menos 40 mil farrapos e pelo menos 60 mil imperiais). Portanto, a taxa de mortalidade da guerra era pouco mais que um décimo da taxa da população em geral. É provável que tenha sido mais seguro lutar que ficar em casa comendo churrasco.

Claro, a taxa de mortalidade era impulsionada muito mais por crianças e por idosos que por homens em idade para lutar, mas as proporções são curiosas e um pouco cômicas (os falecidos talvez não enxerguem humor neste dado).

A história do Rio Grande não me comove muito, então proponho diminuir um pouco o tom ufanista do hino trocando "sirvam nossas façanhas de modelo a toda terra" por algo mais proveitoso comercialmente. Por exemplo, "sirvam nossas picanhas de modelo a toda terra".

sexta-feira, 14 de setembro de 2012

iMarx

O novo iPhone esteve em todos os meios de comunicação na última semana. A primeira aparição foi num jornal da Globo. Logo imaginei que fosse uma notícia paga, mas no dia seguinte ele estava no jornal Metro. E, logo depois, também na BBC. É mais um telefone numa sequência evolutiva sem fim dos circuitos, então por que ele mereceu tanta cobertura?

Marx teve uma ideia interessante sobre as relações entre as pessoas e os objetos, o fetichismo da mercadoria: as relações entre objetos sobrepõem-se às relações entre as pessoas.

Só nesse contexto é que tem sentido os meios de comunicação fazerem propaganda gratuita (se não for, o problema é criminal) para um produto que, convenhamos, é só um pouco melhor que o anterior. Eu tenho umas revistas de computação da década de 1980 e nelas há umas propagandas engraçadas sobre micros novos com 64KB de RAM quando o anterior tinha apenas 32KB.

Lendo um pouco mais sobre o conceito, encontrei umas pérolas extras. As mercadorias adquirem vida própria e determinam as relações entre as pessoas de tal maneira que para quem o trabalhador vende sua mão-de-obra também passa a ser importante e determinante de sua colocação na escala social. Não é cômico agora a maneira como trabalhar para a Dell ou a Google é mais bacana que para uma empresa local?

Além disso, as pessoas assumem papéis (usam máscaras) conforme a função que exercem no mercado de trabalho. Vendedores usam terno e gravata, programadores JavaScript são engraçadinhos, funcionários de multinacionais são descolados, gerentes trabalham muitas horas, etc.

A Apple conseguiu até que ficar na fila esperando o novo produto se tornasse um evento social. A manipulação é óbvia, mas o júbilo dos participantes parece sincero. Logo, as outras empresas vão começar a copiar a Apple e sua deificação dos objetos. Eu imagino que vá ocorrer uma reação (eu já não acho graça nessa brincadeira) e as pessoas terão que encontrar maneiras genuínas de interação.

terça-feira, 4 de setembro de 2012

BrazilJS 2012

Porto Alegre hospedou a maior conferência de Javascript do Universo: a BrazilJS 2012. Realmente, eu duvido que outra civilização avançada criaria um monstrinho como o Javascript. Então, não acho que os organizadores tenham exagerado no título.

Estava presente Brendan Eich, criador da linguagem. Ele declarou-se fã do Scheme e revelou que fora coagido a criar algo parecido com Java. Aparentemente, há também quem pense que Java é uma boa idéia. Pelo menos, ele teve a clarividência de agregar funções de primeira classe.

Foi bom ver um evento deste porte em Porto Alegre, mesmo que as palestras não tenham sido tão interessantes (foram cansativas as tentativas reiteradas dos palestrantes de serem engraçadinhos). Ademais, foi uma boa oportunidade de conhecer o teatro do Bourbon Country; essa experiência eu repeti no domingo com o excelente espetáculo Música de Brinquedo do Patu Fu.

A segunda-feira trouxe de volta a realidade dura do mundo Web. Ao otimizar um script, percebi que o Firefox não implementa a propriedade innerText no DOM. Em seu lugar está a textContent. O meu problema era exibir textos obtidos via JSON que contêm sequências especiais de HTML, como "a&ccedil;&atilde;o".

Uma maneira simples de resolver isso é inserir o texto numa div via o innerHTML e depois recuperá-lo com innerText (ou textContent).

Aproveitando as facilidades herdadas do Scheme, escrevi a seguinte solução que, usando o jQuery, cria um div e uma função para executar a inserção do HTML e a recuperação do texto:

var _unescape=function() {
  var scratch=document.createElement('div');
  return function(s) {
    return $(scratch).html(s).text();
  }
}();

alert(_unescape('Javascript n&atilde;o &eacute; t&atilde;o ruim, afinal.'));

Graças ao jQuery, não preciso me preocupar com as diferenças entre os navegadores.

sexta-feira, 10 de agosto de 2012

Geodemografia meridional analítica

Ignore um pouco o brilhantismo do título e considere esta tabela:

CREATE TABLE MUNICIPIOS (NOME VARCHAR2(50), POPULACAO INTEGER)

Com os dados populacionais dos municípios gaúchos, exercitei um pouco as funções analíticas do Oracle (ou do Postgresql). Temos atualmente 496 municípios (em breve, com o retorno de Pinto Bandeira, serão 497). Em primeiro lugar, enumerei os municípios por ordem decrescente de população e, usando uma função analítica, adicionei uma coluna com a soma de todos os municípios com população menor que o corrente.

select nome, 
       populacao, 
       sum(populacao) 
         over (order by populacao asc rows unbounded preceding)
from municipios
order by populacao desc

Isso nos ajuda a descobrir que os 282 menores municípios têm o primeiro milhão de habitantes. O próximo milhão habita 98 municípios. O terceiro, 45 municípios. O quarto, 28. O quinto, 16. O sexto, 11. Transformar a soma em percentil é fácil (para quem tem senso prático, pelo menos):

select nome, 
       populacao, 
       sum(populacao/10732770) 
         over (order by populacao asc rows unbounded preceding)
from municipios
order by populacao desc

Os 22 maiores municípios (4% do total) são ocupados por 50% da população gaúcha. E os 55 menores municípios (11% do total) têm apenas 1% da população. Os 293 menores municípios (59% do total) têm apenas 10% da população. A média por município é de 21.639 habitantes, mas apenas 76 têm mais que isso (15%). A mediana é de apenas 5.697 habitantes. Se estipulássemos 6 mil habitantes (metade da capacidade da Baixada Melancólica, sede do glorioso Inter de Santa Maria) como o mínimo necessário para constituir um município, restariam apenas 236.

quinta-feira, 5 de julho de 2012

Análise dos salários da União II

Durante a noite consegui carregar mais alguns dados e atingi o total de 29.795 contracheques, dos quais 27.375 têm valor maior que zero. Isso representa 5,2% do total. Tenho que estudar um pouco de estatística para poder derivar alguma conclusão sobre as propriedades dessa amostra, mas acho que os 2,6% já me bastavam.

Então, vou comparar os dados anteriores com os novos:
  • A média saltou de R$6.519,22 para R$6.608,88 (pouco mais que 1% de diferença);
  • A mediana subiu para R$4.384,67 para R$4.837,66 (0,06% de diferença!);
  • Os 10% maiores ganham a partir de R$13.611,62 (3% sobre os R$13.184,85 anteriores) e
  • O coeficiente de Gini mudou de 0,368 para 0,374.
Os salários abaixo de R$2.000,00 representam 4,2% do total. Os menores de R$3.000,00 são 21%.
Separei os salários em faixas de R$2.000,00 para melhor visualizar a distribuição:

Porcentagem na faixaPorcentagem acumuladaFaixa
4,26%4,26%R$1 a R$2.000
35,5%39,76%R$2.001 a R$4.000
19,29%59,05%R$4.001 a R$6.000
13,55%72,60%R$6.001 a R$8.000
8,45%81,05%R$8.001 a R$10.000
6,22%87,27%R$10.001 a R$12.000
3,11%90,38%R$12.001 a R$14.000
2,55%92,93%R$14.001 a R$16.000
2,28%95,21%R$16.001 a R$18.000
3,02%98,23%R$18.001 a R$20.000
0,95%99,18%R$20.001 a R$22.000
0,60%99,78%R$22.001 a R$24.000
0,13%99,91%R$24.001 a R$26.000
0,06%99,97%R$26.001 a R$28.000
0,01%99,98%R$28.001 a R$30.000
0,01%99,99%mais de R$30.001

As porcentagens não somam 100% porque foram arredondadas. A última faixa (mais de R$30.001) representa apenas 4 pessoas (0,0146%).

quarta-feira, 4 de julho de 2012

Análise dos salários da União

Há poucos dias a União publicou sua folha salarial no portal da transparência. É mais de meio milhão de contracheques de servidores federais. Acho que publicar os nomes foi um pouco excessivo e desnecessário; ao mesmo tempo acho que faltaram informações mais interessantes para a sociedade.

Então, armado de um script em Perl, procurei levantar as informações que me parecem importantes. Usei o módulo WWW::Mechanize para percorrer as páginas e processá-las, já que a União não foi transparente o suficiente para publicar uma planilha, um XML, ou mesmo um arquivo-texto. Não vou publicar o código para não facilitar a vida de pessoas com objetivos menos nobres que os meus.

Minha estimativa inicial era a de que seriam necessárias 32 horas para processar todas as páginas, mas logo minhas conexões começaram a cair, mesmo eu sendo um bom cidadão, abrindo apenas uma conexão por vez.

Com apenas dados de 14.885 servidores federais (2,6% do total de 569,6 mil), pensei ter o suficiente para já calcular algumas estatísticas. Desses, 1.209 tinham salário zero no mês e eu os ignorei.

A média é de R$6.519,22, mas a mediana é de apenas R$4.834,67 (ou seja, metade ganha menos que isso e metade ganha mais). O coeficiente de Gini é quase 0,368. Isso é bastante melhor que o valor nacional de 0,519, mas ainda é pior que o de países mais avançados (o da França é 0,289 e o da Alemanha é 0,27).

Para complementar os salários dos que ganham menos de R$2.000,00, seria necessário retirar apenas 1% dos salários dos que estão entre os 10% maiores (são os que ganham mais de R$13.184,85). Para que ninguém recebesse um salário bruto menor que R$3.000,00, seria necessário retirar apenas 7% dos salários mais altos. Eu não considerei as cargas horárias, mas achei que 82% dos que ganham menos de R$2.000,00 fazem 40 horas semanais, assim como 86% dos que ganham menos de R$3.000,00.

Os 10% maiores salários correspondem a 27% do total da folha. Os 10% menores correspondem a pouco menos que 3% da folha. A média de IRPF é de 9,3% do salário bruto; considerando somente os que pagam IRPF, a média sobe apenas para 10%. Isso não chega a surpreender, porque apenas 7% não pagam imposto de renda. Os 10% maiores salários contribuem com 40% do total de IRPF pago, descontando, em média, 19,7% do valor bruto.

Faltam informações sobre idade e sexo dos funcionários. Evidentemente, publicar essas informações seria uma inequívoca invasão de privacidade e não espero que o façam, mas torço que comecem a compilar estatísticas para melhor informar a sociedade. Assim como foram publicados os dados, não vejo grande utilidade neles, exceto para encontrar uma ou outra ilegalidade que tenha escapado ao TCU. Ou então para propiciar algumas horas de diversão para um velho programador de Perl.

quinta-feira, 14 de junho de 2012

Combinações de dígitos III

Tendo já explorado as relações de conjuntos e combinações com números binários, resolvi descobrir o que podia ser feito a respeito de permutações. Infelizmente, os números binários não ajudaram muito, mas, em compensação, descobri uma propriedade interessante de uma base alternativa: a base fatorial, ou fatorádica.

A representação de um número na base fatorádica pode ser diretamente relacionada à i-ésima permutação de um conjunto, como se verá adiante. Em primeiro lugar, procurei escrever uma função para contar nessa base.

sub factoradics {
  my $n=shift;
  my $callback=shift;
  my @rest=@_;
  
  if($n==1) {
    &$callback(@rest,0);
  } else {
    factoradics($n-1, $callback, @rest, $_) for 0..$n-1;
  }
}

Essa função recebe dois parâmetros: n para o número de dígitos (que corresponderá aos número de elementos no conjunto que sofrerá as permutações) e callback para a função que será invocada a cada iteração.

A função tem uma lógica simples: na n-ésima posição, repetem-se (n-1)! vezes os digítos até o valor n-1 e, a cada iteração, faz-se o mesmo para as posições anteriores. Então, na quarta posição, repetimos 6 vezes (3!) cada um dos dígitos de 0 a 3. Na primeira posição, os dígitos são sempre iguais a zero.

Um pequeno teste mostra o que ela produz:

factoradics(4, sub {
  print "@_\n";
});

0 0 0 0
0 0 1 0
0 1 0 0
0 1 1 0
0 2 0 0
0 2 1 0
1 0 0 0
1 0 1 0
1 1 0 0
1 1 1 0
1 2 0 0
1 2 1 0
2 0 0 0
2 0 1 0
2 1 0 0
2 1 1 0
2 2 0 0
2 2 1 0
3 0 0 0
3 0 1 0
3 1 0 0
3 1 1 0
3 2 0 0
3 2 1 0

Para transformar isso em permutações há um algoritmo simples. Percorrem-se, da esquerda para a direita, os dígitos do fatorádico, usando cada um como índice para o conjunto sofrendo permutação. A cada iteração, o elemento anterior é retirado do conjunto original (por isso, o último dígito do fatorádico é sempre zero). Essa enrolação toda esta codificada na pequena função abaixo:

sub permutations {
  my $callback=shift;
  my @elements=@_;
  
  
  factoradics($#elements+1, sub {
    my @permutation;
    my @temp=@elements;
    
    push(@permutation, splice(@temp, $_, 1)) for @_;
    &$callback(@permutation);
  });
}

Como está evidente, é preciso copiar o conjunto original para uma variável temporária (@temp), já que a função precisa ir retirando (com splice) os elementos já inseridos (com push) na permutação corrente. Então, como sempre, termino com um pequeno teste para demonstrar como é fácil usar essa função:

permutations(sub{
  print "@_\n";
}, 'a','b','c');

O resultado é, conforme esperado:

a b c
a c b
b a c
b c a
c a b
c b a
A grande vantagem dessa base é que pode-se encontrar a n-ésima permutação de um conjunto sem calcular as anteriores e, mais interessante ainda, é possível determinar qual é a permutação atual. Por exemplo, com "acb" posso calcular que esta permutação corresponde ao fatorádico 010 (retira-se o primeiro elemento de "abc", depois o segundo (lembre que começamos sempre com 0) do que restou e, finalmente, o primeiro do que restou). Transformando o 010 para a base decimal, (1*1!+0*1!) descobre-se que essa é a primeira permutação de "abc".

quarta-feira, 30 de maio de 2012

Combinações de dígitos II

Eu queria enumerar todos os inteiros de n bits com k dígitos 1. Uma olhadela nos números revelou que uma solução recursiva seria a melhor maneira de atacar o problema. Depois de gastar um pouco de fósforo, encontrei esta singela solução em Perl

sub choose {
  my $n=shift;
  my $k=shift;
  my $callback=shift;
  my @rest=@_;
  
  if($k>0) {
    for my $m ($k..$n) {
      choose($m-1, $k-1, $callback, $m, @rest); 
      &$callback($m, @rest) if $k==1;
    }
  } 
}
A função choose() recebe 4 parâmetros: n é o total de bits; k é o número de dígitos 1; callback é a função que será executada para cada combinação; e rest é um array que contém os dígitos das recursões anteriores (e que assim possibilita termos toda a informação na ponta da recursão sem recorrer a variáveis globais).

A função callback() recebe uma lista com os índices dos bits 1 na iteração atual. Então, se ela recebe (1, 4), isso significa que o primeiro e o quarto bits são 1. Os outros são 0.

Pois bem, com o seguinte teste enumerei todos os números de 8 bits com 7 dígitos ligados:

sub to_number {
  my $total=0;
  map { $total+=2**($_-1) } @_;
  print "$total\n";
}

choose(8, 7, \&to_number);
Esse código imprime a seguinte seqüência: 127, 191, 223, 239, 247, 251, 253, 254. Alternativamente, se eu quisesse enumerar combinações de letras (não deveria haver um verbo eletrar?), eu poderia fazer o seguinte:

choose(4, 2, sub { 
  print map { ('A'..'D')[$_-1] } @_;
  print "\n";
});
Esse código apresenta todos os pares com as letras de A a D: AB, AC, BC, AD, BD, e CD.

quarta-feira, 16 de maio de 2012

etbjxntqrdke

Ontem havia duas reuniões sendo travadas ao meu redor; eu não participava de nenhuma, mas sofria com a gritaria das duas. Como no Brasil é falta de educação apontar a falta de educação dos outros, tive que me contentar com alterar a mensagem do MSN.

Então, um colega me apontou um sítio que vende uma camiseta com a seguinte linha de Perl:

perl -e '
"$a="etbjxntqrdke";
$a=~s/(.)/chr(ord($1)+1)/eg;
print "$a\n;"'
Claro, achei muito explicadinho e limpinho e resolvi arrumar as coisas para manter a má fama do Perl.

perl -e "map { print chr 1+ ord } 'etbjxntqrdke'=~/./g"
Achei o resultado esteticamente aprazível.

terça-feira, 8 de maio de 2012

Linux salva o dia

A mulher amada foi viajar com um notebook (um HP-530) da empresa que, infelizmente, tem um Windows XP cheio de restrições. Não sendo possível instalar o Skype nele, saí à procura de um Linux para resolver o problema de comunicação.

Há muitos Linux que rodam direto de um CD ou DVD, mas são raros os que incluem o Skype. Encontrei uma variação sueca do Ubuntu 12.04 chamada ExLight. Ele funcionou muito bem com a webcam da Genius (uma FaceCam 1000) e com o Wifi (Intel PRO/Wireless 3945ABG).

O boot é demorado se o micro não estiver ligado por cabo a uma rede que devolva logo um IP por DHCP, mas ainda é mais rápido que iniciar o Windows. Esse Linux é tão obscuro que sequer consta do DistroWatch, mas resolveu tão bem o problema em questão que não hesito em indicá-lo.

segunda-feira, 30 de abril de 2012

Mapas de indicadores do RS

Na última terça-feira, dia 24/04, foram lançados os mapas de indicadores do TCE-RS. Eu trabalhei neles por um bom tempo e gostei de vê-los anunciados pela mídia do estado. Vou gostar ainda mais se meus compatriotas gaudérios os usarem para cobrar melhores resultados de seus prefeitos e suas câmaras municipais.

Os mapas foram escritos principalmente em Javascript (porque usam o Google Maps como base) e em PL/SQL (para gerar os dados). Não há muito no meio (deixei vários programadores Java sem emprego), exceto um script em Perl. O PL/SQL gera JSON, cujo resultado é armazenado em arquivos pelo script em Perl (porque algumas consultas são muito lentas e, de qualquer forma, porque eu não queria nenhuma carga extra desnecessária para o banco). O dia seguinte ao lançamento teve o maior número de acessos (6.487) e não teve nenhuma conseqüência sobre os servidores de aplicação e o banco justamente porque foram servidos apenas arquivos estáticos.



A principal dificuldade foi a de tirar um desempenho aceitável do Internet Explorer. O Javascript do IE é o pior por larga margem. Um Firefox 3 num Pentium Core Duo ainda é melhor que um IE9 num i7 de 4 núcleos. O garagalo principal está no código que desenha os 496 municípios; no IE ele demora tanto que o navegador acaba abrindo um alerta de que o script está demorando demais. A única forma de evitar esse problema foi dividir o trabalho no tempo com o setTimeout(). Os municípios são desenhados em grupos de 50 no IE e todos juntos nos outros navegadores.

As coordenadas foram comprimidas através de facilidades da própria API do Google Maps. A minha tentativa de comprimi-las tinha me levado de 1,5MB a 530KB, porque aproveitei o fato de que todas têm números negativos (exclui todos os sinais) e que as diferenças entre coordenadas consecutivas são pequenas (guardei apenas as diferenças sem o separador decimal). Mas a API do Google Maps é mais esperta e o arquivo final tem apenas 298KB.

Além da API do Google Maps, usei os plugins blockUI e Sparkline (para desenhar os gráficos) do jQuery. As consultas ganharam muito com o uso das funções analíticas do Oracle (que o Postgresql também oferece), já que com elas posso calcular médias e somas de vários anos distintos de uma só vez.

Em resumo, foi um excelente exercício de duas tecnologias muito diferentes: PL/SQL e Javascript. A primeira me permitiu escrever consultas muito mais legíveis e concisas; a segunda me permitiu jogar para o cliente toda a computação que restou fazer e assim aliviar a pressão dos servidores.

sábado, 28 de abril de 2012

Mobilidade cerceada

O tempo que levo para voltar do trabalho para casa tem me feito pensar sobre como é estúpido o sistema de transporte que estamos fomentando. Frequentemente, levo mais de 1h para percorrer os cerca de 9km entre meu trabalho e minha casa.

O maior problema é a quantidade de automóveis e o fato de que a maioria leva uma ou duas pessoas. Além disso, os autos estão ficando cada vez maiores. É irracional utilizar um veículo de uma tonelada e meia para carregar uma pessoa com menos de 100kg, mas esses gigantes estão se disseminando. Alguns precisam de duas vagas nos estacionamentos.

Como o país está numa fase de crescimento rápido e as pessoas encontram-se com dinheiro e a possibilidade de comprar carros enormes pela primeira vez, antecipo que vamos ter que aturar esse deslumbramento por um bom tempo ainda. Além disso, esse comportamento infantil adentra o reino do antissocial, porque os proprietários, além de não quererem dividir as ruas com ciclistas, pleiteiam para si cada vez mais investimentos de infraestrutura. Ciclovias são inúteis, reclamam, mas viadutos multi-milionários são indispensáveis.

Eu reservo-me o luxo de usar táxi-lotação, que são um pouco mais caros. Raramente uso ônibus, porque andam lotados e muitos não têm ar-condicionado. Penso que eles não melhoram, porque são vistos como negócio, não como utilidade pública. É muito fácil calcular despesas e lucros, ao passo que bem-estar ainda não sabemos quantificar.

Conferindo o orçamento da prefeitura de Porto Alegre em 2011, vejo que há milhões dotados para obras viárias: duplicação da Avenida Edvaldo Pereira Filho (R$32 milhões); duplicação da Avenida Voluntários da Pátria (R$9 milhões); prolongamento da Avenida Severo Dullius (R$8 milhões); implantação da Avenida Tronco (R$21 milhões); etc. Quando procuro por obras para pessoas, encontro pouco: construção de calçadão na Vila Assunção (R$245 mil dotados, mas nada pago); mobilidade e acessabilidade no centro (R$790 mil dotados, mas apenas R$290 mil pagos); obras viárias na área socioambiental (R$800 mil dotados, nada pago).

Esse excesso de ênfase no automóvel está tornando a vida cada vez mais complicada para todos. Estamos num círculo vicioso em que o transporte coletivo ruim incentiva o uso do carro e isso torna a mobilidade urbana ainda pior. Mas meus compatriotas estão ainda tão enamorados de seus brinquedinhos caros que não enxergam o efeito negativo que eles têm sobre suas vidas. Logo, o assunto que nos toma horas todos os dias não existe no debate político.

segunda-feira, 9 de abril de 2012

Juntando linhas

Tenho uma tabela de permissões que tem a seguinte forma:

CREATE TABLE PERMISSOES (
  OBJETO VARCHAR2(100),
  USUARIO VARCHAR2(100)
)
Eu preciso de um relatório que enumere todos os usuários para cada objeto, colocando todos os autorizados numa só linha. A minha solução, um pouco complicada, foi juntar as facilidades de consultas hierárquicas do Oracle com um pouco de funções analíticas.

O cerne da consulta é o seguinte SELECT:

select objeto, 
       row_number() over (partition by objeto order by usuario) ordem,
       row_number() over (partition by objeto order by usuario desc) medro,
       usuario
from permissoes
order by 1, 2, 3 desc
Isso produz uma lista de todos os objetos com os respectivos usuários numerados alfabeticamente tanto crescentemente como decrescentemente. Considere as seguintes linhas:

OBJETOUSUARIO
COFREHeráclito
COFREXenofonte
COMPUTADORHeródoto
COMPUTADORHeráclito
COMPUTADORPlatão

A consulta produzirá o resultado abaixo:

OBJETOORDEMMEDROUSUARIO
COFRE12Heráclito
COFRE21Xenofonte
COMPUTADOR13Heráclito
COMPUTADOR22Heródoto
COMPUTADOR31Platão

Envolvendo essa consulta com um SELECT hierárquico, podemos agregar os helenos.

  select objeto, substr(sys_connect_by_path(usuario, ','),2) autorizados 
  from (
    select objeto, 
           row_number() over (partition by objeto order by usuario) ordem,
           row_number() over (partition by objeto order by usuario desc) medro,
           usuario
    from permissoes
  )
  where medro=1
  connect by prior ordem=ordem-1 and prior objeto=objeto
  start with ordem=1
Então, uso a coluna ORDEM para simular uma hierarquia e a coluna MEDRO para eliminar todas as linhas exceto a última (que possui todos os usuarios relacionados a um objeto).

O resultado é o seguinte:

OBJETOAUTORIZADOS
COFREHeráclito,Xenofonte
COMPUTADORHeráclito,Heródoto,Platão

A função sys_connect_by_path() aglutina toda a hierarquia e neste caso indiquei uma vírgula como separador. No entanto, ela coloca uma vírgula também no início e para resolver isso adicionei a função substr().

segunda-feira, 2 de abril de 2012

Neofobia

Um amigo apareceu com um tablet daqueles chineses bem ordinários que não tem nome e muito menos manual. O aparelho roda Android e a tela deve ser das piores, porque é difícil de usar. O amigo perguntou (e eu já antecipava a pergunta) se seria possível instalar o Windows, porque era muito difícil configurar a rede sem fio no Android (não é).

O interessante dessa interação é que esse cidadão também não sabe configurar muita coisa no Windows. De fato, ele sabe fazer muito pouco no Windows além de iniciar o IE. Então, eu fiquei curioso: que problema haveria em ficar com o Android?

Eu já presenciei várias instâncias desse problema com pessoas que querem usar o micro para navegar e jogar paciência, mas que fazem questão absoluta de fazer isso com o Windows. O que me deixa mais perplexo é que a mudança do XP para o Windows 7 foi completamente indolor (aparentemente) enquanto eu ainda me atrapalho com o Explorer novo.

Que amor é esse ao Windows? Deve ser o prazer de ter que formatar o HD e reinstalá-lo todo ano!

terça-feira, 27 de março de 2012

Hipercontagem

Há um bom tempo venho colecionando as moedas comemorativas de um real. São elas a dos 50 anos da Declaração dos Direitos Humanos, a dos 40 anos do Banco Central e a do centenário de Juscelino Kubitschek.

MoedaEmissãoNa minha coleção
50 anos da Declaração dos Direitos Humanos600 mil2
40 anos da Banco Central40 milhões9
Centernário de JK50 milhões12

Provavelmente por eu as estar colecionando há vários anos, a distribuição delas na minha niqueleira é parecida com as proporções das emissões. A tabela abaixo mostra as proporções esperadas e as encontradas.

MoedaProporção do totalNa minha coleção
50 anos da Declaração dos Direitos Humanos0,6%8%
40 anos da Banco Central44%39%
Centernário de JK55%52%

Essas emissões especiais representam 4,4% do total de 2.024.376.000 da segunda família de moedas de um real (incluindo as comemorativas). Com isso, estimo que para juntar essas 23 moedas tenham passado quase 500 moedas de um real pela minha niqueleira. Claro, isso se as outras moedas comemorativas não tenham sido guardadas por malucos tentando estragar as estatísticas alheias.

segunda-feira, 27 de fevereiro de 2012

Transformando colunas em linhas III

Uma forma mais simples de transformar colunas em linhas é usar a função decode (do Oracle). Considere a seguinte tabela:

 CREATE TABLE PESSOAS (
    NOME VARCHAR2(200),
    NOME_PAI VARCHAR2(200),
    NOME_MAE VARCHAR2(200)
  )
Se for necessário produzir uma lista com todos os nomes, pode-se usar o decode da seguinte maneira:

select decode(n, 1, nome, 2, nome_pai, 3, nome_mae) 
from pessoas, 
     (select rownum n from pessoas where rownum<4)
A consulta com rownum serve apenas para produzir três linhas com os números 1, 2 e 3. Essa pequena tabela multiplica a tabela de pessoas por 3. O decode() então indica uma coluna diferente para cada valor de n. Assim, colocamos cada valor de coluna numa linha separada.

quinta-feira, 16 de fevereiro de 2012

Antes que seja tarde

Hoje encontrei uma conta deveras interessante envolvendo a velocidade atual dos computadores. O objetivo dela é descobrir qual a distância que a luz percorre durante um ciclo de máquina.
  1. A velocidade da luz é cerca de 300.000km/s ou 3*10^8m/s
  2. Uma máquina de 3GHz (bem comum hoje em dia) executa um ciclo a cada 1/3.000.000.000s ou 1/3*10^9s
  3. Logo, em um ciclo de máquina a luz consegue andar 0,1m ou 10cm.
Isso significa que se eu levantar uma placa com uma conta (por exemplo, 1034*564), uma máquina moderna consegue calcular o resultado antes que a luz rebatida pela placa chegue a uma pessoa do outro lado da sala.

segunda-feira, 13 de fevereiro de 2012

Transformando colunas em linhas II

No artigo anterior, usei expressões regulares para transformar strings com valores separados por vírgula em linhas de uma consulta. Outra utilidade dessa técnica é transformar colunas de uma consulta em linhas.

Por exemplo, considere uma tabela de pessoas que inclui também os nomes dos pais:

  CREATE TABLE PESSOAS (
    NOME VARCHAR2(200),
    NOME_PAI VARCHAR2(200),
    NOME_MAE VARCHAR2(200)
  )

Para transformar isto numa única consulta que retorne os nomes de todas as pessoas referenciadas, podemos juntar com vírgula os nomes linha a linha e depois separá-los com uma expressão regular:

select trim(trailing ',' from nome) from (
  select regexp_substr(
            nome||','||nome_pai||','||nome_mae||','
         ,'[^,]+,',1,n.n) nome
  from pessoas,
      (select rownum n from all_tables where rownum<4) n
)

Deixei a concatenação sozinha numa linha, para evitar a confusão de vírgulas dentro e fora de strings.

A expressão "[^,]+," busca cada pedaço do string que não contenha vírgulas, mas que termine com uma. Por isso, é preciso adicionar uma vírgula ao último item. Por fim, tiram-se as vírgulas e apresentam-se os nomes, linha a linha.

sexta-feira, 10 de fevereiro de 2012

Transformando textos em linhas

Uma função que não se acha no SQL é a split(). Por exemplo, para transformar o string '12,34,56,78,90' em linhas de uma consulta, podemos escrever um pequeno bloco de PL/SQL no Oracle. No entanto, quero usar apenas o SQL.

Minha solução é a seguinte:

select trim(trailing ',' from item) from (
  select regexp_substr('12,34,56,78,90','\d+,',1,n.n) item
  from dual,
    (select rownum n 
     from all_tables 
     where rownum<length(translate('12,34,56,78,90', ',1234567890', ','))+1) n
) 

A função regexp_substr() permite indicar qual ocorrência da busca queremos mostrar, então, junto a tabela dual (que só tem uma linha) com n linhas de all_tables a fim de mostrar todas as ocorrências. Cada uma vira uma linha. Além disso, uso o mesmo string na consulta a all_tables para encontrar o número de vírgulas e somar um, produzindo o número total de elementos na lista.

Para testar mais um pouco minha pequena solução, criei uma tabela dos campões do mundo de futebol:

CREATE TABLE CAMPEOES (
  PAIS VARCHAR2(100), 
  CAMPEONATOS VARCHAR2(200)
)

E dentro dessa tabela coloquei o seguinte:

PAIS CAMPEONATOS
Uruguai 1930,1950
Alemanha 1954,1974,1990
Itália 1934,1938,1982,2006
Argentina 1978,1986
Inglaterra 1966
Espanha 2010
França 1998
Brasil 1958,1962,1970,1994,2002

Para enumerar os campeões ano a ano, executei a seguinte consulta:


select pais, trim(trailing ',' from item) from (
  select pais, regexp_substr(campeonatos||',','\d+,',1,n.n) item
  from campeoes,
    (select rownum n from all_tables where rownum<10) n
) where item is not null
order by 2


Uruguai 1930
Itália 1934
Itália 1938
Uruguai 1950
Alemanha 1954
Brasil 1958
Brasil 1962
Inglaterra 1966
Brasil 1970
Alemanha 1974
Argentina 1978
Itália 1982
Argentina 1986
Alemanha 1990
Brasil 1994
França 1998
Brasil 2002
Itália 2006
Espanha 2010

Modelei ao banco ao problema, mas a solução serve também para quando a modelagem for o problema.

terça-feira, 7 de fevereiro de 2012

Sexo e SOPA

Quando estamos no colégio nos ensinam um monte de simplificações e mentiras com o objetivo de nos dar uma idéia de como o mundo funciona. Uma mentira que apenas recentemente revelou-se para mim é a de que o sexo tem o objetivo de conferir maior variabilidade genética a uma população.

A semelhança que o nosso genoma tem com os outros mamíferos é impressionante. Mesmo com o pequeno rato, temos 80% dos genes com comum. Com os chimpanzés temos algo como 1% de diferença. Entre dois seres humanos, a diferença é minúscula. Então, não é variabilidade que procuramos, mas correção de erros!

A radiação e poluição com as quais convivemos todos os dias vão, pouco a pouco, minando nossa estrutura. Não há como um organismo viver por muito tempo sem uma correção. Nada melhor que o sexo. Comparamos o projeto de um com o projeto do outro, separamos os defeitos e criamos uma cópia.

Claro, ninguém vai contar para as crianças que seus organismos estão se decompondo aos poucos e que o melhor que têm a fazer é procriar. Como adultos, essa idéia também não é muito agradável (a parte sobre decompor, isto é).

Então, estamos nós neste planeta, dependendo vitalmente da nossa capacidade de copiar a nós mesmos enquanto algumas pessoas querem implantar leis como SOPA, PIPA e ACTA para colocar na cadeia quem copia! Não é natural e tampouco é saudável ensinar às crianças que compartilhar é feio.

Sempre achei que essa conversa de propriedade intelectual fosse conversa para consumidor dormir. Agora tenho a prova biológica!

sexta-feira, 27 de janeiro de 2012

A nota do vestibular e o sistema de cotas

Eu lembro bem até hoje da minha nota no vestibular. Era uma ótima nota e eu poderia, naquele ano, ter entrado em qualquer curso. E a verdade, passados 20 anos, é que a nota que me foi tão cara não teve nenhuma relevância sobre o que se passou depois. Por isso, não vejo a importância que se está dando às notas dos aprovados pelo sistema de cotas.

Sempre achei curioso como os norte-americanos entram na universidade para depois decidirem qual formação desejam (alguns devem ter propósitos bem firmes, mas, mesmo assim, as univerdades lhes deixam todas as portas abertas). Aqui no Brasil, por outro lado, parecemos ter a realidade perfeitamente sob controle; como numa ditadura stalinista bem planejada, temos, desde já, definidos os futuros dos 140 gloriosos engenheiros que ingressam hoje na faculdade do povo! E, assim como numa ditadura stalinista bem planejada, os resultados são, digamos, não tão brilhantes quanto se esperava.

Infelizmente, ingressamos sem conhecer o curso e muito menos o mercado profissional que nos aguarda depois. E a nota do vestibular perde toda a importância no minuto que nos matriculamos. Há portadores de notas medíocres com carreiras internacionais fantásticas, assim como portadores de notas excelentes com carreiras menos que gloriosas.

Há quem tema a decadência das universidades públicas, como se sua luz fosse hoje apenas reflexo do brilhantismo de seus alunos. Os professores são todos doutores, mas no instante em que substituirmos seus alunos por outros menos dignos, tudo isso tornar-se-á irrelevante, parecem pensar alguns. Eles tambêm, tudo indica, serão incapazes de dar notas baixas a quem não estudar.

Os cotistas em geral, têm notas piores no vestibular, sim, mas o que importa saber é como serão seus desempenhos acadêmicos e como serão suas vidas depois de completado o curso e, também, como será a nota de seus filhos. Todos os dias, saio caminhando do meu prédio e vejo gente branca saindo em carros imponentes e gente de pele escura entrando à pé para fazer a limpeza. Nos 124 anos desde que foi abolida a escravatura, a sociedade brasileira não tentou de nenhuma forma diminuir as desigualdades e, no momento em que se produz um pequeno experimento, a reação é a de que a civilização ocidental está a perigo.

Penso eu que o vestibular tinha que ser abolido. Quem quiser que matricule-se na cadeira de cálculo e quem passar que prossiga. Podem colocar uma prova de exemplo na porta da reitoria que muitos já desistirão. Sem prova de admissão, então podemos voltar a falar em qualidade de ensino e deixar essa fixação juvenil no vestibular para trás.

P.S. Refletindo sobre o texto, lembrei depois que três empresários de sucesso que conheço não passaram no vestibular da UFRGS quando não havia cotas (talvez para sua própria sorte), o que confirma que a nota do vestibular não mede muita coisa.

quinta-feira, 12 de janeiro de 2012

Feliz 2*2*503!

Para iniciar positivamente o ano novo, resolvi comparar duas informações importantes. A primeira é a probabilidade de ganhar a Mega-Sena (1/50.063.860) e a outra é a de que a probabilidade de continuar vivo diminui pela metade a cada oito anos.

Qual seria a idade, pensei eu, cuja probabilidade de obtenção é igual a de acertar as seis dezenas? Com a ajuda do Wolphram Alpha, descobri que a idade é 111 (quase 112, para os otimistas).

Então, aproveite o pouco tempo que lhe resta e deixe de sonhar com bobagens!