Quarta-feira Dez 23, 2009

São Paulo em 1972

Realmente para viver em São Paulo é preciso saber driblar o trânsito.

Tentei a bicicleta, mas pela falta de chuveiro onde trabalho a coisa fica desconfortável, principalmente no verão.

Agora meu problema não é só o trânsito, também tem o stress dos motoristas, porque carro não foi feito para ficar parado, nem foi para isso que o cidadão comprou. Isso irrita eles e eles me irritam.

Minha solução atual: ônibus. Não chegarei mais rápido ao trabalho, mas pelo menos chegarei menos irritado, e ainda economizo uns trocos do preço do estacionamento.

Essa é a minha promessa de ano novo. Para ela se efetivar é fácil: basta parar de pagar o estacionamento.

Vou ver no que dá.

Peguei a foto da seguinte reportagem: São Paulo precisaria de 125 avenidas Paulistas por ano para absorver carros novos, afirma especialista.

Segunda-feira Dez 14, 2009

Mudei de banco de dados

Estava usando MySQL para aprender melhor a usar.

Cansei e voltei ao meu preferido: PostgreSQL.

Deve ter alguns erros de acentuação por aí, se alguém achar me avisa.

Domingo Dez 13, 2009

Metal Invasion Radio

Metal Invasion Radio

Sábado Dez 12, 2009

Code Jam: Alien Language

O problema é esse: você tem uma lista de palavras, e dadas algumas expressões, você quer saber a quantidade de palavras que ela pode formar:

lista: abc, bbc
expressão: (abc)(bd)c
número: 2

Simples não? Pois bem, fui resolver o problema.

Minha primeira preocupação foi em transformar a expressão em uma lista de palavras. Para isso fiz uma função recursiva. Nada muito complicado... exceto que python não é a linguagem mais rápida que existe. E se estourar a memória? Afinal as entradas eram grandinhas. Outro problema é a performance: por que comparar no caso acima cbc e cdc se eu sei que não existe palavra que começa com c?

O que fiz: criei uma árvore para comparar e dado uma string, ele me diz se é uma palavra ou parte de uma palavra.

Eis o programa:

 

import sys
from bisect import bisect


class Node(object):

def __init__(self, name):
self._name = name
self._children = []

def add(self, word):
if not word:
return
a = word[0]
node = None
for item in self._children:
if item._name == a:
node = item
break

if not node:
node = Node(a)
self._children.append(node)
self._children.sort()
node.add(word[1:])

def search(self, word):
if not word:
return True

a = word[0]
a = Node(a)
i = bisect(self._children, a) - 1
if i < 0:
return False
node = self._children[i]
if a != node:
return False
return node.search(word[1:])

def __cmp__(self, other):
return cmp(self._name, other._name)


def parse_words(to_parse, parsed):
global tree
if not to_parse:
if tree.search(parsed):
return 1
else:
return 0

if not tree.search(parsed):
#print "discarded:", parsed
return 0

if to_parse.startswith('('):
end = to_parse.find(')')
sub = to_parse[end + 1:]
ret = 0
for item in to_parse[1:end]:
ret += parse_words(sub, parsed + item)
return ret
else:
return parse_words(to_parse[1:], parsed + to_parse[0])

if __name__ == '__main__':
input = sys.stdin.readline()
input = input.split()

tree = Node('')

for i in range(0, int(input[1])):
word = sys.stdin.readline()
tree.add(word)

for i in range(1, int(input[2]) + 1):
line = sys.stdin.readline()
line = line.strip()
ret = parse_words(line, '')
print("Case #%d: %d" % (i, ret))

 Legal, resolvi até que rápido e a solução ficou até bonitinha. Só que a segunda entrada demorou 30 minutos pra processar! Usando "python -m cProfile alien_language.py" descobri que o gargalo era a comparação.

Fiz a mesma solução em Java e ela demorava 1 minuto e meio para executar. Bem melhor, mas será que todo mundo fez assim?

Olhando na internet, vi que as soluções mais comuns eram muitos mais simples que a minha: sabendo o tamanho da lista e o tamanho das palavras, é só criar uma matriz com todas as alternativas possíveis e depois comparar. Sem recursão, sem medo de estourar a memória (já sabemos o tamanho de antemão) nem nenhuma outra complicação! E executa rapidão.

A melhor solução que encontrei no entanto foi essa, em python mesmo:

 

import re
import sys

input = sys.stdin.readline()
input = input.split()

list = []

for i in range(0, int(input[1])):
word = sys.stdin.readline()
list.append(word)

