Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PyCon2017_-_Code_challenges.pdf

 PyCon2017_-_Code_challenges.pdf

Alicia Pérez Jiménez

September 03, 2017
Tweet

More Decks by Alicia Pérez Jiménez

Other Decks in Technology

Transcript

  1. https://code.google.com/codejam/ convocatoria anual varias rondas final “en vivo” retos más

    complejos https://adventofcode.com/ 25 días seguidos (1-Dic a 25-Dic) 1 reto cada día 2 partes/reto ”tableros privados”
  2. Algunos concursos interesantes ▶ 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
  3. 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
  4. 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
  5. 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
  6. 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,)
  7. 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
  8. 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 eje imaginario 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
  9. ¿qué hemos aprendido? ▶ strip(), split() ▶ namedtuples() ▶ string

    slicing ▶ números complejos ▶ assert como test unitarios sencillos ▶ set()
  10. 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
  11. transponer filas a columnas 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]) # o también for i, column in enumerate(columns): for message in messages: column.append(message[i]) messages = """ eedadn drvtee eandsr … """ # o también for message in messages: for char, column in zip(message, columns): column.append(char) # o también 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)] # o también columns = zip(*messages)
  12. contar # bad counting example counts = {} for char

    in column: if not char in counts: counts[char] = 1 else: counts[char] += 1 sorted_counts = sorted( counts.items(), key=itemgetter(1) ) most_common = sorted_counts[-1][0] # slightly better counting example counts = defaultdict(int) for char in column: counts[char] += 1 sorted_counts = sorted( counts.items(), key=itemgetter(1) ) most_common = sorted_counts[-1][0]
  13. ‘most common’ → collections.Counter >>> from collections import Counter >>>

    Counter('abracadabra') Counter({'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}) >>> Counter('abracadabra').most_common() [('a', 5), ('r', 2), ('b', 2), ('c', 1), ('d', 1)]
  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. todo junto messages = messages.strip().split('\n'); columns = zip(*messages) solution =

    ''.join(Counter(c).most_common()[0][0] for c in columns) print solution zip(), enumerate(), defaultdict() comprensiones collections.Counter split() y join()
  16. ¿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
  17. AoC - Día 9 ▶ https://adventofcode.com/2016/day/9 ADVENT ADVENT A(1x5)BC ABBBBBC

    (3x3)XYZ XYZXYZXYZ (6x1)(1x3)A (1x3)A (6x1)(1x3)A AAA X(8x2)(3x3)ABCY X(3x3)ABC(3x3)ABCY X(8x2)(3x3)ABCY XABCABCABCABCABCABC Y (27x12)(20x12)(13x14)(7x10)(1x12)A longitud 241920 (25x3)(3x3)ABC(2x3)XY(5x2) PQRSTX(18x9)(3x2)TWO(5x7)SEVEN longitud 445
  18. unos casos de prueba def do_test(lines): for tc in lines.strip().split('\n'):

    compressed, expected = tc.strip().split(',') uncompressed = uncompress(compressed) assert uncompressed == expected, \ "bad result %s, expected %s" % (uncompressed, expected) test_cases = """ ADVENT,ADVENT A(1x5)BC,ABBBBBC (3x3)XYZ,XYZXYZXYZ … """
  19. A(1x5)BC ADVEN T X(8x2)(3x3)ABCY def uncompress(c): m = re.match(r'([^()]*)\((\d+)x(\d+)\)(.*)', c)

    if not m: return len(c) pre, length, reps, rest = m.groups() length, reps = int(length), int(reps) return len(pre) + \ uncompress(rest[:length]) * reps + \ uncompress(rest[length:]) A(1x5)BC pre, length, reps, rest ([^()]*)\((\d+)x(\d+)\)(.*) ABBBBB X(8x2)(3x3)ABCY pre, length, reps, rest ([^()]*)\((\d+)x(\d+)\)(.*) XABCABCABCABCABCA regex
  20. regex - verbose r = re.compile(r""" ([^()]*) # pre \((\d+)x(\d+)\)

    # ( length x reps ) (.*) # rest """, re.VERBOSE)
  21. Code Jam, Qualification Round 2016, Prob B - → +

    1 -+ → ++ 1 +- → -- ++ 2 +++ → 0 --+- → +++- ---- ++++ 3
  22. 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
  23. def shorten(S): return ''.join(ch for ch, _ in itertools.groupby(S)) def

    flip(S, i): top_pankakes = ['+' if p == '-' else '-' for p in reversed(S[0:i])] return ''.join(top_pankakes) + S[i:] 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
  24. def minimumFlips(pancakes): groupedHeight = 1 + pancakes.count('-+') + pancakes.count('+-') if

    pancakes.endswith('-'): return groupedHeight else: return groupedHeight - 1 tiene truco! - → + 1 -+ → ++ 1 +- → -- ++ 2 +++ → 0 --+- → +++- ---- ++++ 3
  25. 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
  26. 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