原文转载自「刘悦的技术博客」https://v3u.cn/a_id_159

北京的疫情一波未平一波又起,由此看来,战“疫”将是一场旷日持久的战争,绝不能掉以轻心、轻易言胜。病毒随时都会死灰复燃,以生命为代价换来的经验教训值得我们每一个人久久深思。笔者所在的小区也开始组织居民批量进行核酸检测,本以为会是一幅摩肩接踵,水泄不通的场景,却出人意料的井然有序、有层有次,效率非常高。原来检疫部门采取了一种特别的策略:每五个人用一组试剂盒,进行快筛,分分钟搞定了几百人的社区检测。

这里解释一下病毒核酸检测的原理,检测人员提取小区居民的鼻腔拭子或者咽拭子(就是用一根棉签在咽喉处或者鼻腔深处刮取一些分泌物),然后将该棉签放入试剂盒,以病毒独特的基因序列检测靶标,通过PCR扩增,使我们选择的这段靶标DNA序列指数级增加,每一个扩增出来的DNA序列,都可与我们预先加入的一段荧光标记探针结合,产生荧光信号,扩增出来的靶基因越多,累计的荧光信号就越强。说白了就是试剂盒荧光反映变色越强烈,说明病毒体量和活性越强。

而五人一组共用一个试剂盒测试,如果结果呈阳性,再对其中四个人分别测试即可。 由于绝大部分人都是健康的,所以这样可以提高五倍的检测量,从而检测更多的人,很明显这次检疫使用到了类似归并的“分治法”来解决问题,提高效率。

分治法,即“分而治之”,出自清·俞樾《群经平议·周官二》“巫马下士二人医四人”:“凡邦之有疾病者,疕疡者造焉,则使医分而治之,是亦不自医也。” 其核心思想是:将一个难以直接解决的大问题,分拆成一些规模较小的相同问题,随后各个击破,分而治之。可以理解为:如果原问题可以分割成n个子问题,1<n<=原问题,且这些子问题均可解并且利用这些子问题的解求出原问题的解,那么分治方法就是可行的。由分治法产生的子问题往往是原问题的较小模式,这就为使用递归算法提供了遍历。反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。这自然导致递归的使用。所以分治与递归经常同时应用在算法解决方案中。

核酸检测正好契合分治算法的使用场景:该问题的规模只要缩小到一定的规模就可以容易的解决。该问题可以分解为若干个规模较小的相同问题(检测是否阳性)。

而我们在技术面试中,可以利用分治算法解决的经典问题如下:

归并排序

def merge_sort(lst):
# 从递归中返回长度为1的序列
if len(lst) <= 1:
return lst middle = len(lst) / 2
# 1.分解:通过不断递归,将原始序列拆分成 n 个小序列
left = merge_sort(lst[:middle])
right = merge_sort(lst[middle:])
# 进行排序与合并
return merge(left, right) def merge(left, right):
i, j = 0, 0
result = []
# 2.解决:比较传入的两个子序列,对两个子序列进行排序
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# 3.合并:将排好序的子序列合并
result.extend(left[i:])
result.extend(right[j:])
return result

快速排序

