《盘点那些秀你一脸的秒天秒地算法》(3)

斐波那契之美

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。

这个数列就是1、1、2、3、5、8、13、21、34、……

在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

但是斐波那契的知识不止于此,它还有很多惊艳到我的地方,下面我们就一起了解一下:

  1. 随着数列项数的增加,前一项与后一项之比越来越逼近黄金分割的数值0.6180339887..
  2. 从第二项开始,每个奇数项的平方都比前后两项之积多1,每个偶数项的平方都比前后两项之积少1。(注:奇数项和偶数项是指项数的奇偶,而并不是指数列的数字本身的奇偶,比如第五项的平方比前后两项之积多1,第四项的平方比前后两项之积少1)
  3. 斐波那契数列的第n项同时也代表了集合{1,2,…,n}中所有不包含相邻正整数的子集个数。
  4. f(0)+f(1)+f(2)+…+f(n)=f(n+2)-1
  5. f(1)+f(3)+f(5)+…+f(2n-1)=f(2n)
  6. f(2)+f(4)+f(6)+…+f(2n) =f(2n+1)-1
  7. [f(0)]^2+[f(1)]^2+…+[f(n)]^2=f(n)·f(n+1)
  8. f(0)-f(1)+f(2)-…+(-1)^n·f(n)=(-1)^n·[f(n+1)-f(n)]+1
  9. f(m+n)=f(m-1)·f(n-1)+f(m)·f(n) (利用这一点,可以用程序编出时间复杂度仅为O(log n)的程序。)

真的不禁感叹:真是一个神奇的数列啊

桶排序

我们都知道,基于比较的排序,时间的极限就是O(NlogN),从来没有哪个排序可以突破这个界限,如速度最快的快速排序,想法新颖的堆排序,分治的归并排序。

但是,如果我们的排序根本就不基于比较,那就可能突破这个时间。

桶排序不是基于比较的排序方法,只需对号入座。将相应的数字放进相应编号的桶即可。

当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间o(n)

对于海量有范围数据十分适合,比如全国高考成绩排序,公司年龄排序等等。

l=list(map(int,input().split(" ")))
n=max(l)-min(l)
p=[0]*(n+1)#为了省空间
for i in l:p[i-min(l)]=1#去重排序,做标记即可
for i in range(n):if p[i]==1:#判断是否出现过print(i+min(l),end=" ")

当然,基数排序是桶排序的改良版,也是非常好的排序方法,具体操作是:把数字的每一位都按桶排序排一次,因为桶排序是稳定的排序,因此从个位进行桶排序,然后十位进行桶排序。。。直到最高位,排序结束。

这样极大的弱化了桶排序范围有限的弱点,比如范围1-100000需要准备100000个铜,而基数排序只要10个铜就够了(因为一共就十个数字。)。

想出这个排序的人也是个天才啊。。。选择合适的场合使用觉得有很好的效果。

快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
分三区优化1:

在使用partition-exchange排序算法时,如快速排序算法,我们会遇到一些问题,比如重复元素太多,降低了效率,在每次递归中,左边部分是空的(没有元素比关键元素小),而右边部分只能一个一个递减移动。结果导致耗费了二次方时间来排序相等元素。这时我们可以多分一个区,即,小于区,等于区,大于区。(传统快排为小于区和大于区)

下面我们通过一个经典例题来练习这种思想。

荷兰国旗问题

”荷兰国旗难题“是计算机科学中的一个程序难题,它是由Edsger Dijkstra提出的。荷兰国旗是由红、白、蓝三色组成的。

现在有若干个红、白、蓝三种颜色的球随机排列成一条直线。现在我们的任务是把这些球按照红、白、蓝排序。

样例输入

3
BBRRWBWRRR
RRRWWRWRB
RBRW
样例输出

RRRRRWWBBB
RRRRRWWWB
RRWB
思路:

现在我们的思路就是把未排序时前部和后部分别排在数组的前面和后面,那么中部自然就排好了。

设置两个标志位head指向数组开头,tail指向数组末尾,now从头开始遍历:

(1)如果遍历到的位置为1,那么它一定是属于前部,于是就和head交换值,然后head++,now++;

(2)如果遍历到的位置为2,说明属于中部,now++;

(3)如果遍历到的位置为3,说明属于后部,于是就和tail交换值,然而,如果此时交换后now指向的值属于前部,那么就执行(1),tail--;

废话不多说,上代码。

