惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Hacker News
The Hacker News
F
Full Disclosure
Cloudbric
Cloudbric
Blog — PlanetScale
Blog — PlanetScale
W
WeLiveSecurity
N
News and Events Feed by Topic
T
Troy Hunt's Blog
V2EX - 技术
V2EX - 技术
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
B
Blog
GbyAI
GbyAI
C
Check Point Blog
B
Blog RSS Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Recorded Future
Recorded Future
The Last Watchdog
The Last Watchdog
N
News and Events Feed by Topic
T
The Blog of Author Tim Ferriss
O
OpenAI News
V
V2EX
人人都是产品经理
人人都是产品经理
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
WordPress大学
WordPress大学
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Security @ Cisco Blogs
C
Cisco Blogs
Security Latest
Security Latest
S
Security Affairs
V
Visual Studio Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Azure Blog
Microsoft Azure Blog
Last Week in AI
Last Week in AI
AWS News Blog
AWS News Blog
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
PCI Perspectives
PCI Perspectives
博客园_首页
U
Unit 42
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
L
LINUX DO - 热门话题
H
Hacker News: Front Page

Parallel Labs

Architect和Artisan - Parallel Labs 创业与企业家精神 - Parallel Labs 采访Hadoop创始人Doug Cutting纪要 - Parallel Labs 智能优化&AB测试-实验驱动用户增长@QCon10 PPT分享 - Parallel Labs Druid 6th Meetup资料下载 - Parallel Labs 增长二三事 - Parallel Labs 两个平行世界 - Parallel Labs Shape the world to come - Parallel Labs 2018新年目标 - Parallel Labs 人工智能芯片公司招聘工程师/行政/出纳 - Parallel Labs Druid中国用户组第一次线下技术交流资料分享 - Parallel Labs 再见了,IBM中国研究院 | Parallel Labs 怎样做颠覆式创新? - Parallel Labs 基于OpenStack, Docker和Spark打造SuperVessel大数据公有云 - Parallel Labs 给Vim配置Scala语法高亮显示 - Parallel Labs 一步一步教你怎样给Apache Spark贡献代码 - Parallel Labs 大数据的价值密度 - Parallel Labs IBM研究院(CRL)诚聘 Bigdata/Clould 方向正式员工 - Parallel Labs My Way - Parallel Labs Impala:新一代开源大数据分析引擎 - Parallel Labs Impala与Stinger对比 - Parallel Labs Git快速学习指南 - Parallel Labs 与Google拼音的工程师聊聊中文滑行输入 - Parallel Labs 仰望星空 脚踏实地 - Parallel Labs 记一次诡异的Debug经历 - Parallel Labs 下一代大数据分析技术 - Parallel Labs 多核与异步并行 - Parallel Labs 做好失败的准备 - Parallel Labs Facebook技术分享: Social Networking at Scale - Parallel Labs 为什么NoSQL和Hadoop该一起使用? - Parallel Labs Understanding System and Architecture for Big Data - Parallel Labs C++ AMP异构并行编程解析 - Parallel Labs Intel Nehalem微处理器架构 by Glenn Hinton (Intel Fellow) - Parallel Labs 云计算时代的多核开发 - Parallel Labs X-RIME: 基于Hadoop的开源大规模社交网络分析工具 - Parallel Labs 并行编程中的“锁”难题 - Parallel Labs [已经招到了,谢谢大家!]IBM中国研究院招聘Hadoop实习生 - Parallel Labs IBM中国研究院招聘大规模数据分析实习生 - Parallel Labs 浅析C++多线程内存模型 - Parallel Labs Facebook的Realtime Hadoop及其应用 - Parallel Labs 《程序员的自我修养》中关于加锁不能保证线程安全的一个错误 - Parallel Labs 你好,2011! - Parallel Labs 移动设备进入多核时代! - Parallel Labs 为什么在多核多线程程序中要慎用volatile关键字? | Parallel Labs Jeff Dean关于Google系统架构的讲座 - Parallel Labs Erlang User Conference 2010见闻(兼谈程序员职业生涯) - Parallel Labs 多线程程序常见Bug剖析(下) - Parallel Labs 多线程程序常见Bug剖析(上) - Parallel Labs 史蒂夫乔布斯(Steve Jobs)在Stanford2005年毕业典礼上的演讲 - Parallel Labs 多线程队列的算法优化 - Parallel Labs Google创始人的求职目标 - Parallel Labs 多核的未来 - Parallel Labs 多核编程的难题(二) - Parallel Labs 多核编程的难题(一) - Parallel Labs 聊一聊瑞典的程序员 - Parallel Labs 多线程程序中操作的原子性 - Parallel Labs 第三次软件危机 - Parallel Labs 实施并行编程的五大障碍 - Parallel Labs 为什么程序员需要关心顺序一致性(Sequential Consistency)而不是Cache一致性(Cache Coherence?) | Parallel Labs 八条设计多线程程序的简单规则 - Parallel Labs 瑞典Ericsson总部Master Thesis面试回忆录 | Parallel Labs Pthreads并行编程: 线程同步之spin lock与mutex性能比较 | Parallel Labs How to do performance analysis on your parallelized program efficiently? - Parallel Labs 09年感悟 - Parallel Labs Proposal for the “Search and sort” competition of Findwise - Parallel Labs 在瑞典打甲流疫苗 - Parallel Labs The Longest Plateau | Parallel Labs Launched my master thesis finally - Parallel Labs Hello world! - Parallel Labs
二进制的二三事 - Parallel Labs
Guancheng (G.C.) · 2010-05-15 · via Parallel Labs

