Author: xnnyygn

  • 看似简单的一个问题:请求速率限制问题

    最近遇到一个场景,在每分钟错误计数达到250时发送消息。这里的每分钟并不是说整点的几分,有可能是现在16:16:16到16:17:16。 我了解到周围有人是用分钟的定时器来近似实现的,首先这样就限制了是整点的分秒,其次只限于对时间不敏感的场景,第三不能精确到秒,比如要求1次每秒的限制,因为定时器中任务执行很可能超过1s,而且还有并发的副作用。 那么直接在每次错误后向前扫描数量的笨方法呢?明显效率太低。 个人对于这个问题进一步分析,认为这个属于请求速率限制问题,并且找到的合适的关键字rate limit之后去google。发现stackoverflow有关于这类问题的讨论。在阅读了诸多资料之后,自己了解到两种专门针对请求速率限制的算法:Leaky Bucket和Token Bucket。下面简单介绍一下两种算法。

  • postgresql小试

    作为一个只用过mysql,外加在公司用oracle的苦逼程序员,今天趁着感冒差不多好的当口,学学之前就碰到过,但是始终没自己动手试过的postgresql。 首先说明一下,我是在vagrant做的虚拟机中搭建的postgresql。postgresql的具体安装方法是 sudo apt-get update sudo apt-get install postgresql 安装好之后你可以通过下面的方式测试 sudo -u postgres psql -l 如果列出一些数据库的话,那就代表成功。基本上apt-get install方式安装的不会失败的,除非你有别的程序占据了5432端口(postgresql默认端口)之类的。 为了方便,你需要建立一个数据库自己用。

  • [Java]排列和组合算法

    下午花了点时间,把排列和组合算法搞出来了。网上一堆资料看了不靠谱,要么是不明所以的变量名,要么直接帖代码。 排列 实现要点:交换和顺序处理。考虑对[1, 2, 3, 4]排列。实际是排列 [1], [2, 3, 4] [2], [1, 3, 4] [3], [2, 1, 4] [4], [2, 3, 1] 排列[1], [2, 3, 4]时,实际是排列 [1, 2], [3, 4] [1, 3], [2, 4] [1, 4], [3, 2] 从上面的分解可以看出,代码中实际要做的是交换当前的数字和右边数字中的某一个,具体来说,第一步是分别交换第一个和第一个,第一个和第二个,第一个和第三个,第一个和第四个。第二步是交换第二个和第二个,第二个和第三个,第二个和第四个。依次类推。递归的停止条件是右边的数组仅有一个元素,比如[1, 2, 3], [4]。具体代码如下,注意参数比较多的permutation方法。 start和end分别是第二个数组的起始位置和结束位置,当两者相等时,右边的数组只有一个元素,这时就可以直接输出排列的数组。 一般情况下,交换当前元素和右边数字中的一个,递归排列,在结束时交换回来(这步是必须的)。

  • 哈希表二次探测

    最近重新看哈希表的东西,发现自己大致能看懂了。以下是自己从书中了解的几个重点。 二次探测是一个根据哈希函数散列到同一个位置即碰撞时重新查找的方式,具体来说,假如i是哈希函数得到的位置,如果i有值了,那么尝试查找i + 1×1,i + 2×2,i + 3×3…i + NxN。 其次和二次探测相关的是在哈希表的大小为一个质数,并且使用量在一半以下时出现碰撞或者聚类的机率很小。 下面写一下实现哈希表二次探测个人认为主要的两个点,剩下的就是一些具体实现的细节问题了。 查找下一个质数 在容量超过一半时重新散列。需要找到比当前表大小大一些的质数。个人实现如下(Java): public static int findNextPrime(int current) { int nextPrime = current; if(nextPrime % 2 == 0) nextPrime++; while(!isPrime(nextPrime)) { nextPrime += 2; } return nextPrime; } private static boolean isPrime(int number) { for (int i = 3; i <= Math.sqrt(number); i +=…

  • 递归的进一步学习-动态规划之最少硬币问题

    阶乘和斐波那契数列相信各位都很熟悉,典型的教材式递归例子,因为简单退化为循环也没啥难度。不过依赖于递归的问题并不都是那么简单的,比如最少硬币问题,需要称为动态规划(DP)的方式来实现。 所谓动态规划个人理解就是缓存一下递归的结果。比如斐波那契数列,简单的实现方式是: public int fib(int n) { if(n == 0 || n == 1) return 1; return fib(n – 2) + fib(n – 1); } 假如计算fib(5),事实上计算了一次fib(4),两次fib(3),三次fib(2),四次fib(1),两次fib(0),换句话说,重复计算了(1 – 1) + (2 – 1) + (3 – 1) + (4 – 1) + (2 – 1)有7次之多。理想情况下每个fib(n)只要计算一次。改进方法很简单,增加cache,其实就是简单的映射数组: public int fib(int n) { if(fib_cache[n] > 0) return fib_cache[n]; //…

  • Dijkstra非负加权有向图最短路径算法

    成果在这里,前天完成的,花了大约半天的时间。参考资料主要是维基百科和一篇博客。 在分析自己代码前,先写一点自己对于Dijkstra算法的理解。 u s v

  • 二叉查找树学习笔记

    二叉查找树如其名是一种主要用于查找的每个非叶子节点最多有两个子节点(左右子节点)的树,其特点是: a b c 任何a, b, c三个节点(父节点和两个子节点,左右子节点可能只有一个或者都没有)满足a < b < c。对于相等的值使用节点计数+1或者节点中的链表等不同的方式来处理。 a < b < c这个条件乍一看和二分搜索binary_search很像,实际查找元素时也是类似的,时间复杂度在O(logn)。 一般的二叉查找树(不考虑链表退化和平衡的处理)包含查找,创建树/增加节点,删除节点,前/中/后序遍历等操作,下面分别介绍。

  • 迟到的大学数据结构作业——堆排序

    时隔好几年,才发现算法的趣味性,比如堆排序。堆排序同时也是大学时自己未完成的数据结构作业之一,就像《高等数学》一样之后都是抄抄了事…… 个人理解,堆排序是一个有名的排序算法,比较适合优先级队列。虽然时间复杂度是O(nlogn),论排序算法中比较快的还是快速排序。堆排序的特点是使用完全或近似完全二叉树来模拟一种父节点肯定比子节点大(最大堆)或者小(最小堆)的结果,并通过多次“删除”根节点并恢复结构来达到“模拟”排序的目的。实践中需要关注的是三点:

  • 【算法学习】趁热打铁,插入,选择和归并排序

    插入排序 插入排序最直观的解释是不断拿扑克牌时排序的动作。一开始你只有一张8,第二张是5,你把5放在8的左边。第三张是9,你放在8的右边。第四张是2,你放在最左边。拿这四张牌的过程可以认为是排序4个数字的过程。步骤可以归纳为: 拿起一张牌 -> 取下一个数 比较现有牌,具体来说是从右往左找到第一个不小于当前牌的位置 -> 将所有大于当前数的数字向右移动一位 放入牌 -> 放置数字 形象上来讲,放置数字的过程是多个数字右移的过程,所以不需要swap。Java代码如下: public void sort(int[] xs) { int len = xs.length; if (len <= 1) return; for (int i = 1; i < len; i++) { int x = xs[i]; int j = i; while (j > 0 && xs[j – 1] > x) {…

  • 【算法学习】快速排序(原地分区)

    最近重新学习算法,先从几个排序算法开始。快速排序是比较有名的一个排序算法,基本原理我明白,但是之前只会筛选构造新序列分区的方式,现在要学的是原地分区的方式。 成果代码如下,使用Java编写(C/C++的数组和指针操作忘得差不多了)。 private void swap(int[] array, int i, int j) { if (i == j) return; System.out.println(String.format(“%d <-> %d”, i, j)); int a = array[i]; array[i] = array[j]; array[j] = a; } private int partition(int[] ns, int begin, int end) { int pivotIndex = (begin + end) / 2; int pivot = ns[pivotIndex]; swap(ns, pivotIndex,…