有趣的小实验:四种语言搞定“超超超难”剑桥面试数学题

本文主要是介绍有趣的小实验:四种语言搞定“超超超难”剑桥面试数学题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

概览

如上图所示,这是一道貌似“超超超难”(作者用了 4 个 Super)的数学题,出自剑桥大学的面试环节。

说实话,现在的我已“不可能”通过纸笔计算得出这个问题的解了。

不过,如果剑桥面试官允许我们带电脑入场的话,解决它是分分钟的事(确切的说应该是毫毫秒的事…)。

在本篇博文中,我们将用 4 种语言(x64汇编、C、ruby 和 Swift)来搞定它,并比较它们性能的优劣。

测试主机为我快被淘汰的爱机: MacBookPro(2016年) 2.9 GHz 双核Intel Core i5,8GB内存,测试系统为 macOS Monterey(12.6.6)。

废话少叙,Let‘s workout it!!!😉


审题

题目意思很简单:

  • 如果 a + b + c + d = 63;
  • 求 ab + bc + cd 的最大值;
  • 其中 a、b、c、d 都为自然数;

那么问题来了,零是否属于自然数?

关于这个问题的答案知乎上有一篇文章做了回答:

各方有两个观点:第一,不包括;第二,包括。


我的观点是自然数(Natural numbers)不包括0,因为在国外的学术界,自然数是从数(shu三声)实物开始,或者数(shu三声)数(Counting Numbers{1,2,3,…}),数(shu三声)物体是从1开始数(shu三声)的。所以自然数不包括0.


给正在学习的学生两个建议:


1.如果你一直在国内读书,从小学(小学数学课本有规定自然数包括0)一直读到大学或者研究生或者博士,那么你一定要知道自然数是包括0的,自然数集N(从0开始,中国教科书的规定),N+(N的右下角是“+”)或者N*(表示从1开始的正整数集)。因为会贯穿国内的数学考试,所以记住0是自然数。


2.如果你在国际学校或者国外读书,请记住自然数不包括0,原因如上(我的观点)。如果你中途出国留学,请记住国内和国外的标准就好了。

原文在此:自然数到底是否包括0?

这里,我们采用 零不是自然数的观点 ,在 swift 的解决方案中,我们将会看到如果选择 零是自然数的观点,结果会有怎样的不同。


另外需要注意的是:对于这些语言生成的可执行文件,我们都使用 time 命令多次运行(估算耗时平均值)的方式来确保计时相对准确,但无法保证非常严谨的绝对准确,毕竟只是“趣味实验”而已。


ruby

ruby 的代码很直截了当:

#!/usr/bin/rubymax = 0
r = 1..63
for a in r dofor b in r dofor c in r dofor d in r doif a + b + c + d == 63rlt = a*b + b*c + c*dif rlt >= max max = rltendendendendend
endprint("max is #{max}\n")

将以上代码保存至 test.rb 文件中,再用 chmod 指令赋予它可执行权限:

chmod u+x test.rb

运行可以看到,ruby 平均耗时大约在 1.28秒左右:

% time ./test.rb
max is 991
./test.rb  1.22s user 0.04s system 98% cpu 1.272 total
% time ./test.rb
max is 991
./test.rb  1.23s user 0.04s system 98% cpu 1.286 total
% time ./test.rb
max is 991
./test.rb  1.24s user 0.04s system 98% cpu 1.295 total

我们还可以换一种算法:

r = (1..63).to_a
all = r.product(r,r,r)all.map do |g|a,b,c,d = g[0], g[1], g[2], g[3]if a + b + c + d == 63rlt = a*b + b*c + c*dif rlt >= max max = rltendend
endprint("max is #{max}\n")

这种算法会先创建所有 a、b、c、d 数的组合,然后再做计算,所以非常慢,花了 11 秒之多:

% time ./test.rb
max is 991
./test.rb  10.58s user 0.95s system 97% cpu 11.831 total

swift

再来看看 Swift 语言的表现。

为了确保计时准确,我们不在 Playground 中测试;而是使用 Xcode 新创建一个 Command Line Tool 类型的项目,将如下代码放入 main.swift 文件中:

import Foundationtypealias GroupNumbers = (a: Int, b: Int, c: Int, d: Int, rlt: Int)@inline(__always) func value(_ g: GroupNumbers) -> Int {g.a * g.b + g.b * g.c + g.c * g.d
}var max = 0
let r = 1...63
for a in r {for b in r {for c in r {for d in r {if a + b + c + d == 63 {let v = (a: a, b: b, c: c, d: d, rlt: 0)let rlt = value(v)if rlt >= max {max = rlt}}}}}
}print("max is \(max)")

使用 Xcode 编译链接生成 test 可以执行文件,运行测试发现耗时要将近 7 秒多:

% time ./test
max is 991
./test  7.20s user 0.04s system 83% cpu 8.671 total
% time ./test
max is 991
./test  7.22s user 0.04s system 99% cpu 7.317 total
% time ./test
max is 991
./test  7.28s user 0.05s system 98% cpu 7.435 total

现在填之前挖的坑:如果零算自然数的话 ,结果会有怎样的不同呢?

改变上面代码中 r 的值为:

let r = 0...63

再次运行可以发现此时 max 值为 992,比之前的值大 1:

% time ./test
max is 992
./test  7.73s user 0.05s system 99% cpu 7.859 total

swift 语言咋这么不给力呢?别急,往下看!

c

c 代码看起来就很 beautiful 了:

#include <stdio.h>int main() {int start = 1;int end = 63;int max = 0;for (int a = start; a <= end; a++) {for (int b = start; b <= end; b++) {for (int c = start; c <= end; c++) {for (int d = start; d < end; d++) {if(a + b + c + d == 63) {int rlt = a*b + b*c + c*d;if(rlt >= max) {max = rlt;}}}}}}printf("max is %d\n", max);return 0;
}

使用 clang 生成可执行文件 test:

clang -o test test.c 

运行看一下结果:

% time ./test
max is 991
./test  0.04s user 0.00s system 93% cpu 0.050 total
% time ./test
max is 991
./test  0.04s user 0.00s system 94% cpu 0.047 total
% time ./test
max is 991
./test  0.04s user 0.00s system 95% cpu 0.049 total

c 果然名不虚传,结果是碾压式的胜利,只需 0.05 秒左右。

x64 asm

最后,我们来看看 macOS 中低调的 x64 汇编语言。


关于新 Apple Silicon 芯片上 ARM64 汇编的测试代码,请移步如下链接观赏:

  • 搞定“超超超难”剑桥面试数学题番外篇:ARM64汇编

为了进一步追求性能,我们将所有临时变量都放在寄存器中(x64模式下新增了 8 个 64 位通用寄存器:r8 - r15,管够! ),以减少内存操作带来的性能影响:

    .data
string: .asciz  "max is %ld\n".text.globl      _main.p2align    4, 0x90
_main:push    %rbpmov     %rsp,%rbppushq   %rbxpushq   %rdxmov     $1,%raxmov     %rax,%rbxmov     %rax,%rcxmov     %rax,%rdxxor     %r11,%r11
start_a_loop:cmpq    $63,%raxjg      end_a_loop
start_b_loop: cmpq    $63,%rbxjg      end_b_loop
start_c_loop:cmpq    $63,%rcxjg      end_c_loop
start_d_loop:cmpq    $63,%rdxjg      end_d_loop# if a + b + c + d == 63xorq    %r8,%r8add     %rax,%r8add     %rbx,%r8add     %rcx,%r8add     %rdx,%r8cmpq    $63,%r8jne     not_equ_63# == 63, 计算 a*b + b*c + c*d 放到 r8 中mov     %rax,%r8imul    %rbx,%r8mov     %r8,%r9mov     %rbx,%r8imul    %rcx,%r8mov     %r8,%r10mov     %rcx,%r8imul    %rdx,%r8addq    %r9,%r8addq    %r10,%r8cmpq    %r11,%r8jl      not_equ_63# 更新 max 值mov     %r8,%r11
not_equ_63:incq    %rdxjmp     start_d_loop
end_d_loop:xor     %rdx,%rdxincq    %rcxjmp     start_c_loop
end_c_loop:xor     %rcx,%rcxincq    %rbxjmp     start_b_loop
end_b_loop:xor     %rbx,%rbxincq    %raxjmp     start_a_loop
end_a_loop:lea     string(%rip),%rdimov     %r11,%rsicallq   _printfpopq    %rdxpopq    %rbxpopq    %rbpxor     %rax,%raxret

注意:在上面代码中我们使用 imul 指令处理乘法操作,这里还可以优化。