二进制是计算机的自然语言,逻辑门中神奇的0/1组合犹如那起起伏伏的“滴答”之声构成了曼妙的电子世界。不仅如此,二进制中的0和1往往也是我们解决实际问题的利器。

Task1:求一个固定长度集合所有子集

最直观的方法就是穷举:对集合中的每个元素来说它要么在当前子集中,要么不在当前子集中,以此依次类推穷举出所有可能的值。如果我们用0表示该元素在当前子集中,用1表示该元素不在当前子集中,我们就可以用一串0/1序列来表示当前的子集。

例如集合{a, b, c, d, e}中的子集{a, b, c}可以用“11100”来表示,子集{c, d, e}可以用“00111”来表示,空子集{}可以用“00000”来表示。

那么对任意固定长度的集合a[n],我们可以从a[0]开始穷举a[0]在子集中和a[0]不在子集中两种情况,再依次类推从a[1]开始一直穷举到a[n-1]为止。于是我们可以得到一个很直观的递归程序:

void sub_sets_recu(int index, int length, char *array, char *bits)
{
	int j = 0;

	if (index >= length) {
		for (j = 0; j < length; j++) {
			if (bits[j] == '1') {
				printf("%c", array[j]);
			}
		}
		printf("\n");
	}
	else {
		bits[index] = '1';
		sub_sets_recu(index+1, length, array, bits);
		bits[index] = '0';
		sub_sets_recu(index+1, length, array, bits);
	}
}

但是递归版本的缺点就是需要一个额外的数组bits来存储0/1序列,那么有没有办法改进呢?我们可以先试试把递归改成迭代的方式会不会有效果。

迭代的关键就在于明白你究竟要做哪些计算步骤,为此我们可以先画出递归函数的Execution Tree来看看:

—                       ○
|                     0/  \1
|                     ○    ○
|                  0/  \1   .
|                  ○    ○     .
n层             .       .      .
|                .         .
|               .           .
|             /  \        /  \
|           ○   ○     ○    ○

上图中,第i层的值为1的边来表示a[i]在子集中,值为0的边表示a[i]不在子集中,那么从根节点到所有叶子节点所经过的边的组合就是所有可能的子集组合,例如从根节点到最左叶子节点的组合为“00000”,即空子集。根据完全二叉树的性质,第n层有2^n个节点,所以共有2^n个不同的子集。既然所有的解都已经存储在这颗二叉树中,那么我们自然就可以依此构建一个遍历所有路径的迭代算法。

