前言

看到 N O I \rm NOI NOI 系列支持 builtin \text{builtin} builtin 函数,喜出望外,赶紧去学了一下。拾来的别人的牙慧。

免责声明:我是看别人说的可以用,一切后果由使用者自行承担。

注意 builtin \text{builtin} builtin 函数的参数都是 u n s i g n e d i n t \tt unsigned\;int unsignedint 类型。函数名的末尾加上 l l \rm ll ll 则变为 u n s i g n e d l o n g l o n g \tt unsigned\;long\;long unsignedlonglong 类型。

最低二进制位

原意应该是 f i n d f i r s t s e t \rm find\; first\; set findfirstset,找到第一个被 s e t \rm set set 为 1 1 1 的二进制位。

__builtin_ffs,若参数为 n n n,返回值为 x x x,表示 2 x − 1 2^{x-1} 2x−1 是 n n n 的最低二进制位。

为了某种统一性, n = 0 n=0 n=0 时的返回值为 x = 0 x=0 x=0 。

性能测试

测试环境为 学校机房的电脑 W i n d o w s 10 \rm Windows10 Windows10 系统 64 64 64 位机,配置 Inter(R) Core(TM) i5-9500T \text{Inter(R) Core(TM) i5-9500T} Inter(R) Core(TM) i5-9500T CPU @ 2.20GHz 2.21 GHz \text{CPU @ 2.20GHz 2.21 GHz} CPU @ 2.20GHz 2.21 GHz 。

参赛选手是我们最喜欢的查表法(预处理 2 16 2^{16} 216 以内的所有数的结果)和内置函数。

使用 m t 19937 \rm mt19937 mt19937 随机(两次的种子相同)生成 32 32 32 位无符号整形,进行 1 0 8 10^8 108 次查询,不开 O 2 O2 O2 时,查表法约 2.9 2.9 2.9 秒,内置函数约 2.4 2.4 2.4 秒;开启 O 2 O2 O2 后,即使进行 1 0 9 10^9 109 次查询,运行速度差异不到 0.1 0.1 0.1 秒。可见查表法极快,效率差异的原因或许是函数调用。

而使用 mt19937_64 \text{mt19937\_64} mt19937_64 生成 64 64 64 位无符号整形时,进行 5 × 1 0 8 5\times 10^8 5×108 次查询,开启 O 2 O2 O2 的情况下查表法仍然是以约 6 6 6 秒的成绩略落后于内置函数 5.4 5.4 5.4 秒的成绩。所以有了结论:随机数据下内置函数最快

当然,事实上二者的差距比较小。我的建议是用内置函数,因为方便。

最高二进制位

这个不能直接查。但是我们可以 c o u n t l e a d i n g z e r o \rm count\; leading\; zero countleadingzero,数前导零!

__builtin_clz,返回前导零的数量。注意它是从第 32 32 32 位(毕竟参数是无符号整形)开始数的。

2022/8/22 update \texttt{2022/8/22 update} 2022/8/22 update:参数为 0 0 0 会导致 runtime error \text{runtime error} runtime error,难以置信! sanitizer \text{sanitizer} sanitizer 也会提醒你这一点。

另:一位著名选手也有一种快速求最高二进制位的方法,而且是 O ( 1 ) \mathcal O(1) O(1) 的,无任何内联函数。虽然跑得更慢就是啦。

二进制位计数

这是最常用的。它听上去很简单,却成了自由发挥想象力的舞台!

下面是瞎讲一通。具体只要记住 p o p u l a t i o n c o u n t \rm population\;count populationcount,即 __builtin_popcount 就好了。

查表法

一般自己的程序就这么实现——预处理一个 2 8 2^8 28 或者更长的表,按照每 8 8 8 位直接查表。

事实上还有一种离谱的写法:利用 宏递归展开,如

# define BIT2(n)       n,       n+1,       n+1,       n+2
# define BIT4(n) BIT2(n), BIT2(n+1), BIT2(n+1), BIT2(n+2)
# define BIT6(n) BIT4(n), BIT4(n+1), BIT4(n+1), BIT4(n+2)
# define BIT8(n) BIT6(n), BIT6(n+1), BIT6(n+1), BIT6(n+2)
static const uint8_t table[256] = {BIT8(0)};

