整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

位操作运算有什么奇技淫巧?(附源码)

运算

百度百科如下:

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作

位操作的优势

  • 位运算是一种底层的运算,往往比我们普通的运算要快上许多许多

  • 位运算是最高效而且占用内存最少的算法操作,执行效率非常高

  • 位运算操作的是二进制数,会拥有一些二进制的特性,在实际问题可以方便运用

  • 位运算只需较低的空间需求

  • 位运算使用能使程序变得更加简洁和优美

  • 位运算可以表示一些状态集合

运算符号

下面的a和b都是整数类型,则:


含义C语言
按位与a & b
按位或a | b
按位异或a ^ b
按位取反~a
左移a << b
带符号右移a >> b
无符号右移

优先级

C语言中位运算符之间,按优先级顺序排列为

优先级符号
1~
2<<、>>
3&
4^
5|
6&=、^=、|=、<<=、>>=

概念简介以及技巧

本文会以C语言的交互环境来做代码演示

常见的二进制位的变换操作

and运算 &

  • 判断奇偶数

对于除0以外的任意数x,使用x&1==1作为逻辑判断即可

if (x&1==1)
{

}
  • 判断某个二进制位是否为1

比如第7位, 0x40转到二进制是0100 0000,代表第7位是1.

if (n&0x40)
{
//TODO:添加你要处理的代码
}
  • 字节读取

(x >> 0) & 0x000000ff/* 获取第0个字节 */
(x >> 8) & 0x000000ff/* 获取第1个字节 */
(x >> 16) & 0x000000ff/* 获取第2个字节 */
(x >> 24) & 0x000000ff/* 获取第3个字节 */
  • 判断一个数是不是 22 的指数

bool isPowerOfTwo(int n) {
if (n <= 0) return false;
return (n & (n - 1)) == 0;
}
  • 取余

//得到余数
int Yu(int num,int n)
{
int i = 1 << n;
return num&(i-1);
}
  • 指定二进制位数截取

比如说16位二进制数A:1001 1001 1001 1000,如果来你想获A的哪一位的值,就把数字B:0000 0000 0000 0000的那一位设置为1.

比如说我想获得A的第三位就把B的第三位数字设置为1,则B为0000 0000 0000 0100,设置完之后再把A、B求与, 其结果若为0,说明A的第三位为0,其结果为1,说明A的第三位为1.

同理:若要获得A的第五位,就把B设置为0000 0000 0001 0000,之后再求与。

通常在我们的程序中,数字B被称为掩码,其含义是专门用来测试某一位是否为0的数值。

  • 统计二进制中 1 的个数

利用x=x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1会将该位变为0.

int Count(int x)
{ int sum=0;
while(x)
{ sum++;
x=x&(x-1);
}
return sum;
}

or操作

  • 生成组合编码,进行状态压缩

当把二进制当作集合使用时,可以用or操作来增加元素。合并编码 在对字节码进行加密时,加密后的两段bit需要重新合并成一个字节,这时就需要使用or操作。

  • 求一个数的二进制表达中0的个数

int Grial(int x)
{
int count = 0;
while (x + 1)
{
count++;
x |= (x + 1);
}
return count;
}

xor操作

  • 两个整数交换变量名

void swap(int &a, int &b) {
a ^= b;
b ^= a;
a ^= b;
}
  • 判断两个数是否异号

int x = -1, y = 2;
bool f = ((x ^ y) < 0); // true

int x = 3, y = 2;
bool f = ((x ^ y) < 0); // false
  • 数据加密

将需要加密的内容看做A,密钥看做B,A ^ B=加密后的内容C。而解密时只需要将C ^ 密钥B=原内容A。如果没有密钥,就不能解密!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define KEY 0x86
int main
{
char p_data[16] = {"Hello World!"};
char Encrypt[16]={0},Decode[16]={0};
int i;

for(i = 0; i < strlen(p_data); i++)
{
Encrypt[i] = p_data[i] ^ KEY;
}

for(i = 0; i < strlen(Encrypt); i++)
{
Decode[i] = Encrypt[i] ^ KEY;
}

printf("Initial date: %s\n",p_data);
printf("Encrypt date: %s\n",Encrypt);
printf("Decode date: %s\n",Decode);

return 0;
}
  • 数字判重

利用了二进制数的性质:x^y^y = x。我们可见,当同一个数累计进行两次xor操作,相当于自行抵销了,剩下的就是不重复的数

  • 找出没有重复的数

int find(int[] arr){
int tmp = arr[0];
for(int i = 1;i < arr.length; i++){
tmp = tmp ^ arr[i];
}
return tmp;
}

not操作

  • 交换符号

int reversal(int a) {
return ~a + 1;
}
  • 取绝对值(效率高)

  1. n>>31 取得n的符号

  2. 若n为正数,n>>31等于0

  3. 若n为负数,n>>31等于-1

  4. 若n为正数 n^0=0,数不变

  5. 若n为负数,有n^-1 需要计算n和-1的补码,然后进行异或运算,结果n变符号并且为n的绝对值减1,再减去-1就是绝对值

int abs(int n)
{
return (n ^ (n >> 31)) - (n >> 31);
}

也可以这样使用

int abs(int n)
{
int i = n >> 31;
return i == 0 ? n : (~n + 1);
}
  • 从低位到高位.将n的第m位置1

将1左移m-1位找到第m位,得到000...1...000, n在和这个数做或运算

int setBitToOne(int n, int m)
{
return n | (1 << (m-1));
}

同理从低位到高位,将n的第m位置0,代码如下

int setBitToZero(int n, int m)
{
return n & ~(1 << (m-1));
}

shl操作 & shr操作

  • 求2的N次方

 1<<n
  • 高低位交换

unsigned short a = 34520;
a = (a >> 8) | (a << 8);
  • 进行二进制逆序

unsigned short a = 34520;

a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
  • 获得int型最大最小值

int getMaxInt
{
return (1 << 31) - 1;//2147483647, 由于优先级关系,括号不可省略
}

int getMinInt
{
return 1 << 31;//-2147483648
}
  • m的n次方

//自己重写的pow方法
int pow(int m , int n){
int sum = 1;
while(n != 0){
if(n & 1 == 1){
sum *= m;
}
m *= m;
n = n >> 1;
}

return sum;
}
  • 找出不大于N的最大的2的幂指数

int findN(int n){
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。
return (n + 1) >> 1;
}
  • 二分查找32位整数的前导0个数

int nlz(unsigned x)
{
int n;

if (x == 0) return(32);
n = 1;
if ((x >> 16) == 0) {n = n +16; x = x <<16;}
if ((x >> 24) == 0) {n = n + 8; x = x << 8;}
if ((x >> 28) == 0) {n = n + 4; x = x << 4;}
if ((x >> 30) == 0) {n = n + 2; x = x << 2;}
n = n - (x >> 31);
return n;
}
  • 位图的操作

将 x 的第 n 位置1,可以通过 x |= (x << n)来实现

set_bit(char x, int n);

将 x 的第 n 位清0,可以通过 x &= ~(1 << n)来实现

clr_bit(char x, int n);

取出 x 的第 n 位的值,可以通过 (x >> n) & 1来实现

get_bit(char x, int n);

如下:

#define clr_bit(x, n) ( (x) &= ~(1 << (n)) )
#define set_bit(x, n) ( (x) |= (1 << (n)) )
#define get_bit(x, n) ( ((x)>>(n)) & 1 )

综合应用

以下仅列出,感兴趣可以参考下面链接.

关于操作计数方法

计算整数的符号

检测两个整数是否具有相反的符号

计算无分支的整数绝对值(abs)

计算两个整数的最小值(最小值)或最大值(最大值),而无需分支

确定整数是否为2的幂

标志延伸

  • 从恒定位宽扩展的符号

  • 从可变位宽扩展的符号

  • 通过3个操作从可变位宽扩展符号 有条件地设置或清除位而不分支

有条件地否定一个值而不分支

根据掩码合并两个值中的位

计数位设置

  • 计数位设置,幼稚的方式

  • 计算由查找表设置的位

  • 数位集,Brian Kernighan的方式

  • 使用64位指令对14、24或32位字中设置的位进行计数

  • 并行设置计数位

  • 从最高有效位到给定位置的计数位的设置(等级)

  • 从给定的计数(等级)中选择位位置(从最高有效位开始)