思路是有了,但是实际编码中我们需要用到一个跟二进制编码相关的trick了。我们知道任意一个十进制的数是可以用二进制来表示的,反过来我们也可以用十进制来表示二进制啊,“用二进制来思考”其实就是这个trick的关键。既然我们的解是“00000”~“11111”,那么它们自然可以用十进制的0~31来表示。很自然的我们就可以想到如果我们依次遍历0~31这些数,根据它们二进制值中每一位的值是0还是1即可确定当前元素是否在子集中。关于二进制的位操作等相关知识Matrix67的blog[1]有一个系列写的很不错,在此就不再赘述。

void sub_sets_iter(int length, char *array)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < (1<<length); i++) {
		for (j = 0; j < length; j++) {
			if ((i & (1<<j)) != 0) {
				printf("%c", array[j]);
			}
		}
		printf("\n");
	}
}

再加上测试用的main函数:

int main()
{
	char a[5] = {'a','b','c','d','e'};
	char b[5];
	int i;

#ifdef USE_RECU
	for (i = 0; i < 10000000; i++) {
		sub_sets_recu(0, 5, a, b);
	}
#else
	for (i = 0; i < 10000000; i++) {
		sub_sets_iter(5, a);
	}
#endif
	return 0;
}

好,代码全写完了,当然应该比试比试两个算法孰优孰劣。测试平台是:cygwin + gcc 3.4.4 + Core2 Duo T7300@2.0GHz

在测试时已经把两个算法中的printf给注释掉了,只比拼速度。先把gcc的优化设成-O0看看:

$ time ./recu.exe

real    0m8.847s
user    0m8.811s
sys     0m0.046s

$ time ./iter.exe

real    0m5.093s
user    0m4.999s
sys     0m0.046s

再看看gcc -O3的结果:

$ time ./recu_o3.exe

real    0m8.285s
user    0m8.234s
sys     0m0.031s

$ time ./iter_o3.exe

real    0m1.887s
user    0m1.796s
sys     0m0.031s

结果显示迭代算法优于递归算法。感兴趣的朋友可以反汇编一下编译器优化后的代码,看看为什么迭代算法优化后能快那么多。关于递归与迭代已经有很多分析了,简单说来递归更加直观易懂,但是栈的开销比较大;迭代的算法不容易一开始就想出来,但是一般来说效率更高。从编译器的角度来讲,部分的编译器能把相对简单的递归算法转化成迭代算法,因为递归的算法对编译器来说是更友好的算法,更易于在此基础上做更多的优化。从体系结构的角度来讲,递归算法更易于发挥CPU流水线的特点让多步运算同时执行,而某些特定的架构,例如GPU就没有对递归提供有效的硬件支持,因为栈空间是一个大问题。

Task2:小白鼠试毒药问题

实验室里有1000个一模一样的瓶子,但是其中的一瓶有毒。可以用实验室的小白鼠来测试哪一瓶是毒药。如果小白鼠喝掉毒药的话,会在一个星期的时候死去,其他瓶子里的药水没有任何副作用。请问最少用多少只小白鼠可以在一个星期以内查出哪瓶是毒药?

这个问题关键是跳出思维,并不是说瓶子和小白鼠必须一一对应,然后我们用1000只小白鼠,每只各试一瓶药水,等一个礼拜然后出结果。其实我们仔细想想,小白鼠试毒只有两种结果,非“死”即“生”,又是一个0/1状态。很显然我们可以再次构建一个0/1组成的二叉树,由此即可表示不同的试毒结果。假设我们需要n个小白鼠,那么2^n-1应大于等于1000(因为有1000种可能的结果,减一是指要除去小白鼠全部不死的情况 — 多谢danqi指正),那么最小的n就是10了。

总结

解决此类问题的关键在于找到能用0/1表示的状态,然后想办法用0/1组成的二进制数来表示不同的结果,从而达到节省存储空间,提高解题效率的目的。

二进制真是神奇,文章的最后附一张0和1构成的Fibonacci数列(来自[2]):

相关文献

[1] 位运算讲解系列文章
[2] Fibonacci数列转二进制图形的惊异发现

转载请注明来自www.parallellabs.com