<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>算法 on lategege 的技术博客</title><link>https://lategege.com/categories/%E7%AE%97%E6%B3%95/</link><description>Recent content in 算法 on lategege 的技术博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Thu, 06 Aug 2020 08:29:57 +0000</lastBuildDate><atom:link href="https://lategege.com/categories/%E7%AE%97%E6%B3%95/index.xml" rel="self" type="application/rss+xml"/><item><title>Leecode罗马数字转整数(第13题)</title><link>https://lategege.com/p/leecode%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97%E8%BD%AC%E6%95%B4%E6%95%B0-%E7%AC%AC13%E9%A2%98/</link><pubDate>Thu, 06 Aug 2020 08:29:57 +0000</pubDate><guid>https://lategege.com/p/leecode%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97%E8%BD%AC%E6%95%B4%E6%95%B0-%E7%AC%AC13%E9%A2%98/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;问题 开始---------------------------------------------------------------------------------------（这是一道简单题，但是做题过程中随着算法的演化却学到了很多)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;罗马数字包含以下七种字符: I， V， X， L，C，D 和 M。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;字符 数值&lt;br/&gt;I 1&lt;br/&gt;V 5&lt;br/&gt;X 10&lt;br/&gt;L 50&lt;br/&gt;C 100&lt;br/&gt;D 500&lt;br/&gt;M 1000&lt;br/&gt;例如， 罗马数字 2 写做 II ，即为两个并列的 1。12 写做 XII ，即为 X + II 。 27 写做  XXVII, 即为 XX + V + II 。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;通常情况下，罗马数字中小的数字在大的数字的右边。但也存在特例，例如 4 不写做 IIII，而是 IV。数字 1 在数字 5 的左边，所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地，数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;I 可以放在 V (5) 和 X (10) 的左边，来表示 4 和 9。&lt;br/&gt;X 可以放在 L (50) 和 C (100) 的左边，来表示 40 和 90。 &lt;br/&gt;C 可以放在 D (500) 和 M (1000) 的左边，来表示 400 和 900。&lt;br/&gt;给定一个罗马数字，将其转换成整数。输入确保在 1 到 3999 的范围内。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;示例 1:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;输入: "III"&lt;br/&gt;输出: 3&lt;br/&gt;示例 2:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;输入: "IV"&lt;br/&gt;输出: 4&lt;br/&gt;示例 3:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;输入: "IX"&lt;br/&gt;输出: 9&lt;br/&gt;示例 4:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;输入: "LVIII"&lt;br/&gt;输出: 58&lt;br/&gt;解释: L = 50, V= 5, III = 3.&lt;br/&gt;示例 5:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;输入: "MCMXCIV"&lt;br/&gt;输出: 1994&lt;br/&gt;解释: M = 1000, CM = 900, XC = 90, IV = 4.&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;问题 结束---------------------------------------------------------------------------------------&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;解法一：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这个解法是我第一个想到的，总耗时58ms 时间基本都花在遍历替换上,所以replaceAll这个方法是比较耗时的&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt; public static int get(String s) {
 HashMap&amp;lt;String, Integer&amp;gt; maps = new LinkedHashMap&amp;lt;&amp;gt;();
 maps.put("IV", 4);
 maps.put("IX", 9);
 maps.put("XL", 40);
 maps.put("XC", 90);
 maps.put("CD", 400);
 maps.put("CM", 900);
 maps.put("I", 1);
 maps.put("V", 5);
 maps.put("X", 10);
 maps.put("L", 50);
 maps.put("C", 100);
 maps.put("D", 500);
 maps.put("M", 1000);
 for (String key : maps.keySet()) {
 s = s.replaceAll(key, maps.get(key) + ",");
 }
 int result = 0;
 String resultNums[] = s.split(",");
 for (int index = 0; index &amp;lt; resultNums.length; index++) {
 if (resultNums[index] != null || resultNums[index] != "") {
 result += Integer.parseInt(resultNums[index]);
 }
 }
 return result;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;解法二： &lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;耗时6ms ，可以看到耗时有了质的飞越，可惜还是指击败了50%多，那究竟差在哪里呢&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt; HashMap&amp;lt;Character, Integer&amp;gt; maps = new HashMap&amp;lt;&amp;gt;();
 maps.put('I', 1);
 maps.put('V', 5);
 maps.put('X', 10);
 maps.put('L', 50);
 maps.put('C', 100);
 maps.put('D', 500);
 maps.put('M', 1000);
 int result = 0;
