如何使用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);

将这段代码运行多次后均得到一个较为稳定的结果:

如何使用PHP最高效率的将一个正整数扩大一千倍?

看到这个结果,大大的疑问重新占领了我小小的脑瓜。乘以 1024 再减去 乘以 24 反而比直接乘以 1000慢一些。当我拿着这个结果去寻找大佬解决疑惑时,得到了他这样的回答:

平时工作不够多是不是?还有空做这种试验?

对试验结果的思考

暗戳戳鄙夷他一把,顺便把他在我心中的地位从大佬降格为我的某个不愿透露姓名的普通朋友。我继续了对这件事请的思考。

在正整数后面拼接字符串 ‘000’,要把这个正整数转换为字符串,然后拼接字符串 ‘000’,之后再转回正整数。计算的复杂程度远超正整数的直接计算,时间上明显劣于后者,这点经过验证也毋庸置疑。

但是我的某个不愿透露姓名的豆豆同学对二进制计算模式解释的同样头头是道。我学习的是解释型语言 php,这意味着有一群对代码理解达到骨灰级的玩家在我看不到的地方对php进行了大量的算法优化。而豆豆同学学习的是编译型语言 C++。是不是这种解释型语言的不同点,导致了本次运算结果的不同?

所以我计划对这个问题进一步挖掘,将在下一篇博文『用计算证明:我远远低估了编译器』中使用编译型语言 golang对这个课题再次进行讨论。

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 22

楼主,给你指出几处不严谨的地方:

  • 第一点:你这结果,应该执行的是百万次吧?我看你下面统计时间除的又是百万。
  • 第二点:方案一,你使用了(int)进行类型转换,非常花时间,这样做是不公平的,你应该去掉他,PHP是弱类型语言,自己会进行隐式转换,如果你非要强调类型,那么拼接行为本身就不能作为一个方案,不是么?
  • 第三点:你在大量循环中使用 rand() 函数,该函数调用的总时间,还是比较多的,这会影响你的统计结果。我知道,你潜意识里认为几个方案都使用了该函数,处于同等条件,但其实这是一个错误的认知,我不知道你明不明白一个,加权时间的概念,每一种时间的“价值”是不同的。我建议你去掉他,让该测试更纯粹。
3年前 评论

这个答案无解啊 平时工作不够多是不是?还有空做这种试验?

3年前 评论

事实证明确实工作不够多 :joy:

3年前 评论
Epona

平时工作不够多是不是?还有空做这种试验?

3年前 评论

平时工作不够多是不是?还有空做这种试验?

3年前 评论

平时工作不够多是不是?还有空做这种试验?

3年前 评论

你的文章风格有点中二,刚工作?

3年前 评论
罐装仙人掌CuratorC (楼主) 3年前

平时工作不够多是不是?还有空做这种试验?

3年前 评论

我觉得挺好啊 也许就是因为刚开始工作才能有这么强的好奇心 希望能一直坚持下去

3年前 评论
罐装仙人掌CuratorC (楼主) 3年前

平时工作不够多是不是?还有空做这种试验?

3年前 评论

哎你们够了~

3年前 评论

单位修改成 千 ;1, 1 千

3年前 评论
罐装仙人掌CuratorC (楼主) 3年前

平时工作不够多是不是?还有空做这种试验?

3年前 评论

楼主,给你指出几处不严谨的地方:

  • 第一点:你这结果,应该执行的是百万次吧?我看你下面统计时间除的又是百万。
  • 第二点:方案一,你使用了(int)进行类型转换,非常花时间,这样做是不公平的,你应该去掉他,PHP是弱类型语言,自己会进行隐式转换,如果你非要强调类型,那么拼接行为本身就不能作为一个方案,不是么?
  • 第三点:你在大量循环中使用 rand() 函数,该函数调用的总时间,还是比较多的,这会影响你的统计结果。我知道,你潜意识里认为几个方案都使用了该函数,处于同等条件,但其实这是一个错误的认知,我不知道你明不明白一个,加权时间的概念,每一种时间的“价值”是不同的。我建议你去掉他,让该测试更纯粹。
3年前 评论

平时工作不够多是不是?还有空做这种试验?

3年前 评论

@忆往昔弹指间 非常感谢你的建议~

  • 第一点:这个测试中确实执行了千万次。统计表中给出的时间也是执行一千万次总共消耗的时间。至于最后除以了一个百万,单纯是为了把纳秒级统计单位转换为级统计单位。
  • 第二点:(int) 转换确实非常花费时间。这么做是为了模拟在实际应用中,被扩大一千倍之后的数字要拿去参与后续的运算,所以还要转回 int 。不过不得不承认你说的对,拼接确实不能作为一个方案。
  • 第三点: 潜意识里确实一直认为 rand() 是必要存在的,公平的条件。甚至我都没有考虑到 rand() 存在的合理性。我的局限性还是太大。在后来使用 Golang重复本次试验的时候,为了排查耗时的原因,我才不得不把计算复杂程度上升数千倍,来稀释 rand() 造成的影响。如果你感兴趣,也邀请你阅读我的后续博客
    用计算证明:我远远低估了汇编语言
3年前 评论
sreio

文笔有趣

3年前 评论

难道是我电脑配置太差了?我的用时怎么这么久啊?

file

把随机数放到外面

file

3年前 评论
罐装仙人掌CuratorC (楼主) 3年前

php位移运算符更加简单,速度理论上应该比较快

3年前 评论
罐装仙人掌CuratorC (楼主) 3年前

直接 $a<<10 应该是最快的吧

3年前 评论
罐装仙人掌CuratorC (楼主) 2年前
欧皇降临 (作者) 2年前
Complicated

@欧皇降临 这是扩大 2 的 10 次方倍

3年前 评论
罐装仙人掌CuratorC (楼主) 2年前

为什么那么多人评论“平时工作不够多是不是?还有空做这种试验?” :joy:

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!