搜索
首页后端开发Python教程如何为 Code 4 的出现编写排序算法

在上一篇文章中,我简单提到我将参加今年的“代码降临”活动。巧合的是,在其中一个谜题中,特别是在第 5 天发布的谜题中,涉及修复列表中页面的顺序。这是在我发布关于实现排序算法的文章后不久,所以我认为我应该写一下它。

How to code a Sorting Algorithm for Advent of Code 4
描绘某种排序算法的可爱图像

对于那些没有听说过“Advent of Code”的人来说,这是由 Eric Wastl 主办的年度活动。每年,它都会讲述一个以节日为背景的故事,今年的故事是关于寻找首席历史学家,他可能是每次大型圣诞雪橇发射中的重要人物。该挑战将于每年12月1日持续至25日。每天,剧情都会进展,并且包含一个编程谜题(并且带有输入)。

在故事叙述中,谜题通常被明确定义,并包含测试用例。每个谜题都分为两部分,第二部分只有在提交第一个答案后才会出现。

参与者可以用任何语言实现任何算法,甚至完全跳过编程,只要派生的答案匹配即可。今年我尝试用 Python 编写解决方案,9 天后,我觉得我在整个过程中学到了很多东西。

第五天,故事要求帮忙印刷安全手册。输入包含页面规则和精灵尝试打印的页面列表。

47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47

让我们从解析输入开始:

def parse(
    input: str,
) -> tuple[tuple[tuple[int, int], ...], tuple[tuple[int, ...], ...]]:
    def inner(
        current, incoming
    ) -> tuple[tuple[tuple[int, int], ...], tuple[tuple[int, ...], ...]]:
        rules, pages = current

        if "|" in incoming:
            return rules + (
                tuple(int(item) for item in incoming.strip().split("|")),
            ), pages

        else:
            return rules, pages + (
                tuple(int(item) for item in incoming.strip().split(",")),
            )

    return reduce(
        inner, filter(lambda line: line.strip(), input.strip().splitlines()), ((), ())
    )

该函数接收名为 input 的字符串形式的输入,使用 .splitlines() 将其分成几行,然后发送到内部函数以生成两个元组,一个用于页面规则,另一个用于页面序列。该代码通过分隔符 | 区分两种类型的定义。表示页面规则, , 表示页面。

在拼图的第一部分,故事要求检查页面是否按顺序排列。让我们从实现一个完成这项工作的函数开始:

def check_pair(rules: tuple[tuple[int, int], ...], alpha: int, beta: int) -> bool:
    return (beta, alpha) not in rules

然后另一个函数发送所有页面组合(combinations((1,2,3), 2) 返回 1,2, 1,3 和 2,3):

from itertools import combinations

def check_pages(rules: tuple[tuple[int, int], ...], pages: tuple[int, ...]) -> bool:
    return all(
        check_pair(rules, alpha, beta)
        for alpha, beta in combinations(pages, 2)
    )

我将这两个函数分成单独的函数的主要原因是我想让每个部分尽可能小。根据我的经验,保持事物足够小不仅可以使其可测试,而且通常还有助于调试最终输入(通常很大)。

很多时候,第 2 部分会让人感到惊讶,并且经常会发现它要求对第 1 部分的代码设计进行修订。这可能是您已实现的内容的一个小变化,或者需要不同的功能不同目标的调用顺序等。我确实保持在工作中编写短函数的习惯(作为注释的替代)。

像这样的小函数只有名字好才有效,所以你需要注意命名。这需要练习,但是一旦你熟练了,这种方法就可以使代码变得非常自我记录。较大规模的函数读起来就像一个故事,读者可以根据需要选择深入了解哪些函数以了解更多细节。

引用自 Martin Fowler 撰写的题为 Function Length 的文章

回到谜题。

最后,谜题要求计算所有页面排序正确的情况下中间页码的总和。

47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47

非常简单,如果你已经完成了所有正确的事情,那么它只是一个列表理解(因为 Python 开发人员更喜欢这个而不是映射/过滤器)。

接下来是排序算法:

继续第 1 部分,第二部分想要中间页的总和,但适用于页面排序不正确的情况。该指令还要求在检索中间页码之前修复顺序。

虽然我的同行在没有成熟的排序算法的情况下设法解决了这个问题,但我决定按照前面描述的难题(在解释页面规则的部分中)的确切方式来完成它。我已经完成了比较部分(check_pair),现在我需要一个可以移动元素的函数。

def parse(
    input: str,
) -> tuple[tuple[tuple[int, int], ...], tuple[tuple[int, ...], ...]]:
    def inner(
        current, incoming
    ) -> tuple[tuple[tuple[int, int], ...], tuple[tuple[int, ...], ...]]:
        rules, pages = current

        if "|" in incoming:
            return rules + (
                tuple(int(item) for item in incoming.strip().split("|")),
            ), pages

        else:
            return rules, pages + (
                tuple(int(item) for item in incoming.strip().split(",")),
            )

    return reduce(
        inner, filter(lambda line: line.strip(), input.strip().splitlines()), ((), ())
    )

假设我有 1,2,3,4,5,该函数将传入的数字移动到当前数字的前面。假设current = 2,传入= 4,那么我将得到1,4,2,3,5作为回报(假设我们是按照递增的数值排列)。

