counting 1 bits C implementations

五子棋 AI(一) 阅读详情

Here are C implementations of all the methods for counting 1 bits mentioned in that node. (Go read that first, if you haven't already.) All of the statistical information is purely anecdotal, but for what it's worth, it's based on my testing the code on a Pentium 3 and a Celeron 2, using the clcompiler of Microsoft Visual C++, and on a Sun Ultra 5, using gcc and Sun's own cc. For testing 64-bit code, I used __int64 on the Intel machines, and long long on the Sparc. It's worth noting that while Sun's compiler outputs faster executables than gcc, it doesn't change the relative performance of the different methods.

Table Lookup

Use a pre-built  lookup table of all the 1-bit counts for every possibly  byte, then index into that for each byte that comprises the word. This is the fastest method (slightly) for 32 bits on both Intel and Sparc, and (even more slightly) the fastest for 64 bits on Sparc, falling to second fastest on 64 bits on Intel. Changing the lookup table from anything but  unsigned or  int makes it a little slower (what with the extra casting and byte-loading the compiler is forced to add.)
unsigned numbits_lookup_table[256] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2,
    3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
    3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
    4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,
    3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
    6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4,
    4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
    6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,
    3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3,
    4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6,
    6, 7, 6, 7, 7, 8
};

unsigned numbits_lookup(unsigned i)
{
    unsigned n;
    
    n = numbits_lookup_table[i & 0xff];
    n += numbits_lookup_table[i>>8  & 0xff];
    n += numbits_lookup_table[i>>16 & 0xff];
    n += numbits_lookup_table[i>>24 & 0xff];
    
    return n;
}

Counters

If you want a full explanation of how this works, read my writeup at  counting 1 bits, but suffice it to say that you are essentially  partitioning the word into groups, and combining the groups by adding them together in pairs until you are left with only one group, which is the answer. (performance notes in the next section.)
unsigned numbits(unsigned int i)
{

    unsigned int const MASK1  = 0x55555555;
    unsigned int const MASK2  = 0x33333333;
    unsigned int const MASK4  = 0x0f0f0f0f;
    unsigned int const MASK8  = 0x00ff00ff;
    unsigned int const MASK16 = 0x0000ffff;
    
    i = (i&MASK1 ) + (i>>1 &MASK1 );
    i = (i&MASK2 ) + (i>>2 &MASK2 );
    i = (i&MASK4 ) + (i>>4 &MASK4 );
    i = (i&MASK8 ) + (i>>8 &MASK8 );
    i = (i&MASK16) + (i>>16&MASK16);
    
    return i;
}

Optimized Counters

call pointed out in  counting 1 bits that you could optimize the Counters method further if you pay attention to which bits you care about and which you don't, which allows you to skip applying some of the masks.

Some symbols that I'll use to represent what's going on:

  • 0: bits we know are zero from the previous step
  • o: bits we know are zero due to masking
  • -: bits we know are zero due to shifting
  • X: bits that might be 1 and we care about their values
  • x: bits that might be 1 but we don't care about their values

So a 0 plus a 0 is still a 0, obviously; the tricky ones are the others, but they're not even so bad. 0 plus X is X, since if the X is a 0 or a 1, added to 0 it will pass through unchanged. However, X plus X is XX, because the sum can range from 0 (0+0), to 10 (1+1). The same holds true with xs, once those show up.

Step 1:

        oXoXoXoXoXoXoXoXoXoXoXoXoXoXoXoX
+       -XoXoXoXoXoXoXoXoXoXoXoXoXoXoXoX
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Step 2:
        ooXXooXXooXXooXXooXXooXXooXXooXX
+       --XXooXXooXXooXXooXXooXXooXXooXX
        0XXX0XXX0XXX0XXX0XXX0XXX0XXX0XXX
Step 3:
        oooo0XXXoooo0XXXoooo0XXXoooo0XXX
+       ----0XXXoooo0XXXoooo0XXXoooo0XXX
        0000XXXX0000XXXX0000XXXX0000XXXX
Step 4:
        oooooooo0000XXXXoooooooo0000XXXX
+       --------0000XXXXoooooooo0000XXXX
        00000000000XXXXX00000000000XXXXX
Step 5:
        oooooooooooooooo00000000000XXXXX
+       ----------------00000000000XXXXX
        00000000000000000000000000XXXXXX