&lt;pre&gt;&lt;code&gt; int pre = 0;
 for (int index = 0; index &amp;amp;lt; s.length(); index++) {
 int num = maps.get(s.charAt(index));
 switch (pre) {
 case 1:
 if (num == 5 || num == 10) {
 result -= 2;
 }
 break;
 case 10:
 if (num == 50 || num == 100) {
 result -= 20;
 }
 break;
 case 100:
 if (num == 500 || num == 1000) {
 result -= 200;
 }
 break;
 }
 pre = num;
 result += num;
 }
 return result;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;解法三：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;耗时4ms，这个算法是题解给的思路，不使用switch转用hashmap去取值得话 会耗时8ms 比我上一个算法还多2ms,所以这个hashmap在数量少的情况下不建议使用，创建开销较大。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt; public static int get3(String s) {
 int result = 0;
 int preNum = getValue(s.charAt(0));
 for (int index = 1; index &amp;lt; s.length(); index++) {
 int num = getValue(s.charAt(index));
 if (preNum &amp;lt; num) {
 result -= preNum;
 } else {
 result += preNum;
 }
 preNum = num;
 }
 result += preNum;
 return result;
&lt;pre&gt;&lt;code&gt;}

private static int getValue(char ch) {
 switch(ch) {
 case 'I': return 1;
 case 'V': return 5;
 case 'X': return 10;
 case 'L': return 50;
 case 'C': return 100;
 case 'D': return 500;
 case 'M': return 1000;
 default: return 0;
 }
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>斐波那契查找原理深入(转)</title><link>https://lategege.com/p/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%9F%A5%E6%89%BE%E5%8E%9F%E7%90%86%E6%B7%B1%E5%85%A5-%E8%BD%AC/</link><pubDate>Fri, 31 Jul 2020 03:02:35 +0000</pubDate><guid>https://lategege.com/p/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%9F%A5%E6%89%BE%E5%8E%9F%E7%90%86%E6%B7%B1%E5%85%A5-%E8%BD%AC/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;看到这篇文章都斐波那契查找讲的很透彻，所以转载收藏一下。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;strong&gt;斐波那契查找原理：&lt;/strong&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;       斐波那契查找是一种在有序表中高效查找指定元素的算法，比折半查找要复杂一些，主要复杂在要多做不少准备工作。下面看它的工作流程：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;        1.计算并保存一个斐波那契序列的数组，方便以后取值。数组名记为f，例如f[1]=1，f[2]=1，f[3]=2，f[4]=3，f[5]=5，f[6]=8，f[7]=13，f[8]=21&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;        2.把有序数组的长度扩充到a.length=f[k]-1，k是满足条件的最小值，比如数组长度为13，那么就把它长度扩充到f[8]-1=20，所有在末尾添加的扩充元素都是原数组最后一个元素的复制品&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;        3.找到mid元素，不断进行二分比较，直到找到目标元素为止，这一步的做法与折半查找一模一样，仅仅是计算mid的公式从(low+high)/2改为low+(f[k-1]-1)。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;        斐波那契查找的理解难点就一个：为什么需要把数组长度扩充到f[k]-1而不是f[k]或者f[k+1]？这是为了能正确递归计算mid值，看下图可发现 f[k]-1 = (f[k-1] + f[k-2]) - 1 = (f[k-1]-1) + 1 + (f[k-2]-1)，中间的1就是我们二分的锚点mid，如果目标在左区，数组长度就缩到(f[k-1]-1)，如果在右区，数组长度就缩到(f[k-2]-1)，否则就等于mid完成查找。而(f[k-1]-1)又能拆成(f[k-2]-1)+1+(f[k-3]-1)，这样递归分割下去就能不断的缩小区间直至找到目标。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image --&gt;
&lt;figure class="wp-block-image"&gt;&lt;img alt="" src="https://img-blog.csdn.net/20170303005411255"/&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;strong&gt;斐波那契查找与折半查找的比较：&lt;/strong&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;           二者理论效率半斤八两，时间复杂度都是log2n，有人说斐波那契查找比折半查找效率高，理由有2个：1.斐波那契查找只用到加减法，而折半查找计算mid时要除以2，除法很影响效率；2.如果目标在low-&amp;gt;mid区，只需要判断一次，而如果在mid-&amp;gt;high需要判断2次（需要先判断不在low-&amp;gt;mid区，再判断在mid-&amp;gt;high区），斐波那契查找的low-&amp;gt;mid区更大(0.618&amp;gt;0.5)，有更多的概率只需要判断一次，所以总体判断次数更少。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;        原因1看起来有理，可是现在的编译器只要遇到/2操作，都会优化为&amp;gt;&amp;gt;1，位运算比加减法只快不慢，所以原因1不成立。原因2也貌似有些道理，可是如果按照这个道理推理下去，把分割点设在0.99岂不是更好？可0.99明显是个垃圾分割点，二分力度很差，所以这个理由我也不认可。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;       斐波那契查找的时间复杂度还是O(log 2 n )，但是 与折半查找相比，斐波那契查找的优点是它只涉及加法和减法运算，而不用除法，而除法比加减法要占用更多的时间，因此，斐波那契查找的运行时间理论上比折半查找小，但是还是得视具体情况而定。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;       对于斐波那契数列：1、1、2、3、5、8、13、21、34、55、89……（也可以从0开始），前后两个数字的比值随着数列的增加，越来越接近黄金比值0.618。比如这里的89，把它想象成整个有序表的元素个数，而89是由前面的两个斐波那契数34和55相加之后的和，也就是说把元素个数为89的有序表分成由前55个数据元素组成的前半段和由后34个数据元素组成的后半段，那么前半段元素个数和整个有序表长度的比值就接近黄金比值0.618，假如要查找的元素在前半段，那么继续按照斐波那契数列来看，55 = 34 + 21，所以继续把前半段分成前34个数据元素的前半段和后21个元素的后半段，继续查找，如此反复，直到查找成功或失败，这样就把斐波那契数列应用到查找算法中了。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image --&gt;
&lt;figure class="wp-block-image"&gt;&lt;img alt="" src="https://img-blog.csdn.net/20151123231338900?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"/&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt; 比如上面的表格中的数据元素为11,在斐波那契数列中是找不到，所以在末尾填充元素，例如在上述的位序中的11,12处添加21,21(重复的是末尾元素。)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;代码实现：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;/*@para:a[]:给定待查的数组，n为要查找的数组的个数，key:为要查找的关键字*/int Fibonacci_Search(int *a,int n,int key){	int low,high,mid,i,k=0;	low=1;	/* 定义最低下标为记录首位 */	high=n;	/* 定义最高下标为记录末位 */	while(n&amp;gt;F[k]-1)//计算n位于斐波那契数列中的位置		k++;	for (i=n;i&amp;lt;F[k]-1;i++)//将不满的数值补全		a[i]=a[n];		while(low&amp;lt;=high)	{		mid=low+F[k-1]-1;		if (key&amp;lt;a[mid])		{			high=mid-1;					k=k-1; /*说明:low=mid+1说明待查找的元素在[low,mid-1]范围内，k-=1 说明范围[low,mid-1]内的 元素个数为F(k-1)-1个，所以可以递归的应用斐波那契查找*/		}		else if (key&amp;gt;a[mid])		{			low=mid+1;					k=k-2;//说明:low=mid+1说明待查找的元素在[mid+1,hign]范围内，k-=2 说明范围 //[mid+1,high]内的元素个数为n-（F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k- //2)-1个，所以可以递归的应用斐波那契查找 		}		else //前面两种情况都不满足，才会执行else		{			if (mid&amp;lt;=n)				return mid;		/* 若相等则说明mid即为查找到的位置 */			else 				return n;		}			}	return 0;} &lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;大部分说明都忽略了一个条件的说明：n=F(k)-1， 表中记录的个数为某个斐波那契数小1。这是为什么呢？&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;我想了很久，终于发现，原因其实很简单：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;是为了格式上的统一，以方便递归或者循环程序的编写。表中的数据是F(k)-1个，使用mid值进行分割又用掉一个，那么剩下F(k)-2个。正好分给两个子序列，每个子序列的个数分别是F(k-1)-1与F(k-2)-1个，格式上与之前是统一的。不然的话，每个子序列的元素个数有可能是F(k-1)，F(k-1)-1，F(k-2)，F(k-2)-1个，写程序会非常麻烦&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>插入排序和归并排序(渐进运行时间对比)</title><link>https://lategege.com/p/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E5%92%8C%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F-%E6%B8%90%E8%BF%9B%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4%E5%AF%B9%E6%AF%94/</link><pubDate>Mon, 13 Jul 2020 01:07:24 +0000</pubDate><guid>https://lategege.com/p/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E5%92%8C%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F-%E6%B8%90%E8%BF%9B%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4%E5%AF%B9%E6%AF%94/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;1、插入排序&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;假定有一个数组array = [a1,a2,a3,a4,a5....an];&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;使用插入排序代码如下(手敲代码,有可能无法运行,但是排序思想肯定是正确的)：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;//第一个循环时间耗费c1*array.length
for(int i=1;i&amp;lt;array.length;i++){
 var temp =array[i];
 int j=i-1;
 //第二个循环时间耗费c2*array.length
 while(j&amp;gt;=0&amp;amp;&amp;amp;array[j]&amp;gt;temp){
 array[j+1]=array[j];
 j=j-1;
 }
 array[j+1]=temp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;所以插入排序的运行时间为c1*array.length *c2*array.length 若 array.length 是n的话 对于n平方这个数，c1.c2常量可忽略。所以插入排序的运行时间记作θ(n²)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;2、归并排序&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;假定有一个数组array = [a1,a2,a3,a4,a5....an];&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;public void start(int[] array,int left,int right){
 if(left&amp;lt;right){
 int middle =(left+right)/2;
 start(array,0,middle-1);
 start(array,middle+1,right);
 merge(array,left,middle,right);
 }
}
public void merge(int []a,int left,int middle,int right){
 int[] tempArray = new int[a.length];
 int rightstart = middle+1;
 int temp = left;
 int third = left;
 while (left&amp;lt;=middle&amp;amp;&amp;amp;rightstart&amp;lt;right){
 //一个一个往新的数组里边放值，遍历被分开的两个数组；
 //比较两个小数组相应下标位置的数字大小，小的先放进新数组；
 if(a[left]&amp;lt;=a[rightstart]){
 //比较左边第一个和右边第一个，然后把小的那一个放进到新数组；再将下标往后移动一位，继续比较；
 tempArray[third++] = a[left++];
 }else{
 tempArray[third++] = a[rightstart++];
 }
 }
 //如果左边还有数据需要拷贝，把左边数组剩下的拷贝到新数组；
 while (left&amp;lt;=middle){
 tempArray[third++] = a[left++];
 }
 //如果右边还有数据需要拷贝，就把右边数组剩下的拷贝到新数组；
 while (rightstart&amp;lt;=right){
 tempArray[third++] = a[rightstart++];
 }
 while (temp&amp;lt;=right){
 a[temp] = tempArray[temp++];
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;归并排序的运行时间需要借助递归树，所以这里直接列出结果为θ(nlgn)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;所以归并排序随着n的增长，其运行时间是大大小于插入排序的。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>素数的图形化</title><link>https://lategege.com/p/%E7%B4%A0%E6%95%B0%E7%9A%84%E5%9B%BE%E5%BD%A2%E5%8C%96/</link><pubDate>Fri, 10 Jul 2020 02:06:11 +0000</pubDate><guid>https://lategege.com/p/%E7%B4%A0%E6%95%B0%E7%9A%84%E5%9B%BE%E5%BD%A2%E5%8C%96/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;最近在摸索哈希算法的原理性东西，比如md5算法的设计原理，网上大多数文章都是实现过程，并没有谈到为什么要这么设计，估计设计原理得看相应论文才能得到答案了，不过在摸索过程中发现一个有趣的东西，那就是素数，这个数的定义是只能被自身和1整除，除是什么呢？除在我这边的定义是能被等分，就是说素数就是不能被等分的数，于是乎就有了想把素数绘制成图案的想法，看看以1000以内的素数为半径画圆，或者以坐标为素数、半径为素数花圆，看看这个图是怎么样的，程序如下：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
 &amp;lt;meta charset="utf-8"&amp;gt;
 &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;canvas id="myCanvas" width="1500" height="1500" style="border: 1px solid black;"&amp;gt;
&amp;lt;/canvas&amp;gt;
&amp;lt;script&amp;gt;
 function isPrime(number) {
 //判断输入是否为number类型，是否为整数
 if (typeof number !== 'number' || !Number.isInteger(number)) {
 return false;
 };
 //小于2都不是素数
 if (number &amp;lt; 2) {
 return false
 }
 ;
 //2是素数，被2整除的都不是素数
 if (number === 2) {
 return true
 } else if (number % 2 === 0) {
 return false;
 }
 ;
 // 判断是否能被奇数整除，最大循环为该数值的开方
 var squareRoot = Math.sqrt(number);
 for (var i = 3; i &amp;lt;= squareRoot; i += 2) {
 if (number % i === 0) {
 return false;
 }
 ;
 }
 return true;
 }
 function draw(nub) {
 var canvas = document.getElementById("myCanvas");
 var ctx = canvas.getContext("2d");
 ctx.beginPath();
 var radius = 0;
 var circle = {x: 750, y: 750, r: radius};
 ctx.strokeStyle = "blue";
 for (var i = 2; i &amp;lt;= nub; i++) {
 if (isPrime(i)) {
 var scale = i;
 circle.r = scale;
 // rrx 表示 ctx.arc(circle.r, circle.r, circle.x, 0, Math.PI * 2, false);
 // rrr 表示 ctx.arc(circle.r, circle.r, circle.r, 0, Math.PI * 2, false);
 ctx.arc(circle.r, circle.r, circle.x, 0, Math.PI * 2, false);
 ctx.stroke();
 }
 }
 }
 draw(1000);
&amp;lt;/script&amp;gt;
&lt;p&gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;上述程序不是伪代码,复制进文本命名为.html就可以直接用浏览器打开查看运行结果。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;我得到的图案是这样的，是不是无规律下有觉得很有规律。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image {"id":189,"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-189" src="https://img.lategege.com:30443/images/web/uploads/2020/07/xxr-1024x1024.png"/&gt;&lt;figcaption&gt;xxr&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"id":190,"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-190" src="https://img.lategege.com:30443/images/web/uploads/2020/07/rrx-1024x1024.png"/&gt;&lt;figcaption&gt;rrx&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"id":191,"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-191" src="https://img.lategege.com:30443/images/web/uploads/2020/07/rrr-1024x1024.png"/&gt;&lt;figcaption&gt;rrr&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"id":192,"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" class="wp-image-192" src="https://img.lategege.com:30443/images/web/uploads/2020/07/rxx-1024x1024.png"/&gt;&lt;figcaption&gt;rxx&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>搜索引擎如何检索排名？</title><link>https://lategege.com/p/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E5%A6%82%E4%BD%95%E6%A3%80%E7%B4%A2%E6%8E%92%E5%90%8D/</link><pubDate>Thu, 02 Jul 2020 02:59:23 +0000</pubDate><guid>https://lategege.com/p/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E5%A6%82%E4%BD%95%E6%A3%80%E7%B4%A2%E6%8E%92%E5%90%8D/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;写这篇文章的目的是因为最近看了一些算法书籍，对搜索引擎核心算法有了个大致的了解，就当做个笔记吧。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;搜索引擎的核心思想就是索引，类似书籍的目录，索引技术其实最早起源于古巴比伦图书馆，这个伟大的发明不是现在计算机科学的产物，如果没有索引，想象一下在一本书中查找想要的内容，就需要一页一页的查询，运气好的很快能找到，运气不好在最后一页出现的话，前面所有的时间都白白浪费了，索引就很好的解决了这个问题，因为索引信息量少，很精简，基本上几页就能描述整本书的大体内容结构。举个例子，我们在词典上查询一个词“乘风破浪“，那我们肯定会翻到字母c开头的目录页查找这个词所在的页码，然后翻到对应页面查看具体这个词是什么意思。搜索也是一样的，我们在搜索引擎搜索“乘风破浪”，搜索引擎会有一个索引数据库，这个数据库中存放了“乘风破浪”---第5页，然后展示出来，当然还有一些第五页这个网页的一些描述信息。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;除了简单的索引，还有一个问题急需解决，那就是我搜索多词的时候，搜索引擎怎么处理，比如我搜“屋子 草莓”，我们知道“屋子“、“草莓“在第一页、第二页都出现，但是两个词出现的位置相邻距离不同，那“屋子 草莓”搜出来的页面怎么排序？很显然，两个词距离相近应该排在前面，所以在索引信息中只记录一个词出现的页数是不够的，还需要记录这个词出现的位置，比如屋子----1-2 2-10 代表第一页的第二个位置，第二页的第十个位置，草莓----1-3 2-16代表第一页的第三个位置，第二页的第十六个位置。很明显第一页两个词相邻近就应该排在前面。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;网页中除了正文，其实还有用户在浏览器上看不到的标签数据，如&amp;lt;title&amp;gt; &amp;lt;/title&amp;gt; &amp;lt;body&amp;gt;&amp;lt;/body&amp;gt; 搜索引擎一般也将标签数据记录在索引中，如&amp;lt;title&amp;gt; 2-10 &amp;lt;/title&amp;gt; 2-50 记录后搜索引擎就能满足一些特殊搜索，如用户搜索在网页title标签中的关键字 由于关键字在索引中也有记录位置 所以在title标签中的位置就变成了简单的数学比较运算。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;搜索引擎的核心排名机制是什么样的？这要追溯到谷歌创始人发表的pageRank论文中的排名算法。一开始搜索引擎根据超链接来决定一个网页的权重，比如a-&amp;gt;b ,c-&amp;gt;b 那a的权重是1，c的权重是1，b的权重是1+1=2，所以哪个网页的超链接多，哪个网页的权重值就大，但是这会造成一个严重的问题，比如 a-&amp;gt;b ,c-&amp;gt;b,b-&amp;gt;a 这种循环链接的方式就会让网页的权重一直增加，于是pageRank论文中就提出了一种随机访问思想，模拟用户访问网页形式，从一个网页随机访问超链接，跳入下一个网页，再随机访问超链接，这样执行几万百万次，得出访问过程中网页被访问的概率就是这个网页的权重，这种方式可以很好和超链接方式获取的权重相结合。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;现代搜索引擎除了上述这些核心算法外，还做了很多很多事情，其复杂程度非常高，比如搜索引擎需要解决人为故意增加一些垃圾超链接来提升权重，还有一些关键字与网页相关度优化等。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item></channel></rss>