因为 imul 是相当慢的指令,我们可以进一步使用 SSE 或 移位指令来提高性能。

不过,这里点到为止,别影响“趣味性”哦。


在 macOS 系统中,我们使用如下命令编译和链接汇编代码:

as test.s -o test.o
ld test.o -lSystem -L `xcrun --show-sdk-path -sdk macosx`/usr/lib -o test

使用汇编果然不同凡响,我们成功的把计算耗时降低至 < 0.03 秒:

% time ./x64
max is 992
./x64  0.02s user 0.00s system 89% cpu 0.027 total
% time ./x64
max is 992
./x64  0.02s user 0.00s system 89% cpu 0.029 total
% time ./x64
max is 992
./x64  0.02s user 0.00s system 89% cpu 0.028 total

所以综上所述:为了达到极致性能,我们不得不回归 c 代码,甚至写一些让人“头大”的汇编指令才行吗?

大错特错!!!

另一个故事

ruby 语言咱就不说了,毕竟它是 真·“出了名的慢”

不过,对于 swift 和 c 语言,我们好像还没开启优化哦?(说好的20倍界王拳呢?说好的超级赛亚人呢?)

首先看 c 语言,我们之前在编译源代码时并没有使用任何优化选项,现在我们要发力了:

clang -O2 -o test test.c

在使用 O2 优化选项后,我们再来看看 c 代码的表现:

% time ./test
max is 991
./test  0.02s user 0.00s system 86% cpu 0.025 total
% time ./test
max is 991
./test  0.02s user 0.00s system 89% cpu 0.025 total
% time ./test
max is 991
./test  0.02s user 0.00s system 91% cpu 0.029 total

看到了吗?几乎和汇编版本不分上下,甚至略微胜出。

再来看看 swift 语言。在 Xcode 中将项目的 Scheme 切换至 Release 模式,并确保 clang 对应的优化选项为如下等级:

在这里插入图片描述

编译并运行可以看到,优化后 swift 语言代码的速度已妥妥的超过汇编了:

% time ./test
max is 992
./test  0.01s user 0.00s system 82% cpu 0.019 total
% time ./test
max is 992
./test  0.01s user 0.00s system 85% cpu 0.021 total
% time ./test
max is 992
./test  0.01s user 0.01s system 86% cpu 0.021 total

感兴趣的童鞋可以使用 otool 工具来查看一下优化后 c 和 swift 可执行文件的汇编代码。

所以,不要低估编译器优化的威力,也不要高估自己汇编语言的水平。T_T

当然,上面汇编代码也是可以再优化的,耗时小于 swift 优化后的代码也不是梦想,不过这真是另一个故事了


在 超详细:实现 Swift 与 汇编(Asm)代码混编并在真机或模拟器上运行 这篇博文中,我们将会在真机(iPhone 14 Pro Max)上测试上述 ARM64 汇编代码,这次汇编终于赢了 C 和 Swift!!!

有兴趣的小伙伴不妨移步观看哦。


看到这里,大家是不是觉得很有意思呢?

时刻保留一颗童心,保持一颗初心,祝天下所有程序员小伙伴们 6.1 儿童节快乐!!!🚀💯

题目的标准答案

以防小伙伴们挡不住的好奇心:这道题目的标准答案以及数学解题思路到底是什么呢?

下面是百度知道的解答:

a、b、c、d加起来是63 ab+bc+cd的最大值是多少?

这里还有 stackexchange 上国外网友的回答:

If a,b,c,d are positive integers with a sum of 63, what is the maximum value of ab + bc + cd ? (By using calculus to solve it))

最后,还有一个详细的视频解题教程,大家可以参考下:

视频:若abcd是正整数,和是63,求ab+bc+cd的最大值

总结

在本篇博文中,我们用 4 种语言(x64汇编、c、swift 和 ruby)解决了一道“超超超难”的剑桥数学题,并讨论了这些语言的实现性能,你绝猜不到最快的是谁!

感谢观赏,再会 😎

这篇关于有趣的小实验:四种语言搞定“超超超难”剑桥面试数学题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/284471

相关文章

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而

C语言:柔性数组

数组定义 柔性数组 err int arr[0] = {0}; // ERROR 柔性数组 // 常见struct Test{int len;char arr[1024];} // 柔性数组struct Test{int len;char arr[0];}struct Test *t;t = malloc(sizeof(Test) + 11);strcpy(t->arr,