segunda-feira, 3 de fevereiro de 2014

Vantagens desperdiçadas

A greve dos ônibus e o calor terrível deste verão me levaram a pensar (novamente) sobre o quão estúpido é o modelo de transporte que temos nas cidades.

Vou começar pelas escadas rolantes, porque elas ilustram bem como as pessoas desperdiçam os meios quem têm e perdem uma excelente oportunidade de viver um pouco melhor. Elas nos permitem subir com maior velocidade, mas as pessoas ficam paradas, esperando o destino chegar (para isso, existem os elevadores). Além disso, poucas são as pessoas que admitem que alguém queira subir caminhando e, por isso, não deixam espaço para ninguém passar. Elas oferecem uma oportunidade excelente de ganhar um pouco de tempo; a oportunidade é desperdiçada e ainda negada a quem quiser tirar proveito dela.

Numa dimensão diferente, vemos o mesmo tipo de desperdício nos automóveis. As pessoas encontram-se com mais dinheiro e, quando poderiam usá-lo para viver melhor, compram carros cada vez maiores e consumidores de combustível. O primeiro Fiat 500 oferecia 18cv; os últimos modelos têm mais de 100cv. Não quebramos a barreira dos 20km/l nos carros ditos urbanos, quando já poderíamos estar nos 50km/l ou 60km/l. Com mais dinheiro no bolso dos motoristas e menos poluição, a vida poderia ser um pouco melhor para todos os habitantes das cidades.

Entretanto, as cidades estão entupidas com monstruosidades como a Tucson da Hyundai, que pesa 1,5 tonelada e mal consegue fazer melhor que 10km/l na estrada. Isto é, trata-se de um veículo que não deveria estar circulando num meio urbano civilizado, tanto porque oferece um risco maior à segurança como porque consome uma quantidade de recursos excessiva para o fim ao qual se propõe (transportar, no máximo, 5 pessoas).

Muitas cidades já limitam o trânsito de caminhões, justamente porque poluem, atrapalham o trânsito, e destroem o asfalto. Eu gostaria de ver os parâmetros para um veículo urbano serem reduzidos gradualmente, para que veículos menores e mais eficientes dominem as cidades. Teríamos um trânsito mais seguro, um ar mais limpo e mais dinheiro no bolso para gastar com coisas que realmente melhorem a qualidade de vida.

sexta-feira, 31 de janeiro de 2014

Login social

Um chamado aparentemente simples acabou como inspiração para uma ideia genial. Às vezes, não damos valor aos usuários que temos.

O cidadão estava reclamando que não conseguia entrar no sistema, o qual o informava algo sobre não ter permissão. Geralmente, isso indica que a pessoa de fato não tem permissão, mas costumo dar o benefício da dúvida a todos e revisei as permissões. Ele as tinha.

Conferi os logs e descobri que era outra pessoa que não estava conseguindo entrar. O cidadão que abriu o chamado tinha entrado várias vezes sem qualquer dificuldade. Seu amigo, no entanto, poderia continuar tentando, porque não estava habilitado.

Pode ser que um fosse muito amigo do outro a ponto de confundirem suas credenciais, mas acho mais provável que a informação sobre quem estava com dificuldade tenha sido julgada irrelevante.

De qualquer forma, ocorreu-me que daí poderia surgir o login social. Através da autenticação do Facebook, por exemplo, os amigos das pessoas autorizadas também poderiam ganhar privilégios no sistema. Claro, seria conveniente que o usuário indicasse corretamente quem é amigo próximo ou família. E, conforme o grau de proximidade, poderíamos conferir mais ou menos permissões.

Vou levantar a questão na próxima reunião.

sexta-feira, 27 de dezembro de 2013

Menos velocidade, mais rapidez

Acho muito curiosos os argumentos de que o limite de velocidade nas avenidas de Porto Alegre é baixo, porque minhas viagens para casa no fim do dia dificilmente alcançam 10km/h. Considerando a quantidade de carros nas ruas, aumentar a velocidade não iria resolver nada, portanto.

Parece-me que há, por outro lado, bons argumentos para diminuir a velocidade máxima em toda a cidade para 40km/h.

É comum reencontrarmos os apressadinhos nas sinaleiras. Eles correm para utrapassar e ganhar a corrida até o próximo sinal vermelho. O que me leva a creer que os tempos dos deslocamentos urbanos são muito mais regidos pelos tempos de parada ou lentidão que pelos tempos de velocidade.

