原文:
towardsdatascience.com/im-doing-the-advent-of-code-2024-in-python-day-4-f0dacf4529a6
欢迎来到第四天!
第一天介绍,如何获取你自己的谜题输入,以及第一天的谜题解决方案。
第二天, 第三天
我们将在第四天的谜题中学习以下主题:
Python 中的列表推导
如何处理一维和二维 NumPy 数组
如何转置和翻转 NumPy 数组
到我写这篇文章的时候,已经发布了 22 个谜题,每个谜题有两个部分。每个部分都算作一个开始,以下是我的当前进度:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/8750a11b9f7abe7d44ab75c5e6fe0f95.png
![作者图片]
第四天 – 第一部分
第四天的谜题输入是一个包含字母 X、M、A 和 S 的长字符串。我的谜题输入的前 10 行看起来像这样:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/907c9c23a9a047a85f5156692b4449a2.png
![作者图片]
第四部分谜题的第一部分是要找到文本中“XMAS”的出现次数。难点在于所有水平、垂直和对角线上的出现都计算在内(包括正向和反向)。
这里有一个示例图,展示了在谜题输入中要寻找的内容:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/033846363e8b70307e84768025c22d19.png
![作者图片]
因此,我们需要在水平、垂直和对角线上计数“XMAS”和“SAMX”。
让我们从获取谜题输入开始。
importrequests session_cookie="your session cookie"# or session_cookie = os.getenv(SESSION_COOKIE)defget_puzzle_input(day,session_cookie,year=2024):url=f"https://adventofcode.com/{year}/day/{day}/input"cookies={"session":session_cookie}response=requests.get(url,cookies=cookies)ifresponse.status_code==200:returnresponse.textelse:returnNonepuzzle_input=get_puzzle_input(4,session_cookie)为了进行水平计数,我将谜题输入分割成一行行的列表,并在列表推导中使用count方法。
# split at new line to convert the puzzle input into a list of lineslines=puzzle_input.split("n")[:-1]# count the occurrences of XMAS and SAMX for each linehorizontal_counts=[line.count("XMAS")+line.count("SAMX")forlineinlines]horizontal_counts列表包含每行中 XMAS 和 SAMX 出现的次数。sum(horizontal_counts)给出了总的水平计数。
有不同的方法来找到垂直计数。我想使用 NumPy 数组。第一步是将lines列表转换成二维数组。
importnumpyasnp arr=np.array([list(line)forlineinlines])print(arr.shape)# output(140,140)print(arr)# outputarray([['M','S','M',...,'M','X','S'],['S','A','A',...,'S','A','A'],['X','M','X',...,'M','X','S'],...,['M','A','S',...,'S','S','S'],['S','A','M',...,'A','A','M'],['M','M','S',...,'M','M','M']],dtype='<U1')谜题输入中有 140 行,每行有 140 个字符。
要计算“XMAS”和“SAMX”的垂直出现次数,我将这个数组转置,这样就将垂直变成了水平。
arr_transpose=arr.transpose()arr_transpose# outputarray([['M','S','X',...,'M','S','M'],['S','A','M',...,'A','A','M'],['M','A','X',...,'S','M','S'],...,['M','S','M',...,'S','A','M'],['X','A','X',...,'S','A','M'],['S','A','S',...,'S','M','M']],dtype='<U1')然后,我为每一行创建一个字符串,通过连接字母可以使用与水平计数相同的方法。
vertical_lines=["".join(line)forlineinarr_transpose]vertical_lines[0]# output'MSXSSSMSMMMXMXMXSSXMASAMMMMSMXMMSMSXMASMMXMASMMMMMSSSXMASAMXMMXSAMMMXAXSMMMMSXMAMMSAMSAMSASMMMASMSSMMSAMASXMXXXSSMMSAMXXSXSXSMSAMXXXMASASMSM'然后,我可以使用与水平计数相同的方法。
vertical_counts=sum([line.count("XMAS")+line.count("SAMX")forlineinvertical_lines])现在我们有了水平和垂直计数。最后一个是对角线上的出现次数。我们可以使用 NumPy 数组的diagonal方法创建对角线列表。np.diagonal(arr, 0)给出了主对角线。通过将 0 替换为正整数和负整数,我们可以获得所有对角线。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/dcfe48bd0a66ae6b9c96d2caf029ed2d.png
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f558e22349a1fb7bfaf8d85a98ed0ac.png
让我们写一个 for 循环来创建所有对角线的列表。
diagonals=[]foriinrange(-len(arr)+1,len(arr)-1):line="".join(np.diagonal(arr,i))diagonals.append(line)# check the first 10 diagonalsdiagonals[:10]# output['M','SM','MAS','SAMA','AXSMM','SXMASX','AMMASAS','MXMASXMM','XSAMMASAM','XMXMMAXSAM']我们处理了从左上角到右下角的对角线,但我们还需要检查从右上角到左下角的对角线。
我们可以使用相同的循环来完成,但我们只需要翻转二维数组,这可以通过fliplr方法完成。
diagonals_flipped=[]foriinrange(-len(arr)+1,len(arr)-1):line="".join(np.diagonal(np.fliplr(arr),i))diagonals_flipped.append(line)我们现在可以计算两个对角线和翻转对角线上的“XMAS”和“SAMX”出现的次数。
diagonal_counts=sum([line.count("XMAS")+line.count("SAMX")forlineindiagonals])diagonal_counts_flipped=sum([line.count("XMAS")+line.count("SAMX")forlineindiagonals_flipped])# final answerhorizontal_counts+vertical_counts+diagonal_counts+diagonal_counts_flipped当我们将我们计算的所有计数相加时,我们得到第一部分的最终答案。
第四天 – 第二部分
在这个谜题的第二部分,我们要找到“X-MAS”的出现次数,这意味着有两个 MAS 值以 X 的形状出现。
下面是我们将在谜题输入中寻找的内容("."代表任何字符)。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f558e22349a1fb7bfaf8d85a98ed0ac.png
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f558e22349a1fb7bfaf8d85a98ed0ac.png
我解决这个谜题的方法是创建一个从左上角开始的 3×3 窗口,并向下移动一个位置。在每次移动中,我都会检查 3×3 窗口是否等于我们想要的任何 X-MAS 形状。
一旦我到达底部,我就回到顶部并向右移动一个步骤。我重复同样的过程,直到我到达右下角。
我试图在下面的图中展示这个过程:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/d3c5ef3a7254833a8f8b243c7f7cfa88.png
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4f558e22349a1fb7bfaf8d85a98ed0ac.png
要在代码中执行此操作,我使用了两个 for 循环。让我们首先定义我们将要寻找的 X-MAS 形状。
xmas1=[['M','.','M'],['.','A','.'],['S','.','S']]xmas2=[['S','.','S'],['.','A','.'],['M','.','M']]xmas3=[['M','.','S'],['.','A','.'],['M','.','S']]xmas4=[['S','.','M'],['.','A','.'],['S','.','M']]我们现在可以创建嵌套的 for 循环来遍历第一部分创建的二维数组。
xmas_cnt=0foriinrange(len(arr)-2):forjinrange(len(arr)-2):res=arr[j:j+3,i:i+3].copy()# very important to use copy hereres[0][1]="."res[1][0]="."res[1][2]="."res[2][1]="."if(res==xmas1).sum()==9:xmas_cnt+=1if(res==xmas2).sum()==9:xmas_cnt+=1if(res==xmas3).sum()==9:xmas_cnt+=1if(res==xmas4).sum()==9:xmas_cnt+=1我们创建了一个变量(xmas_cnt)来跟踪我们检测到的 X-MAS 形状。如果 3×3 数组等于我们想要的 X-MAS 形状之一,我们就将这个变量的值增加一。xmas_cnt的最终值就是第二部分的答案。
我们将无关位置上的字母替换为"."。否则,我们需要将 3×3 数组与更多的组合进行比较。
非常重要的是要注意,在每一步中,我们需要创建 3×3 数组的副本。否则,当我们用"."替换字母时,我们会修改原始数组,最终会错过一些匹配项。
第四天的谜题是关于列表推导和 NumPy 数组操作的良好实践。我们还学习了嵌套循环结构,如何转置和翻转二维数组。
如果你计划在数据科学生态系统中找工作,所有这些都是在你需要时相当常用的基本操作。
感谢您的阅读。请期待第五天。