def quickSort(listx):
if len(listx)<=1:
return listx
pivot = listx[len(listx)//2] #取列表中中间的元素为被比较数pivot
listl = [x for x in listx if x < pivot] #<pivot的放在一个列表
listm = [x for x in listx if x ==pivot] #=pivot的放在一个列表
listr = [x for x in listx if x > pivot] #>pivot的放在一个列表
left = quickSort(listl) #递归进行该函数
right = quickSort(listr) #递归进行该函数
return left + listm + right #整合
print(quickSort([9,3, 6, 8, 9, 19, 1, 5])) #[1, 3, 5, 6, 8, 9, 9, 19]

折半查找

def binary_search(lis, key):
low = 0
high = len(lis) - 1
time = 0
while low < high:
time += 1
mid = int((low + high) / 2)
if key < lis[mid]:
high = mid - 1
elif key > lis[mid]:
low = mid + 1
else:
# 打印折半的次数
print("times: %s" % time)
return mid
print("times: %s" % time)
return False

二叉树的最大深度问题

class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
left = self.maxDepth(root.left) + 1
right = self.maxDepth(root.right) + 1
return left if left > right else right

计算x 的 n 次幂问题

class Solution(object):
def myPow(self, x, n):
"""
:type x: float
:type n: int
:rtype: float
"""
if not n:
return 1
if n < 0:
return 1 / self.myPow(x, -n)
if n % 2:
return (x * self.myPow(x, n - 1))
return self.myPow(x * x, int(n / 2))

当然了,分治算法也并非无懈可击,回到核酸检测的场景,这种做法在最乐观情况下,的的确确是提升了五倍的效率,但是在最不乐观情况下,反而会增大工作量。如果在检测这些人中一个感染的患者都没有,那就是最乐观情况,5人一组检查一遍就OK了;如果这群人全部(正确来讲是在分组后的每一组中都有至少一个)感染人员,这种极端恶劣的情况下会导致至少增加分组数量的工作量,所以根本问题又变成了在假设一定感染率的情况下,如何确定多少个样本一组检测比较好。考虑的因素可能包括,检测效率,费用,有阳性的时候快速定位等。实际监测的时候,还可以不同地区不同的检测策略,监测策略也可以根据检测结果调整。

结语:算法其实在生活中无处不在,很多同学出去面试时往往惧怕做算法题,其实算法也不过就是一种解决问题的方法,目的也仅仅是为了提高效率,如果在生活中多观察、多思考,也许会对算法能力的提升有一定的帮助。

原文转载自「刘悦的技术博客」 https://v3u.cn/a_id_159

当我们谈论算法我们在谈论什么:由疫情核酸检测想到的分治算法(Divide-and-Conquer)的更多相关文章

  1. 分治算法——Karastsuba算法

    分治(Divide and Conquer)算法:问题能够分解为子问题,每一个问题是能够独立的解决的,从子问题的解能够构建原问题. Divide:中间分.随机分.奇偶分等,将问题分解成独立的子问题 C ...

  2. Union-Find 检测无向图有无环路算法

    不相交集合数据结构(Disjoint-set data structure)是一种用于跟踪集合被分割成多个不相交的子集合的数据结构,每个集合通过一个代表来标识,代表即集合中的某个成员. Union-F ...

  3. 算法(第4版)-1.5 案例研究:union-find算法

    问题→ 动态连通性:当程序从输入中读取了整数对p q时,如果已知的所有整数对都不能说明p和q是相连的,那么则将这一对整数写入到输出中.如果已知的数据可以说明p和q 是相连的,那么程序应该忽略p q这对 ...

  4. 从两个平方算法到分治算法-java

    先来看看问题的来源,假设有这么一个数组: 1 2 -5 4 -2 3 -3 4 -15 我们要求出其中连续字数组的和的最大值 例如这么可以很明显看出 4+ –2 + 3 + –3 + 4 = 6 所有 ...

  5. [算法]检测空间三角形相交算法(Devillers & Guigue算法)

    #pragma once //GYDevillersTriangle.h /* 快速检测空间三角形相交算法的代码实现(Devillers & Guigue算法) 博客原地址:http://bl ...

  6. 计算几何 平面最近点对 nlogn分治算法 求平面中距离最近的两点

    平面最近点对,即平面中距离最近的两点 分治算法: int SOLVE(int left,int right)//求解点集中区间[left,right]中的最近点对 { double ans; //an ...

  7. [算法]分治算法(Divide and Conquer)

    转载请注明:http://www.cnblogs.com/StartoverX/p/4575744.html 分治算法 在计算机科学中,分治法是建基于多项分支递归的一种很重要的算法范式.字面上的解释是 ...

  8. 分治算法求乘方a^b 取余p(divide and conquer)

    传统的计算方法为循环n个a相乘.时间复杂度为O(n). 如用分治算法,效率可提升至O(lgn). 结合recursive有 double pow(int a, int n){ ) ; ) return ...

  9. UVA 10245 The Closest Pair Problem 最近点问题 分治算法

    题意,给出n个点的坐标,找出两点间最近的距离,如果小于10000就输出INFINITY. 纯暴力是会超时的,所以得另辟蹊径,用分治算法. 递归思路将点按坐标排序后,分成两块处理,最近的距离不是在两块中 ...

随机推荐

  1. 基于Proxmox平台搭建3D云教室

    背景 本文介绍了在 Proxmox VE 虚拟化平台上使用NVIDIA A16 GPU,开启vGPU特性,利用DoraCloud 搭建3D云教室的方案. Proxmox virtualization ...

  2. 『忘了再学』Shell基础 — 19、使用declare命令声明变量类型

    目录 1.declare命令介绍 2.声明数组变量类型 3.声明变量为环境变量 4.声明只读属性 5.补充: 1.declare命令介绍 Shell中所有变量的默认类型是字符串类型,如果你需要进行特殊 ...

  3. 注意:Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...

    如果你是Spring Boot用户的话,一定有这样的开发体验,当我们要引入某个功能的时候,只需要在maven或gradle的配置中直接引入对应的Starter,马上就可以使用了,而不需要像传统Spri ...

  4. JavaScript之创建八个对象过520

    马上又到了一年一度的520了,程序猿们赶紧创建对象过520吧!!! JavaScript创建对象的几种方式: 一:字面量方式: var obj = {name: '程序猿'}; 二:通过new操作符: ...

  5. CPPRESTSDK 编译

    首先要 git clone这个库git clone git://github.com/Microsoft/cpprestsdk.git然后git 一下 Vcpkg.\vcpkg\bootstrap-v ...

  6. 技术分享 | app自动化测试(Android)--App 控件交互

    原文链接 常用操作 点击操作 通常获取到元素之后,可以调用 click() 方法来实现对这个元素的点击操作.示例代码如下: python 版本 driver.find_element_by_id(&q ...

  7. 一篇文章带你深入浅出Vuex

    在写Vuex之前,我们先用一个简单的例子来实现一个小demo 大家都知道Vue的父传子用在很多场景,比如像这样: 父组件: <template> <div id="app& ...

  8. Gitlab + Gitlab runner + Window powershell

    需求说明 根据领导要求,要把python 项目移到Gitlab 进行管理,并利用Gitlab CI/CD 进行自动化测试,打包,部署.(听起来很简单吧) 比较头大,完全没有经验,python 也是刚上 ...

  9. SAP 定义用户组

    SUGR,可进行创建.查看.删除等维护性操作,并可指定本组的用户

  10. Redis基础与性能调优

    Redis是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库.缓存服务或消息服务使用. Redis支持多种数据结构,包括字符串.哈希表.链表.集合.有序集合.位图.Hyperloglogs等. ...