Porto Alegre não é uma cidade muito grande. Pode-se ir da zona sul à zona norte em menos de 20km. E, supondo que não houvesse qualquer obstáculo no caminho, essa distância seria percorrida em 20 minutos a 60km/h ou em 30 minutos a 40km/h.

Quem tenta entrar numa avenida sabe que a velocidade na via mais rápida dificulta o acesso. Nas ruas de 40km/h, os rapidinhos que excedem o limite também roubam dos outros motoristas a possibilidade de fazer uma conversão em menos tempo. E ainda há o tempo de luz amarela nos semáforos, que deve ser maior para dar segurança aos cruzamentos das avenidas.

Além disso tudo, há o fato de que os atropelamentos a 60km/h (90% são fatais) são muito mais perigosos que os atropelamentos a 40km/h (50% são fatais). Havendo menos velocidade, a cidade tornar-se-ia mais segura para os ciclistas e mais gente poderia sentir-se motivada a adotar esse meio de locomoção.

Em resumo, acho que há mais vantagens de termos uma velocidade máxima menor do que perdemos por poder andar rápido por alguns segundos de vez em quando. Mesmo assim, não guardo muita esperança de que o motorista portoalegrense médio queira adotar uma medida racional e civilizada para melhorar o trânsito.

sexta-feira, 13 de dezembro de 2013

Até tu, Ubuntu?

Minha máquina Ubuntu tem dois monitores. Eu queria desabilitar um deles temporariamente, porque não estava funcionando. Assim como o Windows, o Ubuntu retorna à configuração anterior se uma mudança na configuração de video não for confirmada em poucos segundos (para salvar o usuário, caso a alteração tenha desligado o sinal).

Pois bem, o Ubuntu, deduzo eu, estava tentando pedir a confirmação justamente no monitor que eu estava tentando desabilitar, porque, alguns segundos depois da alteração, tudo piscava e o monitor desligado constava novamente como habilitado.

Foi um momento muito triste na história dos sistemas operacionais.

segunda-feira, 9 de dezembro de 2013

Fatorial revisitado III

Minhas tentativas anteriores de otimizar o cálculo dos fatoriais a partir de potências de números primos tinha evoluído até eu conseguir calcular o total dos expoentes de cada primo p entre potências consecutivas. Agora, consegui encontrar uma maneira de calcular todos os expoentes até uma potência qualquer.

Para um primo p, a soma dos expoentes de sua participação em cada múltiplo anterior ou igual a pn é

n+[(pn-p*n+n-1)/(p-1)]
= pn-1/p-1

Por exemplo, entre 2 e 8, temos os seguintes múltiplos de 2: 2, 4, 6, e 8. Anteriormente, teríamos que calcular os expoentes entre 2 e 4 e somá-los aos expoentes entre 4 e 8 e ainda somar os expoentes das potências (2, 4, e 8).

Agora, chegamos rapidamente ao resultado:

3+[(23-2*3+3-1)/(2-1)] = 3+[(8-6+3-1)/1] = 3+4 = 7
= 8-1/2-1
= 7

Conferindo os múltiplos de 2, confirmamos que
  • 2 = 21
  • 4 = 22
  • 6 = 21*3
  • 8 = 23
E assim, percebemos que a soma dos expoentes é, de fato, 7.

Alterei um pouco o código para exibir as respectivas fatorações.


import java.math.BigInteger;

public class Factorial {

  private static int in(int p, int i) {
    int n=0;
    while(i%p==0) {
      n++;
      i=i/p;
    }
    return n;
  }

  public static BigInteger f(int n) {
    int m=n+1;
    int[] p=new int[m];
    double logn=Math.log(n);
    for(int i=2; i<=m/2; i++) {
      if(p[i]!=-1) {
        //Marcar todos os múltiplos
        for(int j=i+i; j<m; j+=i) {
          p[j]=-1;
        }
        //Calcular soma de expoentes até a maior potência anterior a n
        int power=(int)(logn/Math.log(i));
        int k=(int)Math.pow(i, power);
        p[i]=(k-1)/(i-1);
        //Calcular os expoentes para os múltiplos que seguem a última potência
        for(int l=k+i; l<m; l+=i) {
          p[i]+=in(i, l);
        }
      }
    }
    BigInteger f=BigInteger.ONE;
    for(int k=2; k<m; k++) {
      int pk=p[k];
      if(pk>=0) {
        if(k>2) {
          System.out.print("*");
        }
        if(pk<2) {
          System.out.print(k);
          f=f.multiply(BigInteger.valueOf(k));
        } else {
          System.out.print(k+"^"+pk);
          f=f.multiply(BigInteger.valueOf(k).pow(pk));
        }
      }
    }
    return f;
  }
    
