如何使用PHP最高效率的将一个正整数扩大一千倍?
方案制定
如何最高效率的将一个正整数扩大一千倍?
当这个问题被抛给人脑的时候,接受过优秀的九年义务教育的我们稍加思索就能给出一个调皮而又不失大师风范的答案:添三个零就行了~
但是当这个问题交给程序员用代码解决的时候,问题就要从编程的角度去考虑。我作为phper
第一时间想到的方案分别是
方案1:在数字末尾粘连字符串 “000”
方案2:将原数字简单的乘以 1000
当我把这个问题丢给我的朋友的时候,他告诉我:
方案1肯定不行!你应该采用方案2,但是如果你足够鸡贼的话,你应该采用正整数 X 1024 - 正整数 X 24
!
因为计算机是二进制,当你告诉他要乘以1000的时候,他会进行正整数 X 512 + 正整数 X 256 + 正整数 X 128 + 正整数 X 128 + 正整数 X 64 + 正整数 X 32 + 正整数 X 8
,一直累加到凑齐 正整数的 1000 倍 为止。而运算2的10次方
要比那一串加号更快接近结果。
实践出真知
大佬的这段话很快打动了我。为了践行大佬的真知,证明大佬的阐述对我犹如醍醐灌顶般点醒梦中人,我迅速写出了一小段方法,将一个随机正整数扩大1000倍的算法用三种不同的方法分别跑 一千万次,查看各个方法运行的效率(使用框架:laravel)
// 图表内容
$headers = ['次数', '方案1:拼接法', '方案2:乘1000', '方案3:乘以 1024'];
$data = [
[0=>'第一次'],
[0=>'第二次'],
[0=>'第三次']
];
// 每个方法执行三次
for ($count = 0; $count < 3; $count ++) {
// 生成变量名 : plan1start1
$start = Carbon::now()->getPreciseTimestamp();
for ($i = 0; $i < 10000000; $i ++)
{
$integer = rand(1, 999);
$result = (int)($integer . '000');
}
$end = Carbon::now()->getPreciseTimestamp();
$data[$count][] = ($end - $start)/1000000 . '秒';
}
for ($count = 0; $count < 3; $count ++) {
$start = Carbon::now()->getPreciseTimestamp();
for ($i = 0; $i < 10000000; $i ++)
{
$integer = rand(1, 999);
$result = $integer * 1000;
}
$end = Carbon::now()->getPreciseTimestamp();
$data[$count][] = ($end - $start)/1000000 . '秒';
}
for ($count = 0; $count < 3; $count ++) {
$start = Carbon::now()->getPreciseTimestamp();
for ($i = 0; $i < 10000000; $i ++)
{
$integer = rand(1, 999);
$result = $integer * 1024 - $integer * 24;
}
$end = Carbon::now()->getPreciseTimestamp();
$data[$count][] = ($end - $start)/1000000 . '秒';
}
$this->table($headers, $data);
将这段代码运行多次后均得到一个较为稳定的结果:
看到这个结果,大大的疑问重新占领了我小小的脑瓜。乘以 1024 再减去 乘以 24
反而比直接乘以 1000
慢一些。当我拿着这个结果去寻找大佬解决疑惑时,得到了他这样的回答:
平时工作不够多是不是?还有空做这种试验?
对试验结果的思考
暗戳戳鄙夷他一把,顺便把他在我心中的地位从大佬
降格为我的某个不愿透露姓名的普通朋友
。我继续了对这件事请的思考。
在正整数后面拼接字符串 ‘000’,要把这个正整数转换为字符串,然后拼接字符串 ‘000’,之后再转回正整数。计算的复杂程度远超正整数的直接计算,时间上明显劣于后者,这点经过验证也毋庸置疑。
但是我的某个不愿透露姓名的豆豆同学对二进制
计算模式解释的同样头头是道。我学习的是解释型语言 php
,这意味着有一群对代码理解达到骨灰级的玩家在我看不到的地方对php
进行了大量的算法优化。而豆豆同学学习的是编译型语言 C++
。是不是这种解释型语言的不同点,导致了本次运算结果的不同?
所以我计划对这个问题进一步挖掘,将在下一篇博文『用计算证明:我远远低估了编译器』中使用编译型语言 golang
对这个课题再次进行讨论。
高认可度评论:
楼主,给你指出几处不严谨的地方:
这个答案无解啊 平时工作不够多是不是?还有空做这种试验?
事实证明确实工作不够多 :joy:
平时工作不够多是不是?还有空做这种试验?
平时工作不够多是不是?还有空做这种试验?
平时工作不够多是不是?还有空做这种试验?
你的文章风格有点中二,刚工作?
平时工作不够多是不是?还有空做这种试验?
我觉得挺好啊 也许就是因为刚开始工作才能有这么强的好奇心 希望能一直坚持下去
平时工作不够多是不是?还有空做这种试验?
哎你们够了
~单位修改成 千 ;1, 1 千
平时工作不够多是不是?还有空做这种试验?
楼主,给你指出几处不严谨的地方:
平时工作不够多是不是?还有空做这种试验?
@忆往昔弹指间 非常感谢你的建议~
纳秒
级统计单位转换为秒
级统计单位。(int)
转换确实非常花费时间。这么做是为了模拟在实际应用中,被扩大一千倍之后的数字要拿去参与后续的运算,所以还要转回 int 。不过不得不承认你说的对,拼接确实不能作为一个方案。rand()
是必要存在的,公平的条件。甚至我都没有考虑到rand()
存在的合理性。我的局限性还是太大。在后来使用Golang
重复本次试验的时候,为了排查耗时的原因,我才不得不把计算复杂程度上升数千倍,来稀释rand()
造成的影响。如果你感兴趣,也邀请你阅读我的后续博客用计算证明:我远远低估了汇编语言
文笔有趣
牛啊 老哥
难道是我电脑配置太差了?我的用时怎么这么久啊?
把随机数放到外面
php位移运算符更加简单,速度理论上应该比较快
直接 $a<<10 应该是最快的吧
@欧皇降临 这是扩大 2 的 10 次方倍
为什么那么多人评论“平时工作不够多是不是?还有空做这种试验?” :joy: