贪心:Swift实现

中心思想

求解最优化问题得算法通常需要经过一系列得步骤,每个步骤都面临多种选择。在许多最优化问题上使用动态规划其实会有杀鸡用牛刀的感觉。贪心算法(greedy algorithm)保证每一步都作出当时看起来的最佳的选择,换句话说就是保证局部最优选

贪心算法一般步骤

  1. 确定问题的最优子结构
  2. 设计一个递归算法
  3. 证明我们每做一个贪心选择,则只剩下一个子问题
  4. 证明贪心选择是安全的
  5. 设计一个递归算法实现的贪心策略
  6. 将递归算法转为迭代算法

明显的通过1,2两步骤的对比,我们知道贪心算法是以DP为基础的。

更一般地,我们可以按如下步骤设计贪心算法:

  1. 将最优化问题转为这样的形式:对其做出一次选择后,只剩下一个子问题需要求解。
  2. 证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的。
  3. 证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可以得到原问题的最优解,这样就得到了最优子结构。

贪心算法在最优解适用的两个重要性质

贪心选择性质

做局部最优选择来构造全局最优解。即进行选择的时候只需要考虑当前问题中看来最优的选择,而不必考虑子问题的解。

贪心算法进行选择时候可能依赖之前做出的选择,单不依赖任何将来的选择或者子问题的解。贪心算法在进行第一次选择之前不求解任何子问题。一个动态规划算法通常是自底向上进行计算的,而一个贪心算法通常是自顶向下的。通过不断的当前选择来缩小问题的规模。

贪心选择时候要进行众多的选择,这意味着可以改进贪心选择,使其更为高效。可以通过排序跟改变数据结构(通常是优先队列)做到这点。

最优子结构

如果一个问题的最优解包含其子问题的最优解,则称此问题具有最优子结构性质。最优子结构性质是DP与贪心算法的关键。通过最优子结构我们可以设计出递归式来描述最优解值的方法。

贪心中的我们通常使用更为直接的最优子结构。要做的工作转为证明:将子问题的最优解与贪心选择组合在一起能生成原问题的最优解。(数学归纳法的应用)

举个栗子

栗子1)活动选择问题

输入源:活动起始时间数组s,活动结束时间数组f,下标代表第几个活动【注:活动集合 S = {a1, a2, ..., an},活动ai数据结构为含两个时间的元组,一个为活动开始时间,一个为活动结束时间】。
目标:最大互相兼容的活动集合

牛刀:DP思路

寻找一个最优子结构:
令Sij表示在ai结束之后开,并且在aj开之前结束的活动集合。假设存在Aij子集是我们的解,并且包含活动ak。令Aik = Aij ∩ Sik和Akj = Aij ∩ Skj。 Aij = Aik U {ak} U Akj

反证法可以证明Aij必然包含子问题Sik与Skj的最优解。

递归定义最优解:
图片描述

自顶向上算法与构造最优解 -> 略

言归正传:贪心选择

我们无需考虑最优子问题就可以选择一个活动加入最优解,原因很简单从人的思维来说,每一个步骤起始就是在选择一个活动,同样是选择一个活动如果这个活动结束的时间越早那么这个活动必然是最优解(反证法)。

1)递归贪心算法

将活动按照活动结束时间进行排序,时间复杂度O(nlgn)。上伪代码:

RECURSIVE-ACTIVITY-SELECTOR(s, f, k, n) //k表示要求解的子问题Sk, n表示问题规模
m = k + 1
while m <= n and s[m] < f[k]
    m = m + 1    // Sk中查找第一个兼容活动ak的活动am,f数组非递减,fi <= fj (i < j)
if m <= n
    return {am} U RECURSIVE-ACTIVITY-SELECTOR(s, f, m, n)
else return NIL

2)迭代贪心算法
RECURSIVE-ACTIVITY-SELECTOR机会就是“尾递归”:结尾的自身递归调用接一次并集操作。而迭代形式更为直接:

GREEDY-ACTIVITY-SELECTOR(s, f)
n = s.length
A = {a1}
k = 1
for m = 2 to n
    if s[m] >= f[k]
        A = A U {am}
        k = m
return A

3)总结
递归与迭代类似,在输入活动已经按照结束时间排序好的前提下,两者运行时间为Θ(n)。

Swift实现
cfunc greedyActivitySelector(starts: [Int], finishes: [Int]) -> [Int] {
    var n = starts.count - 1
    var activities: [Int] = [1]

    var k = 1

    for var m = 2; m <= n; m++ {
        if starts[m] > finishes[k] {
            activities.append(m)
            k = m
        }
    }

    return activities
}

// TEST CASE

var s: [Int] = [0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12]
var f: [Int] = [0, 4, 5, 6, 7, 9, 9, 10, 11, 12, 14, 16]

var a: [Int] = greedyActivitySelector(s, f)

for item in a {
    print("\(item)\t")
}

栗子2)哈夫曼编码

采用PyQt5框架与Python编程语言构建图书信息管理平台 本项目基于Python编程环境,结合PyQt5图形界面开发库,设计实现了一套完整的图书信息管理解决方案。该系统主要面向图书馆、书店等机构的日常运营需求,通过模块化设计实现了图书信息的标准化管理流程。 系统架构采用典型的三层设计模式,包含数据存储层、业务逻辑层和用户界面层。数据持久化方案支持SQLite轻量级数据库与MySQL企业级数据库的双重配置选项,通过统一的数据库操作接口实现数据存取隔离。在数据建模方面,设计了包含图书基本信息、读者档案、借阅记录等核心数据实体,各实体间通过主外键约束建立关联关系。 核心功能模块包含六大子系统: 1. 图书编目管理:支持国际标准书号、中国图书馆分类法等专业元数据的规范化著录,提供批量导入与单条录入两种数据采集方式 2. 库存动态监控:实时追踪在架数量、借出状态、预约队列等流通指标,设置库存预警阈值自动提醒补货 3. 读者服务管理:建立完整的读者信用评价体系,记录借阅历史与违规行为,实施差异化借阅权限管理 4. 流通业务处理:涵盖借书登记、归还处理、续借申请、逾期计算等标准业务流程,支持射频识别技术设备集成 5. 统计报表生成:按日/月/年周期自动生成流通统计、热门图书排行、读者活跃度等多维度分析图表 6. 系统维护配置:提供用户权限分级管理、数据备份恢复、操作日志审计等管理功能 在技术实现层面,界面设计遵循Material Design设计规范,采用QSS样式表实现视觉定制化。通过信号槽机制实现前后端数据双向绑定,运用多线程处理技术保障界面响应流畅度。数据验证机制包含前端格式校验与后端业务规则双重保障,关键操作均设有二次确认流程。 该系统适用于中小型图书管理场景,通过可扩展的插件架构支持功能模块的灵活组合。开发过程中特别注重代码的可维护性,采用面向对象编程范式实现高内聚低耦合的组件设计,为后续功能迭代奠定技术基础。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值