计算奇偶校验(如果设置了奇数位数,则为1,否则为0)

  • 天真地计算单词的奇偶性

  • 通过查找表计算奇偶校验

  • 使用64位乘法和模数除法计算字节的奇偶校验

  • 用乘法计算单词的奇偶校验

  • 并行计算奇偶校验

交换价值

  • 用减法和加法交换值

  • 用XOR交换值

  • 用XOR交换单个位

反转位序列

  • 反转位是显而易见的方式

  • 逐字查找表中的位反转

  • 通过3个操作(64位乘法和模数除法)反转字节中的位

  • 通过4个操作反转字节中的位(64位乘法,无除法)

  • 通过7个操作反转字节中的位(无64位,仅32位)

  • 与5 * lg(N)个运算并行地反转N位数量

模数除法(又名计算余数)

  • 在不进行除法运算的情况下,将模数除以1 << s(显而易见)

  • 在不进行除法运算的情况下以(1 << s)-1计算模数除法

  • 不进行除法运算就并行计算(1 << s)-1的模数除法

查找整数的整数对数2(又称最高位集的位置)

  • 使用O(N)运算找到MSB N设置为整数的对数2(显而易见的方法)

  • 查找具有64位IEEE浮点数的整数的整数对数2

  • 使用查找表找到整数的对数2

  • 在O(lg(N))运算中找到N位整数的对数2

  • 使用乘法和查找在O(lg(N))操作中找到N位整数的对数2

查找整数的对数以10为底的整数

查找整数的整数对数10

查找32位IEEE浮点数的整数对数基数2

查找32位IEEE浮点的pow(2,r)根的整数对数基数2(对于无符号整数r)

计算连续的尾随零位(或查找位索引)

  • 线性计算右边的连续零位(后缀)

  • 并行计算右侧连续的零位(后缀)

  • 通过二进制搜索计算右边连续的零位(跟踪)

  • 通过强制转换为浮点数来计算右侧连续的零位(跟踪)

  • 用模数除法和查找计算右边连续的零位(跟踪)

  • 用乘法和查找计数右边连续的零位(后跟)

通过浮法舍入到2的下一个最高幂

向上舍入到2的下一个最高幂

交织位(也称为计算莫顿数)

  • 交错位的明显方式

  • 通过表查找交织位

  • 带64位乘法的交织位

  • 通过二进制幻数交错位

测试单词中的字节范围(并计算出现的次数)

  • 确定单词是否为零字节

  • 确定一个单词的字节数是否等于n

  • 确定一个单词的字节数是否小于n

  • 确定单词的字节数是否大于n

  • 确定单词是否在m和n之间有一个字节

按词典顺序计算下一位排列

http://graphics.stanford.edu/~seander/bithacks.html

s中控制小数点的显示位数

< script language = " JScript " >

Number.prototype.toFixed = function(num)

{

// 重新构造toFixed方法,IE5.0+

with(Math) this .NO = round( this .valueOf() * pow( 10 ,num)) / pow( 10 ,num);

return String( / /. / g.exec( this .NO) ? this .NO: this .NO + " . " + String(Math.pow( 10 ,num)).substr( 1,num));

}

alert(( 12.9299 ).toFixed( 2 ));

alert(( 12.9999 ).toFixed( 2 ));

</ script >

控制保留几位有效小数的js函数

//Code CreateBy abandonship 2007.03.12

function FormatAfterDotNumber( ValueString, nAfterDotNum )

{

var ValueString,nAfterDotNum ;

var resultStr,nTen;

ValueString = ""+ValueString+"";

strLen = ValueString.length;

dotPos = ValueString.indexOf(".",0);

if (dotPos == -1)

{

resultStr = ValueString+".";

for (i=0;i<nAfterDotNum ;i++)

{

resultStr = resultStr+"0";

}

return resultStr;

}

else

{

if ((strLen - dotPos - 1) >= nAfterDotNum ){

nAfter = dotPos + nAfterDotNum + 1;

nTen =1;

for(j=0;j<nAfterDotNum ;j++){

nTen = nTen*10;

}

resultStr = Math.round(parseFloat(ValueString)*nTen)/nTen;

return resultStr;

}

else{

resultStr = ValueString;

for (i=0;i<(nAfterDotNum - strLen + dotPos + 1);i++){

resultStr = resultStr+"0";

}

return resultStr;

}

}

}