How to code a Sorting Algorithm for Advent of Code 4
我向朋友解释算法的失败尝试

下一步是将我手写草稿中显示的算法转化为实际代码。

def check_pair(rules: tuple[tuple[int, int], ...], alpha: int, beta: int) -> bool:
    return (beta, alpha) not in rules

是的,不幸的是它是递归的。我应该发布第一个版本,这可能更容易阅读:

from itertools import combinations

def check_pages(rules: tuple[tuple[int, int], ...], pages: tuple[int, ...]) -> bool:
    return all(
        check_pair(rules, alpha, beta)
        for alpha, beta in combinations(pages, 2)
    )

两者本质相同,只是最终的功能版本略有优化。参考草稿截图,我有两个指针,黄色下划线在代码中名为指针,传入蓝色下划线。

算法的工作原理如下:

  1. 首先将指针设置为第一个元素。
  2. 最初传入的总是它旁边的元素。
  3. 传入的指针将一次遍历一个元素,如果违反规则,会将值移至当前元素之前。
  4. 一旦发生这种情况,传入指针将重置,并移回当前的下一个。
  5. 当前指针没有改变位置,但它现在指向上一步中插入的新元素。

如果传入指针设法逐步遍历列表的其余部分而没有引入任何更改,则我们将当前指针前进(并且传入指针重新初始化到它旁边的位置),并再次重复该过程。

算法完成对最后 2 个元素的比较后,该过程结束,然后返回排序后的页面作为结果。然后,我们可以继续组装第 2 部分中的所有内容:

47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47

两个部分的代码相似。它只是对第 1 部分进行了轻微修改,只是过滤器子句中的一些变化,并且 get_middle 接收的是排序列表。本质上, if 就好像我正在以函数形式的构建块以稍微不同的组合来组装答案。

虽然这仍然不是一个有效的算法,因为时间复杂度接近 O(n^2)。根据windsurf中的cascade AI-companion,该算法在某些方面类似于插入排序(是的,这就是AI工具有用的时候,为算法提供解释)。

今天就这样,我很高兴算法运行良好,尽管我的生活目前一团糟(由于资金问题刚刚从一个项目中退出)。希望随着时间的推移事情会变得更好,下周我会再写。

以上是如何为 Code 4 的出现编写排序算法的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python中的合并列表:选择正确的方法Python中的合并列表:选择正确的方法May 14, 2025 am 12:11 AM

Tomergelistsinpython,YouCanusethe操作员,estextMethod,ListComprehension,Oritertools

如何在Python 3中加入两个列表?如何在Python 3中加入两个列表?May 14, 2025 am 12:09 AM

在Python3中,可以通过多种方法连接两个列表:1)使用 运算符,适用于小列表,但对大列表效率低;2)使用extend方法,适用于大列表,内存效率高,但会修改原列表;3)使用*运算符,适用于合并多个列表,不修改原列表;4)使用itertools.chain,适用于大数据集,内存效率高。

Python串联列表字符串Python串联列表字符串May 14, 2025 am 12:08 AM

使用join()方法是Python中从列表连接字符串最有效的方法。1)使用join()方法高效且易读。2)循环使用 运算符对大列表效率低。3)列表推导式与join()结合适用于需要转换的场景。4)reduce()方法适用于其他类型归约,但对字符串连接效率低。完整句子结束。

Python执行,那是什么?Python执行,那是什么?May 14, 2025 am 12:06 AM

pythonexecutionistheprocessoftransformingpypythoncodeintoExecutablestructions.1)InternterPreterReadSthecode,ConvertingTingitIntObyTecode,whepythonvirtualmachine(pvm)theglobalinterpreterpreterpreterpreterlock(gil)the thepythonvirtualmachine(pvm)

Python:关键功能是什么Python:关键功能是什么May 14, 2025 am 12:02 AM

Python的关键特性包括:1.语法简洁易懂,适合初学者;2.动态类型系统,提高开发速度;3.丰富的标准库,支持多种任务;4.强大的社区和生态系统,提供广泛支持;5.解释性,适合脚本和快速原型开发;6.多范式支持,适用于各种编程风格。

Python:编译器还是解释器?Python:编译器还是解释器?May 13, 2025 am 12:10 AM

Python是解释型语言,但也包含编译过程。1)Python代码先编译成字节码。2)字节码由Python虚拟机解释执行。3)这种混合机制使Python既灵活又高效,但执行速度不如完全编译型语言。

python用于循环与循环时:何时使用哪个?python用于循环与循环时:何时使用哪个?May 13, 2025 am 12:07 AM

useeAforloopWheniteratingOveraseQuenceOrforAspecificnumberoftimes; useAwhiLeLoopWhenconTinuingUntilAcIntiment.ForloopSareIdeAlforkNownsences,而WhileLeleLeleLeleLoopSituationSituationSituationsItuationSuationSituationswithUndEtermentersitations。

Python循环:最常见的错误Python循环:最常见的错误May 13, 2025 am 12:07 AM

pythonloopscanleadtoerrorslikeinfiniteloops,modifyingListsDuringteritation,逐个偏置,零indexingissues,andnestedloopineflinefficiencies

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器