  public static void main(String... args) {
    for(int i=2; i<20; i++) {
      System.out.printf("%d! = ", i);
      BigInteger f1=f(i);
      System.out.printf(" = %d\n", f1);
    }
  }
 }

E isso produz o seguinte:

2! = 2 = 2
3! = 2*3 = 6
4! = 2^3*3 = 24
5! = 2^3*3*5 = 120
6! = 2^4*3^2*5 = 720
7! = 2^4*3^2*5*7 = 5040
8! = 2^7*3^2*5*7 = 40320
9! = 2^7*3^4*5*7 = 362880
10! = 2^8*3^4*5^2*7 = 3628800
11! = 2^8*3^4*5^2*7*11 = 39916800
12! = 2^10*3^5*5^2*7*11 = 479001600
13! = 2^10*3^5*5^2*7*11*13 = 6227020800
14! = 2^11*3^5*5^2*7^2*11*13 = 87178291200
15! = 2^11*3^6*5^3*7^2*11*13 = 1307674368000
16! = 2^15*3^6*5^3*7^2*11*13 = 20922789888000
17! = 2^15*3^6*5^3*7^2*11*13*17 = 355687428096000
18! = 2^16*3^8*5^3*7^2*11*13*17 = 6402373705728000
19! = 2^16*3^8*5^3*7^2*11*13*17*19 = 121645100408832000
É muito mais interessante que um fatorial recursivo simples, com if ou sem.

sexta-feira, 6 de dezembro de 2013

Fatorial revisitado II

Uma propriedade interessante dos números surgiu nas minhas investigações do fatorial. O número de vezes que um número divide seus múltiplos entre duas potências suas consecutivas é igual à primeira potência menos um mais o expoente dessa primeira potência. Bastante intuitivo, eu penso.

Entre 2n e 2n+1, por exemplo, temos os múltiplos 2n+2, 2n+4, 2n+6, e assim por diante. Cada um desses números pode ser dividido por uma potência de 2. E a soma dos expoentes é 2n-(1+n).

Tomemos outro exemplo com 5. Entre 51 e 52 temos os seguintes múltiplos de 5: 10 (2*5); 15 (3*5); e 20 (2*2*5). A soma dos expoentes de 5 é 3, que é igual a 5-2, ou, 5n-(1+n)=51-(1+1)=3.

Essa propriedade podemos usar para acelerar o cáculo do fatorial. Infelizmente, ainda precisamos manter o método anterior para calcular os expoentes de cada primo em cada múltiplo posterior à última potência anterior a n (quando estivermos calculando n!).

Então, o algoritmo ficou divido em 3 partes:
  1. Para cada primo, marcar seus múltiplos menores que n+1;
  2. Para cada potência de cada primo p, calcular pm-(1+m);
  3. Para os múltiplos de p superiores à última potência inferior a n, calcular os expoentes de p.
Em Java, montei o seguinte:

  private static int in(int p, int i) {
    int n=0;
    while(i%p==0) {
      n++;
      i=i/p;
    }
    return n;
  }

  public static BigInteger f(int n) {
    int m=n+1;
    int[] p=new int[m];
    for(int i=2; i<=m/2; i++) {
      if(p[i]!=-1) {
        //Marcar todos os múltiplos
        for(int j=i+i; j<m; j+=i) {
          p[j]=-1;
        }
        //Calcular os expoentes entre potências consecutivas
        int k=i;
        int power=1;
        for(; k*i<=m; k*=i) {
          p[i]+=k-power-1;
          p[i]+=power;
          power++;
        }
        //Calcular os expoentes para os múltiplos que seguem a última potência
        if(k<m) {
          p[i]+=power;
          for(int l=k+i; l<m; l+=i) {
            p[i]+=in(i, l);
          }
        }
      }
    }
    BigInteger f=BigInteger.ONE;
    for(int k=2; k<m; k++) {
      int pk=p[k];
      if(pk>=0) {
        if(pk==0) {
          f=f.multiply(BigInteger.valueOf(k));
        } else {
          f=f.multiply(BigInteger.valueOf(k).pow(pk));
        }
      }
    }
    return f;
  }