每次考虑最高的 2 2 2 个二进制位,剩下的部分递归。

并行计算

从未听说过的技巧,但是极具想象力。考虑到这样一个事实: 2 k > k ( k ∈ N ) 2^k>k\;(k\in\N) 2k>k(k∈N),也就是说,任意 k k k 位二进制数,二进制下 1 1 1 的数量可以直接存储到它原来所占用的二进制位里。

那么,类似 f f t \rm fft fft 去掉递归的方法,我们从最底层开始,逐步合并。最底层是只考虑 1 1 1 个二进制位,那么原本的 b i t \rm bit bit 就是二进制下 1 1 1 的数量。然后我们合并相邻的两个:

n = ( n & 0x55555555 ) + ( (n >> 1) & 0x55555555 );

也就是将两个相邻的块(此时块长为一)的值加在一起。得到的结果不会超出新的块长的二进制位,所以块长翻倍。此时我们继续

n = ( n & 0x33333333 ) + ( ( n >> 2 ) & 0x33333333 );
n = ( n & 0x0F0F0F0F ) + ( ( n >> 4 ) & 0x0F0F0F0F );
n = ( n & 0x00FF00FF ) + ( ( n >> 8 ) & 0x00FF00FF );
n = ( n & 0x0000FFFF ) + ( ( n >> 16 ) & 0x000FFFF );

就能得到最终结果了!真是富有创造力!

一级优化

观察到最后一步等价于 n = ( n m o d 2 16 ) + n 2 16 n=(n\bmod 2^{16})+\frac{n}{2^{16}} n=(nmod216)+216n​,考虑将其转化为 n m o d ( 2 16 − 1 ) n\bmod(2^{16}-1) nmod(216−1) 。显然二者是相等的,因为结果不超过 32 32 32,模 2 16 − 1 = 65535 2^{16}-1=65535 216−1=65535 也没什么问题。

二级优化

仍然运用上面的思考, n m o d ( 2 k − 1 ) n\bmod(2^k-1) nmod(2k−1) 在 2 k − 1 > 32 2^k-1>32 2k−1>32 时等价于 k k k 位为一组的二进制值相加。显然应当取 k = 6 k=6 k=6,如何实现呢?

第一步要解决长度为 3 3 3 的分组。事实上我们可以使用 n − ∑ i = 1 + ∞ ⌊ n 2 i ⌋ n-\sum_{i=1}^{+\infty}\lfloor\frac{n}{2^i}\rfloor n−∑i=1+∞​⌊2in​⌋ 得到任意数的二进制位 1 1 1 的个数,因为 2 k − 2 k − 1 − ⋯ − 2 0 = 1 2^k-2^{k-1}-\cdots-2^0=1 2k−2k−1−⋯−20=1,就恰好会贡献 1 1 1 。

那么第一步无非是

n = n - ( (n >> 1) & 033333333333 ) - ( (n >> 2) & 011111111111 );

由于是 3 3 3 位为一组,采用了更容易理解的八进制数字常量。然后接下来,并行计算与取模,放在一起即可。

return ( ( n + ( n >> 3 ) ) & 030707070707 ) % 63;

分支预测

__builtin_expect(exp,c) 表示表达式 e x p exp exp 的结果更可能是 c ∈ { 0 , 1 } c\in\{0,1\} c∈{0,1},一般常用于 i f \rm if if 语句的判断。例如

if(__builtin_expect(zxy == sister, true))puts("I've already know that it's definitely right!");

则编译器认为,这个 i f \rm if if 语句很可能会成立,那么在汇编中就成了:如果该语句不成立,跳过 i f \rm if if 内的语句块。由于该 i f \rm if if 很可能成立,所以跳转的次数就会很少。习得卡常新技巧!