You'll notice that the higher the step, the more known zeros ( 0) there are.  call's suggestion was to change step 5 to this:

Step 5:
        ooooooooooooxxxx00000000000XXXXX
+       ----------------00000000000XXXXX
        000000000000xxxx0000000000XXXXXX
(mask)  ooooooooooooooooooooooooooXXXXXX
(where " (mask)" means "after adding, apply a mask".)

However, you can go back even further and apply the same technique - all the way to step 3, in fact. The best I can think to optimize this changes the last three steps into the following: Step 3:

        0xxx0XXX0xxx0XXX0xxx0XXX0xxx0XXX
+       ----0XXX0xxx0XXX0xxx0XXX0xxx0XXX
        0xxxXXXX0xxxXXXX0xxxXXXX0xxxXXXX
(mask)  0000XXXX0000XXXX0000XXXX0000XXXX
Step 4:
        0000xxxx0000XXXX0000xxxx0000XXXX
+       --------0000XXXX0000xxxx0000XXXX
        0000xxxx000XXXXX000xxxxx000XXXXX
Step 5:
        0000xxxx000xxxxx000xxxxx000XXXXX
+       ----------------000xxxxx000XXXXX
        0000xxxx000xxxxx00xxxxxx00XXXXXX
(mask)  ooooooooooooooooooooooooooXXXXXX
Anyway, that's all very lovely, but here's the C to do it:
unsigned numbits(unsigned int i)
{

    unsigned int const MASK1  = 0x55555555;
    unsigned int const MASK2  = 0x33333333;
    unsigned int const MASK4  = 0x0f0f0f0f;

    unsigned int const MASK6 = 0x0000003f;

    unsigned int const w = (v & MASK1) + ((v >> 1) & MASK1);
    unsigned int const x = (w & MASK2) + ((w >> 2) & MASK2);
    unsigned int const y = (x + (x >> 4) & MASK4);
    unsigned int const z = (y + (y >> 8));
    unsigned int const c = (z + (z >> 16)) & MASK6;

    return c;
}
The performance on this method is marginally worse than the lookup method in the 32 bit cases, slightly better than lookup on 64 bit Intel, and right about the same on 64 bit Sparc. Of note is the fact that loading one of these  bitmasks into a  register actually takes two instructions on  RISCmachines, and a longer-than-32-bit instruction on the Intel, because it's impossible to pack an instruction  and 32 bits worth of data into a single 32 bit instruction. See the bottom of  jamesc's writeup at  MIPS for more details on that...

Mind-bending "best" method (even more optimized counters)

A slightly-modified version of the code on this page: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel, which in turn stole the code from the "Software Optimization Guide for AMD Athlon TM 64 and Opteron TM Processors":
unsigned numbits(unsigned int i)
{
    unsigned int const MASK1 = 0x55555555;
    unsigned int const MASK2 = 0x33333333;
    unsigned int const MASK4 = 0x0f0f0f0f;

    unsigned int const w = v - ((v >> 1) & MASK1);
    unsigned int const x = (w & MASK2) + ((w >> 2) & MASK2);
    unsigned int const y = (x + (x >> 4) & MASK4);
    unsigned int const c = (y * 0x01010101) >> 24;

    return c;
}
This method is identical to the "Optimized Counters" method, with two tricks applied:
  1. To get rid of an AND in the first line: instead of adding adjacent bits, it subtracts the high bit by itself of a pair of the bits from the pair together, because the results are the same. 00 - 0 = 001 - 0 = 0110 - 1 = 0111 - 1 = 10
  2. To merge the last two lines into one, it uses a multiply and a shift, which adds the four remaining byte-sized "counters" together in one step.

Subtract 1 and AND

See  counting 1 bits SPOILER for a fuller explanation of this one, but basically the lowest 1-bit gets  zeroed out every iteration, so when you run out of 1s to zero, you've iterated to the number of bits in the word. Clever. Unfortunately, not that terribly fast; it's roughly two to three times slower than the lookup and counters methods on both architectures.
unsigned numbits_subtraction(unsigned i)
{
    unsigned n;

    for(n=0; i; n++)
        i &= i-1;
        
    return n;
}

Straightforwardly Examine Each Bit