应用时只需要FormatAfterDotNumber( ’数字字符串’, 保留小数位数 );

for example:

<html>

<head>

<SCRIPT LANGUAGE="JAVAscript">

<!--调用上面的函数 -->

</script>

</head>

<body>

<input type="text" name="strTemp">

<input type="button" onclick="alert( FormatAfterDotNumber( document.all. strTemp.value), 保留小数位)" >

</body>

</html>

一个替代js自带的toFixed函数的方法和优点

之前一直在用这个js自带的toFixed函数来进行四舍五入的操作,可是,在实际使用过程中却遇到了问题。

比如

Javascript代码

  1. var money=0.00542;//0.006;

  2. alert(Number(money).toFixed(2));

  3. //0.00

可以看出上面的结果是错误的,下面的方法通过巧妙的使用Math.round函数,完全可以解决数值很小时的精度问题。

Javascript代码

  1. var money=0.00542;//0.006;

  2. alert(Number(money).toFixed(2));

  3. function round2(number,fractionDigits){

  4. with(Math){

  5. return round(number*pow(10,fractionDigits))/pow(10,fractionDigits);

  6. }

  7. }

  8. alert(round2(money,2));//0.01

round 方法

返回与给出的数值表达式最接近的整数。

Math.round(number)

必选项 number 参数是要舍入到最接近整数的值。

说明

如果 number 的小数部分大于等于 0.5,返回值是大于 number 的最小整数。否则,round 返回小于等于 number 的最大整数。

js保留小数位数的某人总结

一、问题的产生:

自己在编码时,在javascript中遇到了3.21*3=9.629999999999999的现象

二、百度一下

得到如下信息:

用Javascript取float型小数点后两位,例22.127456取成22.13,如何做?

1. 最笨的办法....... [我就怎么干的.........]

function get()

{

var s = 22.127456 + "";

var str = s.substring(0,s.indexOf(".") + 3);

alert(str);

}

2. 正则表达式效果不错

<script type="text/javascript">

onload = function(){

var a = "23.456322";

var aNew;

var re = /([0-9]+/.[0-9]{2})[0-9]*/;

aNew = a.replace(re,"");

alert(aNew);

}

</script>

3. 他就比较聪明了.....

<script>

var num=22.127456;

alert( Math.round(num*100)/100);

</script>

4.会用新鲜东西的朋友....... 但是需要 IE5.5+才支持。

<script>

var num=22.127456;

alert( num.toFixed(2));

</script>

三、总结后,自己写了个javascript多位数四舍五入的通用方法

//num表示要四舍五入的数,v表示要保留的小数位数。

function decimal(num,v)

{

var vv = Math.pow(10,v);

return Math.round(num*vv)/vv;

}

Math对象

属性

LN10 (10的自然对数)

PI (3.1415926...)

SQRT1_2 (1/2的平方根)

方法

abs(x) 返回x的绝对值

acos(x) 返回x的arc cosine值

asin(x) 返回x的arc sin值

atan(x) 返回x的arc tangent值

ceil(x) 返回大于等于x的最小整数

cos(x) 返回x的cosine值

exp(x) 返回e的x次方

floor(x) 返回小于等于x的最大整数

log(x) 返回x的

max(x,y) 返回x,y中的大值

min(x,y) 返回x,y中的小值

pow(x,y) 返回x的y次方

round(x) 舍入到最近整数,(小于或等于0.5小数舍去)

sin(x) 返回x的sin值

sqrt(x) 返回x的平方根

tan(x) 返回x的tangent值

js浮点数相加、减、乘、除精确计算

说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。

//调用:accAdd(arg1,arg2)

//返回值:arg1加上arg2的精确结果

function accAdd(arg1,arg2){

var r1,r2,m;

try{r1=arg1.toString().split(".")[1].length}catch(e){r1=0}

try{r2=arg2.toString().split(".")[1].length}catch(e){r2=0}

m=Math.pow(10,Math.max(r1,r2))

return (arg1*m+arg2*m)/m

}

//说明:javascript的减法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。

//调用:accSub(arg1,arg2)

//返回值:arg1减上arg2的精确结果