【学习笔记】builtin函数相关推荐

  1. python函数是一段具有特定功能的语句组_Python学习笔记(五)函数和代码复用

    本文将为您描述Python学习笔记(五)函数和代码复用,具体完成步骤: 函数能提高应用的模块性,和代码的重复利用率.在很多高级语言中,都可以使用函数实现多种功能.在之前的学习中,相信你已经知道Pyth ...

  2. Python学习笔记:函数(Function)

    Python学习笔记:函数(Function) 一.函数基本概念 函数是Python里组织与重用代码最重要的方法.一般来说,如果你期望多次重复相同或相似的代码,写一个可重用的函数可能是值得的.函数通过 ...

  3. php中声明一个函数,php学习笔记之 函数声明

    /* 函数定义: * 1.函数是一个被命名的 * 2.独立的代码段 * 3.函数执行特定任务 * 4.并可以给调用它的程序返回一个值 * * 函数的优点: * 1.提高程序的重用性 * 2.提高程序的 ...

  4. Matlab学习笔记 figure函数

    Matlab学习笔记 figure函数 matlab中的 figure 命令,能够创建一个用来显示图形输出的一个窗口对象.每一个这样的窗口都有一些属性,例如窗口的尺寸.位置,等等.下面一一介绍它们. ...

  5. JAVA学习笔记五---函数

    JAVA学习笔记五---函数 5.1 方法的学习 编写一个程序,求圆的周长和面积. package practice; /*** 编写一个程序,求圆的周长和面积.* @author iszhangyo ...

  6. MySQL学习笔记—自定义函数

    MySQL学习笔记-自定义函数 注释语法: MySQL服务器支持3种注释风格: 从'#'字符从行尾. 从'– '序列到行尾.请注意'– '(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如 ...

  7. matlab机器人工具箱学习笔记——ikine函数

    matlab机器人工具箱学习笔记--ikine函数 ikine函数用法 使用实例 链接: https://blog.csdn.net/weixin_42596724/article/details/8 ...

  8. 《JavaScript语言精粹》学习笔记(函数(2))

    <JavaScript语言精粹>学习笔记(函数(2)) 函数(Functions) 参数(Arguments) 当参数被调用时,会得到一个"免费"的参数数组argume ...

  9. matlab 调用子函数返回值,matlab学习笔记13_1 函数返回值

    一起来学matlab-matlab学习笔记13函数 13_1 函数返回值 觉得有用的话,欢迎一起讨论相互学习~Follow Me 函数返回一个值 返回值不必使用return语句,而是直接将需要返回的变 ...

  10. Python学习笔记12_函数

    Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...

最新文章

  1. Linux查看进程和进程管理
  2. UA MATH567 高维统计II 随机向量5 亚高斯随机向量
  3. 【虚拟化】Dockerfile构建JDK镜像
  4. python3.7打包exe坑_[求助]入坑学习python 需要装pyinstaller打包成exe
  5. 【计算机系统设计】重点 · 学习笔记(1)(资源消耗)
  6. 如何设计带限流功能的5V供电电路?快来学!
  7. [USACO 2008 MAR] 土地购买
  8. 微信小说小程序源码-自带采集带安装教程
  9. 合理的电梯(水题 杭电排位赛-6)
  10. 如何实现windows命令提示符的tab补全
  11. pfx证书转pem、crt、key
  12. Expat XML parser
  13. win7计算机怎么远程桌面连接不上,Win7系统连接不上远程桌面的解决方法
  14. 【论文分享】异构图神经网络域名检测方法GAMD:Attributed Heterogeneous Graph Neural Network for Malicious Domain Detection
  15. 横评美国IaaS“8匹狼”:AWS、GCE、Azure、Rackspace、SoftLayer、Dell、HP、Joyent
  16. windows 快速修复内存不能read
  17. 爱普生(EPSON)喷墨打印机清零工具SSC Service Utility V4.3中文版使用说明(转载)
  18. 数据库隔离级别发展史
  19. sql删除用逗号分隔字段中的某一个值
  20. 基础不扎实,工作中很迷茫。

热门文章

  1. [React Error]: Target container is not a DOM element
  2. 3.1 该如何编写程序界面
  3. 素数的定义法判断(含C++代码)
  4. 同源策略的限制,没有同源策略会怎么样?
  5. 将url地址中的编码转汉字
  6. C语言程序设计教程(第三版)李凤霞 第一章课后习题答案
  7. 35个Python实战项目,完整源代码!
  8. 【Day12-Stream流Map集合】
  9. “美亚杯”第三届中国电子数据取证大赛答案解析(个人赛)
  10. quartz报错:Couldn‘t retrieve trigger: No record found for selection of Trigger with key—————————————