log2取整效率测试
RMQ问题中有个ST算法,当然还有个标准算法.LCA问题可以转化为带限制的RMQ(RMQ+-1)问题来解决.我们姑且认为这些问题的时间复杂度是查询$O(1)$的.但是,注意到对于RMQ(/+-1)问题,这个问题有个长度的限制,我们记为n.那么对于每个查询,我们都要询问一个范围[L,R],1<=L<=R<=n.这个区间的长度为R-L+1.然后我们将原区间分成两个Sparse Table上的项,即长度为int_log2(R-L+1)-1的两个子区间求解min,即合并两个子区间的信息.
那么问题来了,int_log2(R-L+1)-1也是要花时间的.有些人推荐用floor(log(n)/lg2-1),非常仪赖于cmath库的log函数,潜意识里认为它是$O(1)$的.我非常反对这种"眼不见为净"的人,于是我想出了一系列算法求解int_log2(n)-1.我做了一些测试来对比这些算法的效率.
1) cmath log函数求解
2) iterate 迭代右移求解
3) binary 二分右移求解
4) float conversion 转换为浮点数进行位运算求解
下面简述一下这些求解法.
log函数求解
<cmath>库中提供了函数log.直接调用log计算.
代码
inline int ilog2_cmath(int n){
return floor(log(n+.0)/l2)-1;
}
就这样.非常简单.
迭代右移求解
我们循环右移n,当n为0时退出循环.每次循环将一个计数器加.
inline int ilog2_iter(int n){
int i;for(i=0,n>>=1;n;++i) n>>=1;
return i-1;
}
二分右移求解
我们二分n有的bits.
inline int ilog2_bin(int n){
int i=0;
if(n>>16) i|=16,n>>=16;
if(n>>8) i|=8,n>>=8;
if(n>>4) i|=4,n>>=4;
if(n>>2) i|=2,n>>=2;
if(n>>1) i|=1,n>>=1;
return i-1;
}
转换为浮点数进行位运算求解
这个办法比较难以理解了.
我们需要从浮点数的构造着手.
Float: [1bit sign bit][8bit exponent bits][23bit mantissa bits]
00000000101000100010001000100010
^ 符号位
^------^ 指数位
^---------------------^ 尾数位(有效数字.[开头的1已省去])
我们要获取的,就是这个符号位的信息.
这个符号位恰好是int_log2(n)-1.因此,我们甚至无需减.这是一个非常好的性质.
那么我们只需要把一个整数转成Float,右移位再用做与&运算掩码即可.
inline int ilog2_kf(int n){
float q=(float)n;
return (*(int*)&q)>>23&31;
}
代码很短.开O3时很快.
测试
实践是检验真理的唯一标准.
代码
#define sizex 100000000
#define l2 0.6931471805599453
#include <cmath>
inline int ilog2_cmath(int n){
return floor(log(n+.0)/l2)-1;
}
inline int ilog2_iter(int n){
int i;for(i=0,n>>=1;n;++i) n>>=1;
return i-1;
}
inline int ilog2_bin(int n){
int i=0;
if(n>>16) i|=16,n>>=16;
if(n>>8) i|=8,n>>=8;
if(n>>4) i|=4,n>>=4;
if(n>>2) i|=2,n>>=2;
if(n>>1) i|=1,n>>=1;
return i-1;
}
inline int ilog2_kf(int n){
float q=(float)n;
return (*(int*)&q)>>23&31;
}
#include <cstdio>
#include <random>
#include <malloc.h>
#include <sys/time.h>
using namespace std;
int *data,res;
long long mytic(){
long long result = 0.0;
struct timeval tv;
gettimeofday( &tv, NULL );
result = ((long long)tv.tv_sec)*1000000 + (long long)tv.tv_usec;
return result;
}
#define dic1() disA(generator)
void genData(int a){
mt19937 generator;
uniform_int_distribution<int> disA(0,2147483647);
int i=0;
for(;i<a;++i) data[i]=dic1();
}
void testN(int k){
int i;
printf("cmath log method\n");
long long start=mytic();
for(i=0;i<k;++i){
res=ilog2_cmath(data[i]);
}
start=mytic()-start;
printf("%d\n",res);
printf("Time usage: %lld us\n",start);
}
void testU(int k){
int i;
printf("iterate log method\n");
long long start=mytic();
for(i=0;i<k;++i){
res=ilog2_iter(data[i]);
}
start=mytic()-start;
printf("%d\n",res);
printf("Time usage: %lld us\n",start);
}
void testP(int k){
int i;
printf("binary divide log method\n");
long long start=mytic();
for(i=0;i<k;++i){
res=ilog2_bin(data[i]);
}
start=mytic()-start;
printf("%d\n",res);
printf("Time usage: %lld us\n",start);
}
void testUP(int k){
int i;
printf("float convertion log method\n");
long long start=mytic();
for(i=0;i<k;++i){
res=ilog2_kf(data[i]);
}
start=mytic()-start;
printf("%d\n",res);
printf("Time usage: %lld us\n",start);
}
int main(){
int a,b,c,i,j,k,l,m,n,N,U,P,UP;
data=(int*)malloc(400000000*sizeof(int));
while(printf("0 to quit> "),scanf("%d",&a),a){
printf("CMLog Iter Bina Flcv\n");
scanf("%d%d%d%d",&N,&U,&P,&UP);
if(a>400000000) continue;
genData(a);
if(N) testN(a);
if(U) testU(a);
if(P) testP(a);
if(UP) testUP(a);
printf("%d %d %d %d\n",ilog2_cmath(a),ilog2_iter(a),ilog2_bin(a),ilog2_kf(a));
}
free(data);
return 0;
}
测试结果
//数据大小: 4×10^8数 //cmath log
19277285 us
~
19.3 s //iterate
6197113 us
~
6.2 s 3.1x faster than cmath log //binary iterate
2018023 us
~
2.0 s 3.1x faster than iterate //float bit operation
406996 us
~
0.41 s 5.0x faster than binary iterate
and
47.4x faster than cmath log
数据无误.结果无误.
(机器数据:i7 4700m 2.0GHz (16GB=15.6GiB RAM DDR3 800MHz)?)
(编译命令:gcc ... -O3)
结果分析
第四种方法特别快.事实上从用时中看得出来每一次运算几乎是整的2个时钟周期.
由此看来i7 int2float的效率是时钟周期.
下面贴-O2的数据.依次为CMLog Iter Binary FloatConvBitOperation 单位us 数据均由mt19937随机数算法随机生成
19301840 6186242 2379282 400056
下面贴-O1的数据.
19267001 6466776 2446129 385642
下面贴-O的数据.
19302953 6472134 2460882 400694
下面贴-Os的数据.
19247815 8508664 2500930 390131
下面贴不带optimize选项的数据.
19198380 25362664 6802623 1290716
下面贴-O3带-march=corei7-avx的数据.
19301717 6196286 2010706 377347
数据分析:
只要带optimize选项,fclm都是最快的,在0.4s左右.否则fclm还是最快的,1.2s左右.
-Os的Iter从6.2s变成8.5s,变慢了许多.
不开optimize的除了cmath log(已编译好直接连接)外都慢了很多很多,3-4x左右.大约是函数调用开销!
corei7-avx减少了fclm的时间.
程序优化notes: 开-O2的地方基本不用担心速度了.
评测程序: 修改了并查集测试的程序用.
编程建议: 使用fclm方法来获取highbit等intlog2的应用.
优点:
1) 代码短
2) 没有判断.充分利用处理器架构.
3) 代码不容易看懂.
4) 在IEEE754 compatiable的机器上均可使用.(几乎没有不能使用的机器.)
5) 非常非常快.几乎相当于lowbit的速度,然而求出lowbit必须用fclm转换成指数.
缺点:
1) 在特别特别特别老或特别奇葩的机器上不能用.
2) 动态类型语言不可用.可以用native extension或biniter解决.动态语言不需要高效率.
该问题完美解决.
2个时钟周期的算法无论如何都不能看作$O(\log{\log{n}})$了.显然是$O(1)$时间复杂度的算法.
log2取整效率测试的更多相关文章
- java的四种取整方法
java 中取整操作提供了四种方法:分别是: public static double ceil(double a)//向上取整 public static double floor(double ...
- 关于 pgsql 数据库json几个函数用法的效率测试
关于 pgsql 数据库json几个函数用法的效率测试 关于pgsql 几个操作符的效率测试比较1. json::->> 和 ->> 测试方法:单次运行100次,运行10个单次 ...
- 完全二叉树的高度为什么是对lgN向下取整
完全二叉树的高度为什么是对lgN向下取整呢? 说明一下这里的高度:只有根节点的树高度是0. 设一棵完全二叉树节点个数为N,高度为h.所以总节点个数N满足以下不等式: 1 + 21 + 22 +……+ ...
- AVA取整以及四舍五入
AVA取整以及四舍五入 import java.math.BigDecimal; public class Test { public static void main(String[] args) ...
- C#采用的是“四舍六入五成双”、上取整、下取整
c# 四舍五入.上取整.下取整 Posted on 2010-07-28 12:54 碧水寒潭 阅读(57826) 评论(4) 编辑 收藏 在处理一些数据时,我们希望能用“四舍五入”法实现,但是C#采 ...
- python中的数字取整(ceil,floor,round)概念和用法
python中的数学运算函数(ceil,floor,round)的主要任务是截掉小数以后的位数.总体来说 就是取整用的.只是三者之间有微妙的区别: floor() :把数字变小 ceil() : ...
- Web侦察工具HTTrack (爬取整站)
Web侦察工具HTTrack (爬取整站) HTTrack介绍 爬取整站的网页,用于离线浏览,减少与目标系统交互,HTTrack是一个免费的(GPL,自由软件)和易于使用的离线浏览器工具.它允许您从I ...
- c# 四舍五入、上取整、下取整
在处理一些数据时,我们希望能用“四舍五入”法实现,但是C#采用的是“四舍六入五成双”的方法,如下面的例子,就是用“四舍六入五成双”得到的结果: double d1 = Math.Round(1.25, ...
- lua浮点数取整
向下取整 math.floor(num) 向上取整 math.ceil(num) 取整取余 math.modf(num) 测试 num = 12.4 print(math.floor(num)) 12 ...
随机推荐
- EasyUI——弹窗展示数据代码
JS代码: $("#editDv").css("display","block"); $("#editDv").dial ...
- javascript函数自调用
1. 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块. 2. 将函数用 “()”括起来, 后面再加一个“()” 3. javascript函数的内置对象arguments对象, 它包 ...
- 利用less监视模式实时预览样式刷新浏览器
[前言]此处介绍的方法只是我个人的用法,相信大家有更好更简洁的方式. 上次写到利用LiveReload解放F5.而且LiveReload可以编辑sass/less/stylus.但是可惜发现LiveR ...
- XCode7继续用http协议解决办法
昨天被苹果放鸽子也没升级iOS9,今天升级了Xcode7,同时手机升级了iOS9,发现项目报错,查了查才知道是iOS9不支持不安全的http传输协议,让用https协议,这根本就不x现实,,服务端根本 ...
- 什么是POJO?
本文转载自百度文库,详细出处请参考: http://wenku.baidu.com/view/4a9ad533ee06eff9aef80765.html 我认为写的很准确,很抱歉没有找到作者的名字! ...
- Angulajs系列-01-入门
1.解决什么问题? a, controller的各种的创建 b,体验angular的双向绑定 2.怎么解决 2.1 引入angularjs 下载地址 2.2 创建controller的方法 2.2.1 ...
- GridView动态添加列之后,导致PostBack(回发)页面数据丢失问题解决
直入主题,首先声明,这个问题是无法解决的,特此在这说明 一.如何动态添加列,如下: 在页面重写OnInit事件,至于为什么要在这个事件写,根据页面的声明周期和经验可知(不用去别的地方找了,这个我找了之 ...
- 洛谷P1108 低价购买
题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...
- Sublime Text 3 笔记
Nearly all of the interesting files for users live under the data directory. The data directory is ~ ...
- python库requests登录zhihu
废了很大劲,开始搞错了登录post信息的网址,后来没找到xsrf信息,看了很多文章才搞定. 大概过程如下: 打开登录页面,同时打开fldder,输入信息去监控过程. 查看post了哪些信息,哪些是自己 ...