#include<iostream>
#include<algorithm>
using namespace std;const int maxn = 100 + 5;int n;
string str;
int main(){cin>>n;while(n--){cin>>str;int len=str.size();int now=0,ans=0;int head=0,tail=len-1;while(now<=tail){if(str[now]=='R'){swap(str[head],str[now]);head++;now++;}else if(str[now]=='W'){now++;}else{swap(str[now],str[tail]);tail--;}}cout<<str<<endl;}return 0;
}

快排分三区以后降低了递归规模,避免了最差情况,性能得到改进。
但是还是存在退化情况:

随机化快排优化2:
快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。比如1 2 3 4 5,每次取第一个元素,就退化为了O(N^2)。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。

这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。
 

def gg(a,b):global lif a>=b:#注意停止条件,我以前没加>卡了半小时returnx,y=a,bimport random#为了避免遇到基本有序序列退化,随机选点g=random.randint(a,b)l[g],l[y]=l[y],l[g]#交换选中元素和末尾元素while a<b:if l[a]>l[y]:#比目标元素大l[a],l[b-1]=l[b-1],l[a]#交换b=b-1#大于区扩大#注意:换过以后a不要加,因为新换过来的元素并没有判断过else:a=a+1#小于区扩大l[y],l[a]=l[a],l[y]#这时a=b#现在解释a和b:a的意义是小于区下一个元素#b是大于区的第一个元素gg(x,a-1)#左右分别递归gg(a+1,y)l=list(map(int,input().split(" ")))
gg(0,len(l)-1)
print(l)

BFPRT

我们以前写过快排的改进求前k大或前k小,但是快排不可避免地存在退化问题,即使我们用了随机数等优化,最差情况不可避免的退化到了O(N^2),而BFPRT就解决了这个问题,主要的思想精华就是怎么选取划分值。

我们知道,经典快排是选第一个数进行划分。而改进快排是随机选取一个数进行划分,从概率上避免了基本有序情况的退化。而BFPRT算法选划分值的规则比较特殊,保证了递归最小的缩减规模也会比较大,而不是每次缩小一个数。

这个划分值如何划分就是重点。

如何让选取的点无论如何都不会太差。

1、将n个元素划分为n/5个组,每组5个元素
2、对每组排序,找到n/5个组中每一组的中位数; 
3、对于找到的所有中位数,调用BFPRT算法求出它们的中位数,作为划分值。

下面说明为什么这样找划分值。

我们先把数每五个分为一组。

同一列为一组。

排序之后,第三行就是各组的中位数。

我们把第三行的数构成一个数列,递归找,找到中位数。

这个黑色框为什么找的很好。

因为他一定比A3、B3大,而A3、B3、C3又在自己的组内比两个数要大。

我们看最差情况:求算其它的数都比c3大,我们也能在25个数中缩小九个数的规模。大约3/10.

我们就做到了最差情况固定递减规模,而不是可能缩小的很少。

下面代码实现:

public class BFPRT {
//前k小public static int[] getMinKNumsByBFPRT(int[] arr, int k) {if (k < 1 || k > arr.length) {return arr;}int minKth = getMinKthByBFPRT(arr, k);int[] res = new int[k];int index = 0;for (int i = 0; i != arr.length; i++) {if (arr[i] < minKth) {res[index++] = arr[i];}}for (; index != res.length; index++) {res[index] = minKth;}return res;}
//第k小public static int getMinKthByBFPRT(int[] arr, int K) {int[] copyArr = copyArray(arr);return select(copyArr, 0, copyArr.length - 1, K - 1);}public static int[] copyArray(int[] arr) {int[] res = new int[arr.length];for (int i = 0; i != res.length; i++) {res[i] = arr[i];}return res;}
//给定一个数组和范围,求第i小的数public static int select(int[] arr, int begin, int end, int i) {if (begin == end) {return arr[begin];}int pivot = medianOfMedians(arr, begin, end);//划分值int[] pivotRange = partition(arr, begin, end, pivot);if (i >= pivotRange[0] && i <= pivotRange[1]) {return arr[i];} else if (i < pivotRange[0]) {return select(arr, begin, pivotRange[0] - 1, i);} else {return select(arr, pivotRange[1] + 1, end, i);}}
//在begin end范围内进行操作public static int medianOfMedians(int[] arr, int begin, int end) {int num = end - begin + 1;int offset = num % 5 == 0 ? 0 : 1;//最后一组的情况int[] mArr = new int[num / 5 + offset];//中位数组成的数组for (int i = 0; i < mArr.length; i++) {int beginI = begin + i * 5;int endI = beginI + 4;mArr[i] = getMedian(arr, beginI, Math.min(end, endI));}return select(mArr, 0, mArr.length - 1, mArr.length / 2);//只不过i等于长度一半,用来求中位数}
//经典partition过程public static int[] partition(int[] arr, int begin, int end, int pivotValue) {int small = begin - 1;int cur = begin;int big = end + 1;while (cur != big) {if (arr[cur] < pivotValue) {swap(arr, ++small, cur++);} else if (arr[cur] > pivotValue) {swap(arr, cur, --big);} else {cur++;}}int[] range = new int[2];range[0] = small + 1;range[1] = big - 1;return range;}
//五个数排序,返回中位数public static int getMedian(int[] arr, int begin, int end) {insertionSort(arr, begin, end);int sum = end + begin;int mid = (sum / 2) + (sum % 2);return arr[mid];}
//手写排序public static void insertionSort(int[] arr, int begin, int end) {for (int i = begin + 1; i != end + 1; i++) {for (int j = i; j != begin; j--) {if (arr[j - 1] > arr[j]) {swap(arr, j - 1, j);} else {break;}}}}
//交换值public static void swap(int[] arr, int index1, int index2) {int tmp = arr[index1];arr[index1] = arr[index2];arr[index2] = tmp;}
//打印public static void printArray(int[] arr) {for (int i = 0; i != arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}public static void main(String[] args) {int[] arr = { 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 };// sorted : { 1, 1, 1, 1, 2, 2, 2, 3, 3, 5, 5, 5, 6, 6, 6, 7, 9, 9, 9 }printArray(getMinKNumsByBFPRT(arr, 10));}
}

这个办法解决了最差的退化情况,在一大堆数中求其前k大或前k小的问题,简称TOP-K问题。目前解决TOP-K问题最有效的算法即是BFPRT算法,其又称为中位数的中位数算法,该算法由Blum、Floyd、Pratt、Rivest、Tarjan提出 ,让我们永远记住这群伟大的人。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444699.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux(15)-

Linux下的编程开发

《盘点那些秀你一脸的秒天秒地算法》(4)

防止新手错误的神级代码 #define ture true #define flase false #difine viod void #define mian main #define &#xff1b; ; 以后有新手问题就把这几行代码给他就好啦。 不用额外空间交换两个变量 a 5 b 8 #计算a和b两个点到原点的距离之和&#xff0c;并且赋值给…

Linux(16)-

Vim编辑器的使用

php生成有复杂结构的excel文档

以前都用PHPExcel等工具来生成Excel&#xff0c;但是我们有时候需要非常复杂的样式&#xff0c;比如有合并单元格和拆分单元格&#xff0c;甚至有颜色&#xff0c;行间距之类的&#xff0c;这样做起来很费劲&#xff0c;而且你如果使用插件&#xff0c;你也需要学习这里我们可以…

caffe2安装篇(二) ubuntu16.04 安装方法