for i in range(1, int(input[2]) + 1):
line = sys.stdin.readline()
line = line.strip()
line = line.replace('(', '[')
line = line.replace(')', ']')
ret = 0
m = re.compile(line)
for item in list:
if m.match(item):
ret += 1
print("Case #%d: %d" % (i, ret))

 

Bem mais simples não? E bem rápida

Quinta-feira Dez 10, 2009

Complicando as coisas

Por causa do trânsito, na quinta passada fiquei até mais tarde no trabalho e participei do Dojo.

Achei o problema escolhido muito interessante e aproveitei ele para praticar um pouco. Depois de acabar, achei que o código que fiz ficou bem legal, e me empolguei para estudar mais.

Escolhi os problemas do Google Code Jam para resolver. No primeiro problema tudo bem, mas já no segundo percebi que eu complico as coisas: o problema era para ser resolvido com uma matriz, e eu fiz uma árvore. Resultado: meu código demorava 30 minutos na versão Python e 1 minuto e meio na versão Java. De um amigo meu demorava meio minuto, e na versão "ótima" (uma gambiarra total), demorava 5 segundos em Python!

Este aqui é outro exemplo de como eu complico as coisas: o problema é um labirinto, e o programa deve ir marcando o caminho. Como não tem o tamanho do labirinto, eu vou ajustando conforme vou andando. Eis o resultado:

import os
import sys

class Maze(object):
'''Maze object.
(r, c):
(0, 0) (0, 1)
(1, 0) (1, 1)'''

# Direction constants
SOUTH = 0
WEST = 1
NORTH = 2
EAST = 3

def _setDirection(self, value):
self._direction = value % 4

direction = property(lambda s: s._direction, _setDirection, None, None)

def invert(self):
self.direction += 2
self._outside = True

def turnRight(self):
self.direction += 1

def turnLeft(self):
self.direction -= 1

# Wall openings
WO_CLOSED = 0
WO_NORTH = 1
WO_SOUTH = 2
WO_WEST = 4
WO_EAST = 8

wo = {
NORTH: WO_NORTH,
SOUTH: WO_SOUTH,
WEST: WO_WEST,
EAST: WO_EAST}

def __init__(self):
self.map = [[Maze.WO_CLOSED]]
self._direction = Maze.SOUTH
self.position = (-1, 0)
self._outside = True

def walk(self):
self._adjustMap()
self._markLast()
row, column = self.position
if self.direction == Maze.SOUTH:
row += 1
elif self.direction == Maze.WEST:
column -= 1
elif self.direction == Maze.NORTH:
row -= 1
elif self.direction == Maze.EAST:
column += 1
self.position = (row, column)

# Inside maze: ajust map
self._outside = False

def _markLast(self):
if self._outside:
return
opening = Maze.wo[self.direction]
row, column = self.position
self.map[row][column] |= opening

def _adjustMap(self):
if self._outside:
return
size = self.position[0] + 1
if size > len(self.map):
self._addRow()
column = self.position[1]
if column < 0:
self._addColumnLeft()
elif column >= len(self.map[0]):
self._addColumnRight()

def _addRow(self):
size = len(self.map[0])
row = []
while len(row) < size:
row.append(Maze.WO_CLOSED)
self.map.append(row)

def _addColumnLeft(self):
for row in self.map:
row.insert(0, Maze.WO_CLOSED)
row, column = self.position
self.position = (row, column + 1)

def _addColumnRight(self):
for row in self.map:
row.append(Maze.WO_CLOSED)

def __str__(self):
'Print inside representantion'
ret = ''
for i in range(0, len(self.map)):
for j in range(0, len(self.map[0])):
ret += "%x" % self.map[i][j]
ret += os.linesep
return ret

if __name__ == '__main__':
x = sys.stdin.readline()
x = int(x)
for i in range(1, x + 1):
maze = Maze()
input = sys.stdin.readline()
input = input.strip()
for command in input:
if command == 'R':
maze.turnRight()
elif command == 'L':
maze.turnLeft()
elif command == 'W':
maze.walk()
elif command == ' ':
maze.invert()
print "Case #%d:" % i
print maze

 O switch faz um pouco de falta no final, mas o mais interessante é ficar ajustando o tamanho do mapa conforme vai andando. Depois olhando na net como outras pessoas fizeram, fiquei realmente chateado: bastava usar um dicionário onde as chaves eram as tuplas das casas ex. (0, 0).

Depois postarei o outro problema para mostrar como eu complico demais as coisas.