The most easily understandable and slowest method: iterate over all the bits in the word; if the current bit is a 1, then increment the counter, otherwise, do nothing. That's actually done here by looking at the least-significant bit on each iteration, then shifting to the right one, and iterating until there are no more 1 bits in the word. There's a little optimization in the  #ifndef here: instead of doing  if (i & 1) n++;, which uses a branch instruction, just add the actual value of the least-significant bit to the counter (  n += (i & 1); ), as it will be a 1 when you want to add 1, and 0 when you don't. (We're just  twiddling bits anyway, so why not?) This actually makes the processor do more adds, but adding is fast, and branching is slow, on modern processors, so it turns out to be about twice as fast. However, even "twice as fast" is still four to five times slower than the lookup method, again, on all architectures.
unsigned numbits(unsigned int i)
{
    unsigned n;

    for(n=0; i; i >>= 1)
#ifndef MORE_OPTIMAL
        if (i & 1) n++;
#else    
        n += (i & 1);
#endif
    
    return n;
}
Now, why does this all matter?  It doesn't, really, but it was sure a good way to waste some time, and maybe someone learned some optimizing tricks from it... (Well, I did, actually - so I hope  someone else did as well.)
2018.08.30 Tyvj1952 Easy(期望dp) Description 某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:( 我们来简化一下这个游戏的规则 有n次点击要做,成功了就是o,失败了就是x,分数是按comb计算的,连续a个comb就有a*a分,comb就是极大的连续o。 比如ooxxxxooooxxx,分数就是2*2+4*4=4+16=20。 Sevenkplus闲的慌就看他打了一盘,有些地方跟... 阅读详情

相关推荐

闲来无事整一个蒙娜丽莎(ASCII Art)

【代码】闲来无事整一个蒙娜丽莎(ASCII Art)

ytk888的博客 2万+

一个很猛的mm被甩之后(zz)

song:有点扯,但挺搞笑:) 故事从一个星期天开始     和男朋友一起有4年的,大学的时候就在一起,刚开始的时候也是浪漫非凡,玩遍了所 有男生女人所谓的小资和情调,大学毕业走上社会,工作开始忙起来了,生活也渐渐 走向平淡      我们几乎已经是快谈婚论嫁的时候了,妈妈给我出钱付了首付,装修也一手 操办,基本上,就在等着选个好日子,把自己嫁了,日子真是美好        男朋友,小撒,

gsong的专栏 5万+

从机械到数字:EC11旋转编码器与ESP32的PWM控制艺术

本文详细解析了EC11旋转编码器与ESP32的PWM控制技术,探讨了如何将机械旋转动作转换为精确的数字信号控制。通过硬件连接、信号处理和软件优化等实践技巧,开发者可以构建高效的人机交互系统,适用于智能照明、电机控制等多种应用场景。

android的专栏 762

