Solución a 4 retos de programación de Advent of Code y Google Code Jam para ilustrar construcciones pythónicas de código y algunas utilidades poco conocidas del lenguaje.
4. AoC - Día 6 https://adventofcode.com/2016/day/6
5. AoC - Día 6
messages = """
eedadn
drvtee
eandsr
raavrd
atevrs
tsrnev
sdttsa
rasrtv
nssdts
ntnada
…
"""
https://adventofcode.com/2016/day/6
convertir las columnas en listas →
transponer
contar la frecuencia de las letras
quedarse con el máximo de cada columna
6. transponer filas a columnas
messages = """
eedadn
drvtee
eandsr
…
"""
messages = messages.strip().split('n');
ncolumns = len(messages[0])
assert all(len(message) == ncolumns
for message in messages)
# columns = [[]] * ncolumns # bad!!
columns = [[] for i in range(ncolumns)]
for i in range(ncolumns):
for message in messages:
columns[i].append(message[i])
7. zip(), iteración en paralelo
messages = """
eedadn
drvtee
eandsr
…
"""
messages = messages.strip().split('n');
ncolumns = len(messages[0])
columns = [[] for i in range(ncolumns)]
for message in messages:
for char, column in zip(message, columns):
column.append(char)
8. comprensiones de listas
messages = """
eedadn
drvtee
eandsr
…
"""
messages = messages.strip().split('n');
ncolumns = len(messages[0])
columns = [[] for i in range(ncolumns)]
for i, column in enumerate(columns):
column = [message[i] for message in messages]
# o también
columns = [[message[i] for message in messages]
for i in range(ncolumns)]
14. combinar los más comunes
solution = ""
for column in columns:
solution += Counter(column).most_common()[0][0]
solution = ''.join(Counter(c).most_common()[0][0] for c in columns)
15. ¿qué hemos aprendido?
enumerate(), zip(), defaultdict()
split(), join()
comprensiones
collections.Counter()
messages = messages.strip().split('n');
columns = zip(*messages)
solution = ''.join(Counter(c).most_common()[0][0] for c in columns)
print solution
16. AoC - Día 1 https://adventofcode.com/2016/day/1
17. AoC - Día 1
https://adventofcode.com/2
016/day/1
parsear las instrucciones
representar las posiciones
y direcciones
aplicar las instrucciones
calcular la distancia
Manhattan
input = """
L2, L5, L5, R5, L2, L4, R1, R1, L4, R2, R1, …
"""
(0, 0) hacia el Norte
N
S
W E
2
5
5
(3, -5) → dist=8
18. parsear la entrada
input = """
L2, L5, L5, R5, L2, L4, R1, R1, L4, R2, R1, …
"""
def solve(input):
input = [s.strip() for s in input.split(',')]
direction = N
pos = Vector(0, 0)
for step in input:
instruction = Instruction(LEFT if step[0] == 'L' else RIGHT, int(step[1:]))
direction = turn(direction, instruction.side)
for _ in range(0, instruction.advance):
pos = advance(pos, directions[direction])
return pos
['L2', 'L5', 'L5', 'R5’, …]
from collections import namedtuple
Instruction = namedtuple('Instruction', "side advance")
t = Instruction(-1, 10)
t[0] == -1
t.side == -1
t[1] == 10
t.advance == 10
19. Vector = namedtuple('Vector', "x y")
directions = {
N: Vector(0, 1), # equiv. Vector(x=0, y=1)
E: Vector(1, 0),
S: Vector(0, -1),
W: Vector(-1, 0),
}
def advance(pos, direction):
delta = directions[direction]
return Vector(pos.x + delta.x, pos.y + delta.y)
# return Vector(*map(sum, zip(pos, delta)))
representar posiciones y direcciones
# directions
N = 0 # clockwise
E = 1
S = 2
W = 3
# sides
RIGHT = +1
LEFT = -1
def turn(direction, side):
return (direction + side) % 4
assert turn(N, RIGHT) == E
assert turn(N, LEFT) == W
assert turn(S, RIGHT) == W
assert turn(S, LEFT) == E
…
# N
# |
# W --+-- E
# |
# S
20.
21. aplicar las instrucciones y calcular distancia
def solve(input):
input = [s.strip() for s in input.split(',')]
direction = N
pos = Vector(0, 0)
for step in input:
instruction = Instruction(LEFT if step[0] == 'L' else RIGHT, int(step[1:]))
direction = turn(direction, instruction.side)
for _ in range(0, instruction.advance):
pos = advance(pos, direction)
return pos
if __name__ == '__main__':
input = """ … """
pos = solve(input)
dist = abs(pos[0]) + abs(pos[1])
print "distance to (0,0) = %d" % (dist,)
22. complicación (2ª parte)
si pasas dos veces por el mismo sitio, terminamos
def solve(input):
input = [s.strip() for s in input.split(',')]
direction = N
pos = Vector(0, 0)
already_visited = {pos}
for step in input:
instruction = Instruction(LEFT if step[0] == 'L' else RIGHT, int(step[1:]))
direction = turn(direction, instruction.side)
for _ in range(0, instruction.advance):
pos = advance(pos, direction)
if pos in already_visited:
return pos
already_visited.add(pos)
return pos # no se ha repetido ninguna posición
23. hay gente muy PRO
def solve(input):
input = [s.strip() for s in input.split(',')]
direction = 0+1j # (0,1)
pos = 0+0j # (0,0)
already_visited = {pos}
for step in input:
side, advance = step[0], int(step[1:])
direction *= 1j if side == 'L' else -1j
for _ in range(advance):
pos += direction
if pos in already_visited:
return pos
already_visited.add(pos)
return pos
if __name__ == '__main__':
input = """ … """
pos = solve(input)
dist = abs(pos.real) + abs(pos.imag)
print "distance to (0,0) = %d" % (dist,) http://mathworld.wolfram.com/Rotation.html
N
S
W E
eje real
ejeimaginario
números complejos
operaciones con números complejos
• suma: 3+5j + 2-4j = 5+1j
• rotación:
• 90º izquierda = mult por 0+1j
• 90º derecha = mult por 0-1j
(2,4) = 2+4j
28. def BFS(S):
visited, queue = set(), [<initial_state>]
while queue:
depth, current = queue.pop(0)
if is_solution(current):
return depth
if current not in visited:
visited.add(current)
<add possible states to queue>
return None
optimización? BFS
29. def flip(S, i):
top_pankakes = ['+' if p == '-' else '-' for p in reversed(S[0:i])]
return ''.join(top_pankakes) + S[i:]
assert flip('+--+', 1) == '---+'
assert flip('+--+', 2) == '-+-+'
assert flip('+--+', 3) == '++-+'
assert flip('+--+', 4) == '-++-'
dando la vuelta a las tortitas
30. def shorten(S):
return ''.join(ch for ch, _ in itertools.groupby(S))
def solve(S):
visited, queue = set(), [(0, shorten(S))]
while queue:
depth, current = queue.pop(0)
if current.count('+') == len(current):
return depth
if current not in visited:
visited.add(current)
for i in range(1, len(current)+1):
flipped = shorten(flip(current, i))
if flipped not in visited:
queue.append((depth+1, flipped))
return None
implementación BFS
40. resumen
zip, enumerate
tuple, set, dict
collections
namedtuple, Counter, defaultdict
itertools
groupby
product, permutations
slicing [:], join, split
assert
regex
python es MUY expresivo
tiene su propio “acento”
nunca paras de aprender
practicar, practicar, practicar
41. trucos
los tipos de problemas se repiten
librería de funciones de uso común
vectores
lectura de la entrada
strip(), split()
pensar/escribir/pintar antes de escribir código
casos de prueba pequeños
assert
fuerza bruta, no suele funcionar
pero da ideas de cómo resolverlo bien
42. Referencias
Cracking the Code Interview, Gayle Laakmann McDowel
Fluent Python, Luciano Ramalho
Python Essential Reference, David M. Beazley
reddit
Advent of Code
25 días seguidos
1 reto cada día
2 partes/reto
Code Jam de Google
convocatoria anual
varias rondas
más complejos
https://code.google.com/codejam/
karate kid
practicar
descubrir nuevas herramientas
entrevistas de programación