如何为 Code 4 的出现编写排序算法

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

如何为 Code 4 的出现编写排序算法
描绘某种排序算法的可爱图像

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

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

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

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

47|5397|1397|6197|4775|2961|1375|5329|1397|2953|2961|5397|5361|2947|1375|4797|7547|6175|6147|2975|1353|1375,47,61,53,2997,61,53,29,1375,29,1375,97,47,61,5361,13,2997,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 combinationsdef 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 的文章

回到谜题。

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

def get_middle(pages: tuple[int, ...]) -> int:    return pages[len(pages) // 2]def part1(input: str) -> int:    rules, pages_list = parse(input)    return sum(        get_middle(pages)        for pages in pages_list        if check_pages(rules, pages)    )

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

接下来是排序算法:

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

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

def move(items: tuple[int, ...], current: int, incoming: int) -> tuple[int, ...]:    assert incoming > current    return (        *items[:current],        items[incoming],        *tuple(            item            for idx, item in enumerate(items)            if (idx >= current) and not (idx == incoming)        ),    )

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

如何为 Code 4 的出现编写排序算法
我向朋友解释算法的失败尝试

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

def sort_pages(    rules: tuple[tuple[int, int], ...],    pages: tuple[int, ...],    pointer: int = 0,    subpointer: int = 0,) -> tuple[int, ...]:    return (        sort_pages(            rules,            *next(                (                    (move(pages, pointer, incoming), pointer, pointer + 2)                    for incoming in range(subpointer, len(pages))                    if check_pair(rules, pages[pointer], pages[incoming]) is false                ),                (pages, pointer + 1, pointer + 2),            ),        )        if pointer < (len(pages) - 1)        else pages    )

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

def sort_pages(    rules: tuple[tuple[int, int], ...], pages: tuple[int, ...]) -> tuple[int, ...]:    result, pointer = pages, 0    while true:        if pointer == (len(pages) - 1):            break        changed = false        for incoming in range(pointer + 1, len(pages)):            if check_pair(rules, result[pointer], result[incoming]) is false:                result = move(result, pointer, incoming)                changed = true                break        pointer = 0 if changed else pointer + 1    return result

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

算法的工作原理如下:

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

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

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

def part2(input: str) -> int:    rules, pages_list = parse(input)    return sum(        get_middle(sort_pages(rules, pages))        for pages in pages_list        if check_pages(rules, pages) is False    )

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

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

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

以上就是如何为 Code 4 的出现编写排序算法的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1354755.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月13日 18:46:47
下一篇 2025年12月13日 18:46:54

相关推荐

  • 确保芹菜的公平加工 – 第二部分

    本文在上一篇有关公平处理的文章的基础上探讨了 celery 中的任务优先级。任务优先级提供了一种通过根据自定义标准为任务分配不同优先级来增强后台处理的公平性和效率的方法。 为什么任务级优先级? 任务级优先级提供对任务执行的细粒度控制,无需复杂的实现。通过将所有任务提交到具有指定优先级值的单个队列,工…

    好文分享 2025年12月13日
    000
  • Day – 字符串函数

    1.编写一个程序来检查给定的密钥是否可用: txt = “i love many fruits, apple is my favorite fruit”key = ‘fruit’l = len(key)start = 0 end = lwhile end<=len(txt): if txt[s…

    2025年12月13日
    000
  • 代码的出现 &#- DayDisk Fragmenter (Python)

    代码出现第 9 天:磁盘碎片 今天的解决方案只是用 Python 完成的,老实说,我发现很难找到时间用 Python 和 C# 编写以及撰写文章,所以选择继续使用其中之一。 第 1 部分 这相当简单,要求是将输入转换为 id 和空格,其中奇数索引是 id,偶数索引是空格 (.) 并重复 x 次,与输…

    2025年12月13日
    000
  • 使用 Python 和 NumPy 为神经网络创建简单高效的遗传算法

    这是有关 ml 进化算法课程的第一篇文章。 当你知道神经网络的参数,但不知道输出应该是什么时,就需要遗传算法,例如,这个算法可以用来玩 google dinosaur 或 flappy bird,因为你不知道输出应该是什么,但您有能力对最可行的选项进行排序,例如按时间,这称为适应度函数。 我一直没能…

    2025年12月13日
    000
  • 易于复制的 Bash 脚本来可视化 Python 代码

    通过视觉理解代码比仅仅阅读代码容易 10 倍。 想知道如何快速创建一个吗? 这是我用来可视化 python 代码的 3 个最佳 bash 脚本: 可视化代码结构 你永远不知道什么时候会遇到下一个过于复杂的代码,如果没有工具,就很难发现代码的复杂性。随着项目规模的扩大,这可能会导致可读性差和出现错误的…

    2025年12月13日
    000
  • 使用 Python 请求模块使 HTTP 变得简单

    简介 http 是一种基于 tcp/ip 的应用层通信协议,它标准化了客户端和服务器之间的通信方式。它用于使用超文本链接加载网页。 “无论您是从 api 获取数据还是提交表单数据,python 中的 requests 库都是您的首选工具,可以让 http 请求无缝且直观。” 如何安装请求 在终端中输…

    2025年12月13日
    000
  • 掌握 Python 并发编程:利用先进技术提升性能

    python 的并发编程能力已经显着发展,为开发人员提供了编写高效、并行代码的强大工具。我花了相当多的时间探索这些先进技术,很高兴与您分享我的见解。 使用 asyncio 进行异步编程是 i/o 密集型任务的游戏规则改变者。它允许我们编写非阻塞代码,可以同时处理多个操作,而无需线程开销。下面是一个简…

    2025年12月13日
    000
  • ChatsAPI — 世界上最快的人工智能代理框架

    github: https://github.com/chatsapi/chatsapi图书馆: https://pypi.org/project/chatsapi/ 人工智能已经改变了各行各业,但有效部署人工智能仍然是一项艰巨的挑战。复杂的框架、缓慢的响应时间和陡峭的学习曲线给企业和开发人员带来了…

    2025年12月13日 好文分享
    000
  • 最大化间隔

    每周挑战 298 穆罕默德·s·安瓦尔 (mohammad s. anwar) 每周都会发出“每周挑战”,让我们所有人都有机会为每周两次的任务提出解决方案。这对我们所有人来说都是练习编码的好方法。 挑战,我的解决方案 任务 1:最大平方 任务 给你一个只有 0 和 1 的 m x n 二进制矩阵。 …

    2025年12月13日
    000
  • 如何在 Google Colab 上运行 stable-diffusion–large-turbo

    stable-diffusion-3.5-large-turbo 是一种高精度文本到图像模型。 本指南将解释如何在 google colab 上设置和运行模型。 先决条件 访问拥抱脸。 要使用 stable-diffusion-3.5-large-turbo,您需要一个 huggingface 帐户…

    2025年12月13日 好文分享
    000
  • 在 PyTorch 中移动 MNIST

    请我喝杯咖啡☕ *我的帖子解释了移动 mnist。 movingmnist() 可以使用 moving mnist 数据集,如下所示: *备忘录: 第一个参数是 root(必需类型:str 或 pathlib.path)。 *绝对或相对路径都是可能的。第二个参数是 split(optional-de…

    2025年12月13日 好文分享
    000
  • 用于安全密码哈希的 Bcrypt 算法

    哈希是一种无法逆转的加密函数。它需要随机大小的输入来生成固定大小的值。这些固定大小的值称为哈希值, 加密函数称为哈希函数。散列具有一致和可预测的性质,这意味着相同的输入将始终产生相同的散列值。它还表现出雪崩效应,这意味着即使输入的微小变化也会导致哈希值截然不同,从而确保高安全性和不确定性。 散列通常…

    2025年12月13日
    000
  • PyTorch 中的 FashionMNIST

    请我喝杯咖啡☕ *我的帖子解释了 fashion-mnist。 fashionmnist() 可以使用 fashion-mnist 数据集,如下所示: *备忘录: 第一个参数是 root(必需类型:str 或 pathlib.path)。 *绝对或相对路径都是可能的。第二个参数是 train(opt…

    2025年12月13日
    000
  • 任务-Python 包

    几个 python 包 进度条和 tqdm: 为循环、文件处理或下载等任务实现进度条。 from progress.bar import chargingbarbar = chargingbar(‘processing’, max=20)for i in range(20): # do some w…

    2025年12月13日
    000
  • 使用 Python、LangChain 和矢量搜索构建可扩展的 AI 聊天应用程序

    构建可投入生产的人工智能聊天应用程序需要强大的矢量存储和高效的工作流程管理。让我们探索如何使用 astra db 和 langflow 创建它。 环境设置 首先,让我们使用所需的依赖项设置 python 环境: from langchain.vectorstores import astradbfr…

    2025年12月13日
    000
  • 通过 Ready Mailing Team 的首席执行官电子邮件列表释放战略业务增长

    在商业领域,与合适的人建立联系可以定义成功。 ready mailing team 的首席执行官电子邮件列表是您到达决策顶峰的终极工具——首席执行官、总裁和各行业的顶级领导者。无论您是想提出突破性的想法、促进合作还是加强您的营销活动,此电子邮件列表都可以弥合您的愿景与能够将其变为现实的领导者之间的差…

    2025年12月13日
    000
  • Python 中处理错误的最佳实践

    错误处理是编写健壮且可维护的 python 代码的关键。这是使您的错误管理更智能、更有效的快速指南。 ☝️ 捕获特定异常。 始终捕获特定异常,而不是使用通用的 except 块。这可以帮助您更轻松地识别问题的根本原因,并防止掩盖其他潜在错误。☝️ 针对无效条件提出例外。如果不满足某些条件,请故意提出…

    2025年12月13日
    000
  • 用于动态代码的强大 Python 元编程技术

    作为一名 python 开发人员,我一直对该语言操纵自身的能力着迷。元编程是一种编写在运行时生成或修改其他代码的代码的艺术,它为创建灵活和动态的程序开辟了可能性的世界。在本文中,我将分享七种强大的元编程技术,这些技术彻底改变了我的 python 开发方法。 装饰器:修改函数行为 装饰器是 pytho…

    2025年12月13日
    000
  • Day – CSV 文件、ASCII、字符串方法

    csv(逗号分隔值): csv 文件代表一行,行内的每个值都用逗号分隔。csv 文件看起来像 excel,但 excel 文件只能在 excel 软件中打开。csv 文件用于所有操作系统。 我们可以打开以下两种格式的csv文件。 f =open(“sample.txt”, “r”)with open…

    2025年12月13日
    000
  • Python 缓存:如何通过有效的缓存来加速代码

    此博客最初发布到 crawlbase 博客 高效、快速的代码对于在软件应用程序中创建出色的用户体验非常重要。用户不喜欢等待缓慢的响应,无论是加载网页、训练机器学习模型还是运行脚本。加快代码速度的一种方法是缓存。 缓存的目的是临时缓存经常使用的数据,以便您的程序可以更快地访问它,而不必多次重新计算或检…

    2025年12月13日
    000

发表回复

登录后才能评论
关注微信