[underscore源码学习]——`>>` 运算符和二分查找
这是一篇记录学习 underscore v0.0.5 的fragment,觉得有点意思,和大家分享一下。
先看_.sortedIndex的源码,它用来确定 obj 在 array中的位置(array升序):
_.sortedIndex = function(array, obj, iterator) {
iterator = iterator || _.identity;
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
};
_.identity = function(value) {return value};
代码虽短却两个我们不常用的姿势:>>
运算符和二分查找。
右移运算符 >>
右移运算符的计算规则如下
int a = 8; int b = a >> 1; // b ?
先将 8 转化为二进制数,(在 C 中,32位操作系统下int 类型占2个字节,即16bit)
0000 0000 0000 1000
整体右移 1 位,高位不足添 0
0000 0000 0000 0100
再将新的二进制数转化为十进制得到 b = 4;
那么,代码 var mid = (low + high) >> 1
mid 变量取值是多少呢?因为好(dou)奇(bi),我作了一点尝试:
0 >> 1 // 0
1 >> 1 // 0
2 >> 1 // 1
3 >> 1 // 1
4 >> 1 // 2
5 >> 1 // 2
...
10 >> 1 // 5
11 >> 1 // 5
12 >> 1 // 6
...
由归纳法可得 a >> 1 === parseInt(a / 2)
。这点相信眼尖的同学早就看出来了。
二分查找
既然我们已经知晓mid >> 1 === parseInt((low + high) / 2)
,我们再来回顾下代码片段:
while (low < high) {
var mid = (low + high) >> 1; // (1)
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; // (2)
}
(1)我们已经知晓,mid 取得了一个中间数值,在(2)中是一个三元运算符,解释起来,就是用一个有序数组 array 的中间数和目标数进行比较,则如果中间数比目标数小,就只需要比较中间数右侧的值,反之亦然。然后继续循环...
等价于下面的代码,由此差距立显:
while (low < high) {
var mid = (low + high) >> 1;
var a = iterator(array[mid]);
var b = iterator(obj)
if(a == b) {
return mid;
} else if(a < b) {
low = mid + 1;
} else {
high = mid
}
}
题外话
内容照理说已经讲完了,但对于 >>
运算符我还是有些疑虑。既然有a >> 1 === parseInt(a / 2)
那么可否假设 a >> 2 === parseInt(a / (2*2))
呢?
0 >> 2 // 0
3 >> 2 // 0
4 >> 2 // 1
7 >> 2 // 1
8 >> 2 // 2
11 >> 2 // 2
12 >> 2 // 3
继而a >> 3 === parseInt(a / (2*2*2))
,可得a >> n === parseInt(a / (X))
X 为 2 的 n 次方。
左移运算符 <<
与右移运算符相对的叫左移运算符,记为 <<
,相信它也不是吃干饭的。
有了前面的铺垫,我这里一步到位,假设有a << n === parseInt(a * (X))
X 为 2 的 n 次方。简单验证下:
1 << 1 // 1*2 = 2
2 << 1 // 2*2 = 4
3 << 2 // 3 * (2*2) = 12
5 << 4 // 5 * (2*2*2*2) = 80
...
最后的最后,由于水平有限(大家看出来了,忍住不要笑:-D),文章内容连蒙带猜,没有经过特别严谨的证明,欢迎拍砖!!!
[underscore源码学习]——`>>` 运算符和二分查找的更多相关文章
- Underscore.js 源码学习笔记(上)
版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ...
- [算法2-数组与字符串的查找与匹配] (.NET源码学习)
[算法2-数组与字符串的查找与匹配] (.NET源码学习) 关键词:1. 数组查找(算法) 2. 字符串查找(算法) 3. C#中的String(源码) 4. 特性Attribute 与内 ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
- 【 js 基础 】【 源码学习 】源码设计 (持续更新)
学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析第二部分:undersc ...
- 【 js 基础 】【 源码学习 】源码设计 (更新了backbone分析)
学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析 第二部分:unders ...
- [算法1-排序](.NET源码学习)& LINQ & Lambda
[算法1-排序](.NET源码学习)& LINQ & Lambda 说起排序算法,在日常实际开发中我们基本不在意这些事情,有API不用不是没事找事嘛.但必要的基础还是需要了解掌握. 排 ...
- [数据结构1.2-线性表] 动态数组ArrayList(.NET源码学习)
[数据结构1.2-线性表] 动态数组ArrayList(.NET源码学习) 在C#中,存在常见的九种集合类型:动态数组ArrayList.列表List.排序列表SortedList.哈希表HashTa ...
- JDK源码学习笔记——Integer
一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...
- [数据结构-线性表1.2] 链表与 LinkedList<T>(.NET 源码学习)
[数据结构-线性表1.2] 链表与 LinkedList<T> [注:本篇文章源码内容较少,分析度较浅,请酌情选择阅读] 关键词:链表(数据结构) C#中的链表(源码) 可空类 ...
随机推荐
- 如何处理好前后端分离的 API 问题(转载自知乎)
9 个月前 API 都搞不好,还怎么当程序员?如果 API 设计只是后台的活,为什么还需要前端工程师. 作为一个程序员,我讨厌那些没有文档的库.我们就好像在操纵一个黑盒一样,预期不了它的正常行为是什么 ...
- 最近学习工作流 推荐一个activiti 的教程文档
全文地址:http://www.mossle.com/docs/activiti/ Activiti 5.15 用户手册 Table of Contents 1. 简介 协议 下载 源码 必要的软件 ...
- 2018.09.28 hdu5434 Peace small elephant(状压dp+矩阵快速幂)
传送门 看到n的范围的时候吓了一跳,然后发现可以矩阵快速幂优化. 我们用类似于状压dp的方法构造(1(1(1<<m)∗(1m)*(1m)∗(1<<m)m)m)大小的矩阵. 然后 ...
- Django入门与实践-第23章:分页实现(完结)
http://127.0.0.1:8000/boards/1/ #从现在起,我们将在 board_topics 这个视图中来操作. python manage.py shell from django ...
- 如何在Linux中统计一个进程的线程数(转)
方法一: /proc proc 伪文件系统,它驻留在 /proc 目录,这是最简单的方法来查看任何活动进程的线程数. /proc 目录以可读文本文件形式输出,提供现有进程和系统硬件相关的信息如 CPU ...
- java NIO编程(转)
一.概念 在传统的java网络编程中,都是在服务端创建一个ServerSocket,然后为每一个客户端单独创建一个线程Thread分别处理各自的请求,由于对于CPU而言,线程的开销是很大的,无限创建线 ...
- Nginx中间件使用心得(三)
一.Nginx搭建系统需求 1.系统硬件:CPU >= 2Core,内存 >= 256M 2.自行搭建服务器(Linux操作系统) (1) 使用vmWare虚拟服务器 (2)使用 ...
- java基础-day5
第05天 java基础知识 今日内容介绍 u 方法的概述及基本使用 u 方法的练习及注意事项 u 方法的重载及参数传递 u 方法的操作数组的练习 第1章 方法的概述及基本使用 1.1 方法定义格 ...
- Reactor 模式在Netty中的应用
Reactor 模式在Netty中的应用 典型的Rector模式 mainReactor 服务端创建成功后,会监听Accept操作,其中ServerSocketchannel中的PipeLine中现在 ...
- laravel命令
新建控制器 php artisan make:controller IssuesController 新建控制器并自动生成对应RESTful风格路由相关CURD方法 php artisan make: ...