1. 概述

计算自然数之和在数学、算法设计以及实际编程中都有广泛的应用场景。例如求解等差数列、性能测试中的基准计算,甚至在某些业务逻辑中也会遇到类似需求。

本文将系统性地介绍 在 Kotlin 中计算前 N 个自然数之和的多种实现方法,涵盖从传统循环到函数式编程风格的各种写法,帮助你在不同场景下选择最合适的方式。

✅ 推荐集合,这类小问题看似简单,但面试和踩坑时经常出现!


2. 使用循环

循环是最直观的实现方式,适合理解底层逻辑或对性能有明确控制需求的场景。

2.1 for 循环

使用 for 循环遍历从 1n 的每个整数并累加:

fun sumUsingForLoop(n: Int): Int {
    var sum = 0
    for (i in 1..n) {
        sum += i
    }
    return sum
}

测试验证:

Assertions.assertEquals(15, sumUsingForLoop(5))
Assertions.assertEquals(5050, sumUsingForLoop(100))

✅ 正确无误。代码清晰易懂,是初学者最常采用的方式。

⚠️ 注意:1..n 是 Kotlin 中的闭区间(inclusive range),等价于数学上的 [1, n]


2.2 while 循环

如果你更习惯 C 风格的控制结构,可以用 while 实现等效功能:

fun sumUsingWhileLoop(n: Int): Int {
    var sum = 0
    var i = 1
    while (i <= n) {
        sum += i
        i++
    }
    return sum
}

测试用例:

Assertions.assertEquals(15, sumUsingWhileLoop(5))
Assertions.assertEquals(5050, sumUsingWhileLoop(100))

✅ 结果一致。虽然语法稍显冗长,但在复杂条件判断中更具灵活性。


3. 使用等差数列公式

前 N 个自然数构成一个首项为 1、公差为 1 的等差数列,可以直接套用数学公式:

$$ \text{sum} = \frac{N(N+1)}{2} $$

这不仅效率最高(时间复杂度 O(1)),而且避免了循环开销,在处理大数值时优势明显。

实现如下:

fun sumUsingArithmeticProgressionFormula(n: Int): Int {
    return (n * (n + 1)) / 2
}

测试验证:

Assertions.assertEquals(15, sumUsingArithmeticProgressionFormula(5))
Assertions.assertEquals(5050, sumUsingArithmeticProgressionFormula(100))

✅ 完美匹配。

⚠️ 踩坑提醒:当 n 很大时,n * (n + 1) 可能溢出 Int 范围。若需支持更大范围,建议升级为 Long

fun sumUsingArithmeticProgressionFormula(n: Long): Long {
    return n * (n + 1) / 2
}

4. 使用 Range 扩展函数

Kotlin 提供了丰富的集合操作扩展函数,结合 1..n 区间可写出极具表达力的函数式代码。

4.1 使用 sum()

最简洁的方式之一,直接调用 sum() 扩展函数:

fun sumUsingRangeAndSum(n: Int): Int {
    return (1..n).sum()
}

测试:

Assertions.assertEquals(15, sumUsingRangeAndSum(5))
Assertions.assertEquals(5050, sumUsingRangeAndSum(100))

✅ 一行搞定,推荐日常使用。


4.2 使用 sumBy()

虽然多此一举用于简单求和,但 sumBy 更适用于需要映射后再求和的场景(比如平方和)。这里只是为了展示语法完整性:

fun sumUsingRangeAndSumBy(n: Int): Int {
    return (1..n).sumBy { it }
}

测试通过:

Assertions.assertEquals(15, sumUsingRangeAndSumBy(5))
Assertions.assertEquals(5050, sumUsingRangeAndSumBy(100))

✅ 功能正确,但不推荐在此类简单场景使用,略显多余。


4.3 使用 fold()

fold 允许你指定初始值并逐个累积结果,非常适合自定义聚合逻辑:

fun sumUsingRangeAndFold(n: Int): Int {
    return (1..n).fold(0) { acc, num -> acc + num }
}
  • acc 初始值为 0
  • ✅ 每次将当前数字 num 加入累加器

测试:

Assertions.assertEquals(15, sumUsingRangeAndFold(5))
Assertions.assertEquals(5050, sumUsingRangeAndFold(100))

✅ 安全且灵活,适合复杂聚合场景。


4.4 使用 reduce()

fold 类似,但 不接受初始值,而是以第一个元素作为起点:

fun sumUsingRangeAndReduce(n: Int): Int {
    return (1..n).reduce { acc, num -> acc + num }
}

⚠️ 注意:如果传入 n = 0,即空区间 (1..0),会抛出异常 —— NoSuchElementException

因此:

  • ❌ 不适用于可能为空的输入
  • ✅ 仅建议在确定区间非空时使用

测试(正常情况):

Assertions.assertEquals(15, sumUsingRangeAndReduce(5))
Assertions.assertEquals(5050, sumUsingRangeAndReduce(100))

5. 使用 Sequence

对于大数据流或惰性计算场景,Sequence 是更好的选择,它支持懒加载和链式操作优化。

实现方式如下:

fun sumUsingSequence(n: Int): Int {
    return generateSequence(1) { it + 1 }
        .take(n)
        .sum()
}
  • generateSequence(1) { it + 1 } 生成无限自然数序列
  • .take(n) 截取前 n
  • .sum() 计算总和

测试:

Assertions.assertEquals(15, sumUsingSequence(5))
Assertions.assertEquals(5050, sumUsingSequence(100))

✅ 成功运行。

📌 适用场景:

  • 数据量大且希望节省内存
  • 后续还需进行其他中间操作(如过滤、转换)
  • 希望延迟执行提升性能

6. 使用递归

递归是一种优雅的函数式解法,体现“分而治之”的思想:

fun sumUsingRecursion(n: Int): Int {
    return if (n <= 1) {
        n
    } else {
        n + sumUsingRecursion(n - 1)
    }
}

测试:

Assertions.assertEquals(15, sumUsingRecursion(5))
Assertions.assertEquals(5050, sumUsingRecursion(100))

✅ 功能正确。

⚠️ 踩坑警告:

  • ❌ 深度递归可能导致 StackOverflowError(尤其是 n > 几千
  • ✅ 若需安全递归,请改用尾递归(tailrec)

优化版(尾递归):

tailrec fun sumUsingTailRecursion(n: Int, acc: Int = 0): Int {
    return if (n <= 0) acc else sumUsingTailRecursion(n - 1, acc + n)
}

加上 tailrec 关键字后,编译器会将其优化为循环,避免栈溢出。


7. 总结

我们系统回顾了在 Kotlin 中计算前 N 个自然数之和的 7 种实现方式,各自特点总结如下:

方法 时间复杂度 是否推荐 说明
for / while loop O(n) 易懂,控制精细
等差数列公式 O(1) ✅✅✅ 最高效,首选方案
Range.sum() O(n) ✅✅ 简洁,Kotlin 风格
fold / reduce O(n) 函数式编程常用
Sequence O(n) ✅(大数据) 惰性求值,节省资源
递归 O(n) ⚠️ 注意栈溢出风险
尾递归 O(n) ✅(函数式偏好者) 安全递归替代方案

🎯 最佳实践建议

  • 日常开发优先使用 公式法Range.sum()
  • 大数据流处理考虑 Sequence
  • 学习函数式编程可深入研究 foldtailrec

所有示例代码已上传至 GitHub:https://github.com/baeldung/kotlin-tutorials/tree/master/kotlin-math

📌 建议动手实践每种写法,并尝试加入边界测试(如 n=0, n=负数),这才是真正掌握的关键。


原始标题:Calculate Sum of the First N Natural Numbers in Kotlin