O mais surpreendente é que funciona.

quinta-feira, 5 de dezembro de 2013

Fatorial revisitado

Eu estava tendo dificuldades para criar um executável com um banco de dados embutido (com Tcl, SQLite e Freewrap), quando resolvi brincar um pouco com o Tcl. Escrevi uma pequena função para calcular fatoriais. Tcl permite gerar números bem grandes (como 1000!) com facilidade. Para tornar a coisa mais interessante, resolvi escrever uma versão sem if (abaixo).

proc fac {n} {
  set e 1;
  for {set x 2} {$x<=$n} {incr x} {
    append e "*$x"
  }
  puts $e
  return [expr $e]
}
A função simplesmente gera uma expressão (por exemplo, ela gera "2*3*4*5" para 5!) e depois calcula o resultado. Talvez inspirado pela recente releitura o livro Metamat! (de Gregory Chaitin), achei que seria interessante encontrar uma maneira de encolher essa expressão e assim tornar o programa mais curto e eficiente.

A solução mais próxima seria gerar um produto de primos, assim a multiplicação teria o menor número possível de multiplicandos. Não demorou muito para sair um algoritmo parecido com o Crivo de Eratóstenes. Se eu quiser calcular n!, faço o seguinte:
  1. Enumero todos os inteiros até n;
  2. Começando em 2, marco todos os múltiplos dos primos e anoto qual potência de cada primo divide cada múltiplo (e vou somando as potências);
  3. Ao fim, terei uma potência para cada primo e a multiplicação de cada um elevado à sua potência produzirá o fatorial.
Por exemplo, para 6!, os passos são:
  1. Começando em 2, descubro que 2=2^1, 4=2^2 e 6=2^1*3;
  2. Com 3, tenho 3=3^1 e 6=3^1*2;
  3. Quatro posso ignorar, porque já o visitei e anotei como inteiro composto;
  4. 5 não tem múltiplos menores ou iguais a 6, então anoto 5^1;
  5. Finalmente, 6 já sei que não é primo.
O resultado é 2^4*3^2*5 e isso produz 720, conforme esperado. Abaixo apresento uma solução em Java:

  private static int in(int p, int i) {
    int n=0;
    while(i%p==0) {
      n++;
      i=i/p;
    }
    return n;
  }

  public static BigInteger f(int n) {
    int m=n+1;
    int[] p=new int[m];
    for(int i=2; i<=m/2; i++) {
      if(p[i]!=-1) {
        p[i]=1;
        for(int j=i+i; j<m; j+=i) {
          if(j%i==0) {
            int v=in(i,j);
            p[i]+=v;
            p[j]=-1;
          }
        }
      }
    }
    BigInteger f=BigInteger.ONE;
    for(int k=2; k<m; k++) {
      int pk=p[k];
      if(pk>=0) {
        if(pk==0) {
          f=f.multiply(BigInteger.valueOf(k));
        } else {
          f=f.multiply(BigInteger.valueOf(k).pow(pk));
        }
      }
    }
    return f;
  }

Esse código tem uma pequena otimização. Ele percorre os primos apenas até n/2. Todos os primos acima disso só vão aparecer uma única vez no produto final (em 10!, por exemplo, 7 só aparece como 7^1). O array p começa com zeros. As posições ocupadas por primos vão recebendo as respectivas potências e as demais recebem -1. Ao fim, as posições de primos acima de n/2 vão continuar com 0 (porque não as visitamos), então a segunda parte da função (que faz as multiplicações para calcular o resultado final) ignora os valores menores que zero, troca os zeros por uns e usa os demais valores como os encontrar.

Comparando com uma função simples que multiplica instâncias de BigInteger de 2 até n, para valores pequenos, a diferença de tempo é insignificante. Em algum ponto entre 1000! e 2000!, a nova função passa a tomar um pouco mais que a metade do tempo. Ela é mais econômica em memória também.

O método f() retorna um BigInteger, mas imagino que em algumas situações possa ser mais útil guardar o produto de primos ou simplesmente imprimir a expressão.