B OOXX Game(FZU 2151

B  OOXX Game Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, there are N*M coins in this board with two symbol “O” or “X”. Th

YOONGI 1万+

hdoj1240

Asteroids! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description You're in space. You want to get home. There are asteroids. You don't w

◆◇丶生如夏花。谁来订阅我的悲伤 892

FZU_Problem 2151 OOXX Game

Problem 2151 OOXX Game Accept: 476    Submit: 730 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description Fat brother and Maze are playing a kind of special (hentai) game on

有一种力量叫乐观 1615

一个很猛的MM被甩以后

故事从一个星期天开始 和男朋友一起有4年的,大学的时候就在一起,刚开始的时候也是浪漫非凡,玩遍了所 有男生女人所谓的小资和情调,大学毕业走上社会,工作开始忙起来了,生活也渐渐 走向平淡我们几乎已经是快谈婚论嫁的时候了,妈妈给我出钱付了首付,装修也一手 操办,基本上,就在等着选个好日子,把自己嫁了,日子真是美好 男朋友,小撒,自从一个大学聚会回来以后,就不是很正常,天天发呆,手 机当宝,私事

herring的专栏 8668

CF1450 C1. Errich-Tac-Toe (Easy Version) C2. Errich-Tac-Toe (Hard Version)(构造)

链接 https://codeforces.com/contest/1450/problem/C2 The only difference between the easy and hard versions is that tokens of type O do not appear in the input of the easy version. Errichto gave Monogon the following challenge in order to intimidate him from

萧瑟的博客 5万+

Pond

2072. 【2016.10.6NOIP普及模拟】Pond  (File IO): input:pond.in output:pond.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制   Goto ProblemSet 题目描述        G最后在规定时间内完成了任务,Alice只能放行。         出

cdy1206473601的博客 5万+

Dreamweaver正则表达式批量替换

下面举几个例子,希望朋友们可以举一反三:    1、替换所有的title和alt里的内容。      alt=".*" 替换所有的alt标签里的内容   title=".*" 替换所有的alt标签里的内容     2、清理掉图片,图片代码中间有换行。    内蒙小新  我们用正则 即可去掉图片     3、替换视频下载地址。     视频的地址要稍微有一定的规律,例如

HiNet 4113

Linux下进程间通信的方法--信号量

这里的信号量主要是在不同的进程之间使用。  需要四个操作 P操作V操作以及通过semctl调用command参数设置SETVAL来初始化信号量,在使用信号量之前必须要这么做.并且可以通过semctl调用command设置为IPC_RMID来删除信号量ID: 因为库里没有定义union semun,因此需要自己定义. 用到信号量的三个函数 semget 用来创建一个新信号量

This My Life - chisj 1921

uva 1585 - Ancient Cipher (OX)

给出一个由O和X组成的串(长度为1~80),统计得分。每个O的得分为目前连续出现 的O的个数,X的得分为0。例如,OOXXOXXOOO的得分为1+2+0+0+1+0+0+1+2+3。 Sample Input 5 OOXXOXXOOO OOXXOOXXOO OXOXOXOXOXOXOX OOOOOOOOOO OOOOXOOOOXOOOOX Sa

SuCicada 小米的蝉 9463

WSGI(Web Server Gateway Interface, Web服务器网关接口)

Django框架是应用程序,负责业务逻辑,本身没有服务器程序功能。现有有很多优秀的服务器程序 例如 uwsgi、Gunicorn。而Web应用框架和服务器程序进行配合使用就需要遵循一定的规范(就比如图中的桥梁部分)。而这个规范就是WSGIWSGI(Web Server Gateway Interface, Web服务器网关接口) 就是一种规范, 它定义了使用Python编写的web应用程序与we...

qq_39112101的博客 1506

0010 嘿嘿

----- For instance, if you are older, you have less time to recover from any major losses and you may well wish to boost your pension income. So preserving your capital and generating extra income a

nukuok的专栏 1万+

Codeforces Global Round 12 C2. Errich-Tac-Toe (Hard Version)(思维)

题目描述 The only difference between the easy and hard versions is that tokens of type O do not appear in the input of the easy version. Errichto gave Monogon the following challenge in order to intimidate him from taking his top contributor spot on Codeforce

li_wen_zhuo的博客 12万+

xxxooo

namespace xxoo { class ooxx { } } An h1 header Paragraphs are separated by a blank line. 2nd paragraph. Italic, bold, monospace. Itemized lists look like: this one that one the other one Note th...

weixin_30391339的博客 2万+

FZU 2151 OOXX Game

Description Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, there are N*M coins in this board with two symbol “O” or “X”. The

? 7227

【HDU - 1240】 F - Asteroids! (三维BFS)

You’re in space. You want to get home. There are asteroids. You don’t want to hit them. Input Input to this problem will consist of a (non-empty) series of up to 100 data sets. Each data set will be formatted according to the following description, and the

黑桃 4780

AIGC实战——变分自编码器(Variational Autoencoder, VAE)

变分自编码器通过在模型中引入随机性,并限制潜空间中的点的分布来解决自编码器存在的问题。只需进行一些微小的调整,就可以将自编码器转换为变分自编码器,从而使其成为真正的生成模型。在本节中,我们介绍了变分自编码器的基本原理,并使用 `Keras` 实现了一个变分自编码器用于生成 Fashion-MNIST 图像。

盼小辉丶的博客 1万+

基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)

基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计),该项目是个人毕设项目,答辩评审分达到98分,代码都经过调试测试,确保可以运行!欢迎下载使用,可用于小白学习、进阶。该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早期诊断辅助系统源代码+文档说明(python毕业设计)基于深度学习的阿兹海默症早

上一篇: 经典makefile例子
下一篇: 清空文件中的一行
wisage
博客等级 码龄19年 15粉丝 38原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值