Advent of Code: Question 1

3 minute read

Published:

A Gauntlet Has Been Thrown Down

OK, that is a bit of hyperbole. Emma, a connection of mine on LinkedIn, has been participating in Advent of Code. She commented that she would like to see how I would approach some of these problems, so I figured I would start doing some myself.

Familiar Territory

I learned python through doing Project Euler problems, so I am no stranger to these types of challenges. However, I usually solved the problems through brute force search. Not very clever.

In order to push myself a little more, I am going to take a page out of my own book and vow not to use for loops for the entire challenge. That is right, everything has to be functional programming or vectorization. I’m going to use python to complete these challenges (just to show Emma how cool python can be ;) ).

Let’s see how I solved the first problem:

Problem 1

Problem one provides me with a sequence of digits. It asks me to sum those digits which match the next digit in the sequence. The sequence wraps around itself, so if the first and last match, then that also counts. You can read more here.

It would be easy to loop through the sequence and compare digits. Alas, I can’t do that.

Instead, I’ll create two arrays by lopping off the first digit from the sequence and lopping of the last digit from the sequence. Comparing the first entry of the first array with the first entry of the second array is equivalent to comparing the first and second digit in the sequence (can you see why?).

This is easy to write in python

import numpy as np
my_input = '''516299281491169512719425276194596424291268712697155863651846937925928456958813624428156218468331423858422613471962165756423837756856519754524985759763747559711257977361228357678293572698839754444752898835313399815748562519958329927911861654784216355489319995566297499836295985943899373615223375271231128914745273184498915241488393761676799914385265459983923743146555465177886491979962465918888396664233693243983969412682561799628789569294374554575677368219724142536789649121758582991345537639888858113763738518511184439854223386868764189133964543721941169786274781775658991329331759679943342217578532643519615296424396487669451453728113114748217177826874953466435436129165295379157226345786756899935747336785161745487933721527239394118721517195849186676814232887413175587327214144876898248571248517121796766248817366614333915154796983612174281237846165129114988453188844745119798643314857871527757831265298846833327863781341559381238458322786192379487455671563757123534253463563421716138641611915686247343417126655317378639314168461345613427262786624689498485599942336813995725145169355942616672812792174556866436158375938988738721253664772584577384558696477546232189312287262439452141564522329987139692281984783513691857538335537553448919819545332125483128878925492334361562192621672993868479566688564752226111784486619789588318171745995253645886833872665447241245329935643883892447524286642296955354249478815116517315832179925494818748478164317669471654464867111924676961162162841232473474394739793968624974397916495667233337397241933765513777241916359166994384923869741468174653353541147616645393917694581811193977311981752554551499629219873391493426883886536219455848354426461562995284162323961773644581815633779762634745339565196798724847722781666948626231631632144371873154872575615636322965353254642186897127423352618879431499138418872356116624818733232445649188793318829748789349813295218673497291134164395739665667255443366383299669973689528188264386373591424149784473698487315316676637165317972648916116755224598519934598889627918883283534261513179931798591959489372165295'''

#Make sequence an array and convert to integer
my_input = np.array(list(my_input), dtype=int)


def check_condition(x):


    #Lopp off first and last digits and compare directly
    ix = x[:-1]==x[1:]

    #Sum the numbers which satisfy the condition
    s = np.sum(x[:-1][ix])

    #Check the boundary
    if x[0]==x[-1]:
        s+=x[0]
    return s

#returns 1251

Part 2?

Oh a second part? Now it is asking me to sum those digits which are identical to the digit half way around the array. This is particularly tricky because the sequence is circular. Still easy.

I can use modular arithmetic to make sure I am wrapping around the sequence. Here is how I did it:

def check_condition_part2(x):

    ## NEW CODE##
    ix = np.arange(x.size) #Incidies of my array
    wix = (ix+ix.size/2)%ix.size #Comparison index.  Add half to ix and then ensure everything is mod size of sequence
    six = x==x[wix.astype(int)] #Where to we find identical digits
    s= x[six].sum() #add to sum

    return s

     #returns 1244

I initially got this question wrong because I didn’t read that I was not supposed to include my answer from part one. So the real answer is $2495-1251 = 1244$.

Comments and Conclusion

OK, that was really fun. Thanks to Emma for challenging me to do this. I’m pretty pumped I did it without looping through the sequence. Hopefully, I can find my way around using loops in the future.

Bonus Round

How would I do it with pandas? Very easily. Here is how I solved this with pandas

import pandas as pd

#create the dataframe
df = pd.DataFrame({'digit':my_input})

#Shift the digit column for comparison
df['digit_next'] = df.digit.shift().fillna(my_input[-1]).astype(int)

#Check to see if they are the same
df['is_same'] = df.digit==df.digit_next

df.loc[df.is_same,'digit'].sum()

Part 2

import pandas as pd

df = pd.DataFrame({'digit':my_input})

#create another dataframe but change the index
df2_index = (np.arange(my_input.size)+my_input.size/2)%my_input.size
df2 = pd.DataFrame({'digit':my_input}, index = df2_index)

#Use a join to find the digit halfway through the sequence
df3 = df.join(df2, rsuffix='_next')

#Find same digits and sum
df3['is_same'] = df3.digit==df3.digit_next
df3.loc[df3.is_same,'digit'].sum()