程序员需要知道数学吗?
#初学者 #编程 #php #math

擅长数学的辩论是否使一名出色的程序员与科技行业本身一样古老。意见极其两极分化,从完全丢弃数学与声称数学或CS学位无关,成为软件工程的绝对先决条件。

一如既往,事实在频谱的中间。本文旨在给出一个实用的例子,说明数学如何告知编程。

免责声明

毫无疑问,编程的利基领域需要独特的技能和/或专业教育。例如:计算机图形学,图像/视频处理,密码学,机器学习,建模物理过程等。其中一些比实际编程更接近科学研究,并且确实需要学术培训。

与上面的域相反,通用软件工程与平凡的业务应用程序有关:桌面,移动和Web应用程序。这不是火箭科学。

实际例子

考虑一个实用的编码示例,大多数开发人员在职业生涯的早期遇到。任务是用unit prefix渲染一个人类友好的圆形数字,例如:

  • 350,000 -350K
  • 1,500,000 1500万
  • 等等。

此算法无处不在:从财务图表到YouTube喜欢。

这是Laravel社区的最新推文。

代码片段在PHP中,但它们转化为任何高级编程语言,例如JavaScript。

基本实现

直接实现将是分别处理每个单元的转换,检查所有受支持的整数范围。

初始实现:

function formatNumber(
    int|float $number,
    int $precision = 1
): string {
    if ($number < 1000) {
        $result = $number;
        $unit = '';
    } else if ($number < 1000000) {
        $result = $number / 1000;
        $unit = 'K';
    } else if ($number < 1000000000) {
        $result = $number / 1000000;
        $unit = 'M';
    } else {
        $result = $number / 1000000000;
        $unit = 'B';
    }
    return (string)round($result, $precision) . $unit;
}

此算法有几个缺点。

最大的缺点之一是必须修改源代码以注册新的单元前缀;它们在实施中进行了硬编码。它不够通用,无法用作可重复使用的库。

另一个缺点是整个实施中的重复。您需要非常小心地输入0以免犯错。

让我们尝试克服这些限制。

广义实施

我们算法演变中的下一个迭代接受单位作为输入和循环。现在,它可以与任意数量的单元前缀!

一起使用

改进的实现:

function formatNumber(
    int|float $number,
    int $precision = 1,
    int $base = 1000,
    array $units = ['K', 'M', 'B']
): string {
    $result = $number;
    $unit = '';
    $unitCount = count($units);
    for ($unitIndex = 0; $unitIndex < $unitCount; $unitIndex++) {
        if ($result < $base) {
            break;
        }
        $result = $result / $base;
        $unit = $units[$unitIndex];
    }
    return (string)round($result, $precision) . $unit;
}

对于固定的单元,这种基于循环的方法等同于原始实现,只是更灵活。

现在,基本因子1000还没有硬编码,例如,实现支持小数和binary units,例如:

formatNumber($number, base: 1024, units: ['KiB', 'MiB', 'GiB'])

我们尚未考虑算法复杂性,让我们看看实现的位置。大o符号中的time complexity是o(n),其中n是单位前缀的数量。

该算法非常灵活,简短且易于理解和维护。但是,线性复杂性可以提高吗?

优化实现

进行进一步的改进需要完全重新思考该方法。理想情况下,该算法应独立于输入大小。这意味着根本没有循环。

仔细研究算法。每次迭代做什么?它基于先前迭代的结果来计算中间结果。但是我们可以立即跳到解决方案吗?那是数学的来源。

每次迭代逐渐将输入号转换为较大的单位。迭代次数对应于最大的适用单元。每个部门本质上都会增加转换中使用的碱的功率。

我们可以将输入编号除以将其除以升高为单位功率的底座来将其转换为相应的单元。我们如何确定基本力量?我们可以使用对数来计算产生输入号的基础功率。然后将其围成最接近的整数。这也为我们提供了单位数组中的顺序索引(为零基索引进行调整)。和Voilã,我们同时获得了转换的数字及其单位前缀!

最终实现:

function formatNumber(
    int|float $number,
    int $precision = 1,
    int $base = 1000,
    array $units = ['K', 'M', 'B']
): string {
    $unitCount = count($units);
    $power = (int)floor(log(abs($number), $base));
    $power = min($power, $unitCount);
    $result = $number / pow($base, $power);
    $unitIndex = $power - 1;
    $unit = $units[$unitIndex] ?? '';
    return (string)round($result, $precision) . $unit;
}

表达式min($power, $unitCount)将算法限制为已定义前缀的单元,即转换停止在最大的单元。

请注意,abs($number)使其用于负数,所有以前的实现都被忽略了。

算法的时间复杂性为o(1),因为它在固定时间内完成,无论单位前缀的数量如何。缺乏条件和循环还改善了其他代码质量指标,即Cyclomatic complexityNPath complexity

结论

在日常编码中,数学绝不需要。实际上,即使它确实归结为高级数学,也将使用其技术堆栈的Rich Oss生态系统提供的现有库。

但是,中学代数和几何形状偶尔会派上用场。数学是对某些原本资源密集型算法的性能优化的基础:熟练程度可以理解和实施这些优化。

愉快的编程,有或没有数学!