caffe2 ubuntu16.04 安装方法 Caffe2的安装相比于caffe在安装的时候更加简便,略去了Makefile.config的各种配置,对于有无GPU以及各种可选库例如opencv,anaconda的支持也更简单。(其实你直接装好库以后make就好,以GPU为例,在make的时候,自动检测你是否安装了CUDA,若没有…

为啥用redis解决会话呢?

什么是会话&#xff1f; 会话可简单理解为&#xff1a;用户开一个浏览器&#xff0c;点击多个超链接&#xff0c;访问服务器多个web资源&#xff0c;然后关闭浏览器&#xff0c;整个过程称之为一个会话。 •会话过程中要解决的一些问题&#xff1f; –每个用户不可避免各自会…

推荐系统(5)-深度推荐模型-AutoRec、DeepCrossing、NeuralCF、PNN、WideDeep、FNN、DeepFM、NFM

GBDTLR1. AutoRec-20152. Deep Crossing-20163. NeuralCF-20164. PNN-20165. Wide&Deep-20166. Deep&Cross-20177.FM深度学习7.1 FNN-20167.2 DeepFM-20177.3 NFM-2017《深度学习/推荐系统》读书笔记2016年开始&#xff0c;推荐系统和计算广告全面进入深度学习时代。 &…

关于在安装caffe2环境中遇到的坑整理(欢迎入坑讨论)

1.ImportError: cannot import name caffe2_pb2 测试caffe2的pytorch环境是否正常的时候使用 root@lxsj-ThinkStation:~/pytorch# python Python 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", &…

leetcode172. 阶乘后的零 最快算法

给定一个整数 n&#xff0c;返回 n! 结果尾数中零的数量。 示例 1: 输入: 3 输出: 0 解释: 3! 6, 尾数中没有零。 示例 2: 输入: 5 输出: 1 解释: 5! 120, 尾数中有 1 个零. 说明: 你算法的时间复杂度应为 O(log n) 。 思路&#xff1a;102*5&#xff0c;而因数中2一定比…

Win10 连接 Ubuntu16.04.3(通过Xdrp连接xfce4界面)

Win10 连接 Ubuntu16.04.3(通过Xdrp连接xfce4界面) sudo apt-get install xrdp sudo apt-get install vnc4server sudo apt-get install xubuntu-desktop echo "xfce4-session" >~/.xsession sudo apt-get install dconf editor sudo dconf editor(或者在搜索…

Linux(17)-

Make编译机制,Configure

听说你还在纠结自己没访问量?成不了“博客专家”?

一、提高浏览量的技巧 相信很多人都这么想过&#xff1a;“我文章写的这么好&#xff0c;怎么就没人看呢&#xff1f;”&#xff1b; 或者这样想过&#xff1a;“这文章写得明明比我烂很多&#xff0c;凭什么这么多浏览量&#xff1f;”&#xff1b; 虽然在我看来这是极其严…

推荐系统(6)-注意力机制+深度推荐模型、强化学习推荐系统

注意力机制深度推荐模型、强化学习推荐系统1.AFM -20172.DIN-20173.DIEN-20194. DRN-20181.AFM -2017 Attention factorization machines–浙江大学–基于模型结构的改进 引入注意力机制FM&#xff0c; 可视为NFM模型的改进。给特征交叉池化后的特征向量施加不同的注意力权重。…

Caffe安装的坑整理

怎么说了,入了深度学习的坑,就要踩一踩才算你入门,这里我整理了我在安装学习caffe自己遇到的坑: 1.Caffe-GPU编译问题:nvcc fatal : Unsupported gpu architecture compute_20 仔细查看了一下 Makefile.config 中 CUDA_ARCH 设置未按规定设置: # CUDA architecture se…

leetcode74. 搜索二维矩阵 ,你见过吗

编写一个高效的算法来判断 m x n 矩阵中&#xff0c;是否存在一个目标值。该矩阵具有如下特性&#xff1a; 每行中的整数从左到右按升序排列。 每行的第一个整数大于前一行的最后一个整数。 示例 1: 输入: matrix [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34,…

pytorch学习 入门篇(一)

PyTorch 是什么? 它是一个基于 Python 的科学计算包, 其主要是为了解决两类场景: NumPy 的替代品, 以使用 GPU 的强大加速功能一个深度学习研究平台, 提供最大的灵活性和速度Tensors(张量) Tensors 与 NumPy 的 ndarrays 非常相似, 除此之外还可以在 GPU 上使用张量来加速…

关系数据库——范式/反范式的利弊权衡和建议

范式&#xff08;避免数据冗余和操作异常&#xff09; 函数依赖 A->B A和B是两个属性集&#xff0c;来自同一关系模式&#xff0c;对于同样的A属性值&#xff0c;B属性值也相同 平凡的函数依赖 X->Y&#xff0c;如果Y是X的子集 非平凡的函数依赖 X->Y&#xff…

pytorch学习入门 (二) Variable(变量)

Variable&#xff08;变量&#xff09; autograd.Variable 是包的核心类. 它包装了张量, 并且支持几乎所有的操作. 一旦你完成了你的计算, 你就可以调用 .backward() 方法, 然后所有的梯度计算会自动进行. 你还可以通过 .data 属性来访问原始的张量, 而关于该 variable&#…

Linux(x)-

Ubuntu装机后的基础应用

pytorch入门学习(三) 神经网络

神经网络可以使用 torch.nn 包构建. autograd 实现了反向传播功能, 但是直接用来写深度学习的代码在很多情况下还是稍显复杂,torch.nn 是专门为神经网络设计的模块化接口. nn 构建于 Autograd 之上, 可用来定义和运行神经网络. nn.Module 是 nn 中最重要的类, 可把它看成是一个…