function accSub(arg1,arg2){

return accAdd(arg1,-arg2);

}

//说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。

//调用:accMul(arg1,arg2)

//返回值:arg1乘以arg2的精确结果

function accMul(arg1,arg2)

{

var m=0,s1=arg1.toString(),s2=arg2.toString();

try{m+=s1.split(".")[1].length}catch(e){}

try{m+=s2.split(".")[1].length}catch(e){}

return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)

}

//说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。

//调用:accDiv(arg1,arg2)

//返回值:arg1除以arg2的精确结果

function accDiv(arg1,arg2){

var t1=0,t2=0,r1,r2;

try{t1=arg1.toString().split(".")[1].length}catch(e){}

try{t2=arg2.toString().split(".")[1].length}catch(e){}

with(Math){

r1=Number(arg1.toString().replace(".",""))

r2=Number(arg2.toString().replace(".",""))

return (r1/r2)*pow(10,t2-t1);

}

}

欢迎关注公众号(hongji8410)和加入QQ群一起交流(522342554)

系列教程是scikit-learn学习的第一篇,我会根据scikit-learn官网教程和自己的学习经验来进行总结。首先,我会介绍模型的一些基本知识,不会花时间去推导公式,会介绍一下这个模型的适应情况,以及用scikit-learn这个框架来实现这个模型。

一、最小二乘法

还记得高中物理实验课的时候,做实验的时候我们会首先记录一系列的数据,然后根据这些自变量和因变量之间的关系(线性关系)画出直线,而在画直线的时候所采用的方法就是最小二乘法。最小二乘法也叫最小平方法是一种数学的优化算法,它是通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数据,并使得求得的数据与实际数据之间误差的平方和最小。如果,自变量(x)和因变量(y)是线性组合的话,那么它们一定满足下面的公式

线性模型

其中,w表示系数,可以用一个向量表示为W=(W1,W2......,Wp),在scikit-learn中,用coef_表示。其中W0表示截距,在scikit-learn中用intercept_表示。数据集的实际观测数据和预测数据(估计值)之间的残差平方和最小,公式如下:

残差平方和

其中,Xw表示预测值,y表示实际数据。下面,引用一个维基百科的例子来介绍一下使用最小二乘法的求解过程

最小二乘法例子

通过上面的例子我们可以发现,我们是通过求解最小化误差来求解参数的。然而,我们在实际编程的时候并不会采用这种方式去求解参数,通常都是采用梯度下降算法迭代来使得预测值与实际值之间误差的平方和最小。在使用最小二乘法求解参数的时候有几个地方需要注意:

1、方程组的个数要多于未知数的个数。

2、模型与参数是线性的,参数只能以一次方出现。

3、对数据的噪声干扰之间是零均值同方差的,就是指在测量数据的时候要保证测量误差的平均值为0,而且每一个测量误差之间是不相关的,测量误差与X是不相关的。

二、scikit-learn实现线性模型

1、求解维基百科例子中的参数

from sklearn import linear_model
import matplotlib.pyplot as plt
import numpy as np
if __name__ == "__main__":
 #定义自变量x
 x = np.array([[1],[2],[3],[4]],dtype=np.float32)
 #定义因变量y
 y = np.array([6,5,7,10],dtype=np.float32)
 #加载scikit-learn的线性模型
 linear = linear_model.LinearRegression()
 #通过x和y来建立线性模型
 linear.fit(x,y)
 #查看模型系数β2
 print(linear.coef_) #[ 1.39999998]
 #查看模型的截距β1
 print(linear.intercept_) #3.5000000596

2、scikit-learn官网的例子

http://sklearn.lzjqsdd.com/auto_examples/linear_model/plot_ols.html#example-linear-model-plot-ols-py

score(X, y, sample_weight=None)返回预测的决定系数R^2

R^2 = 1 - u/v,u是一个残差的和squares ((y_true - y_pred) ** 2).sum() ,v是一个平方和squares ((y_true - y_true.mean()) ** 2).sum(),最好的结果是1,它也能为负数,因为这个模型有可能被任意的损坏,一个常量的模型总是能预测一个期望值,在不考虑特征输入的情况,R^2=0。可以通过R^2来判断,预测的值与实际值的均值之间的关系。