1. 概述

判断一个数是否属于斐波那契数列的算法,在金融、密码学、数学建模等多个领域都有实际应用价值。

本文将重点介绍 在 Kotlin 中判断某个数字是否为斐波那契数的几种实现方式,涵盖迭代、递归、数学性质以及数据结构优化等思路。每种方法各有适用场景,理解它们有助于我们在不同性能与内存需求下做出合理选择。

2. 什么是斐波那契数列?

斐波那契数列是一个经典的数学序列,其定义是:从第3项开始,每一项都等于前两项之和

标准起始序列为:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

通常我们以 01 作为初始值。明确这一点很重要,因为有些变体可能从 1, 1 开始,这会影响最终判断结果。

✅ 建议统一采用 F(0)=0, F(1)=1 的定义,避免歧义。

3. 使用迭代法

这是最直观且效率较高的方法之一。核心思想是不断生成下一个斐波那契数,直到大于或等于目标值 n

实现步骤:

  • 初始化两个变量 first = 0, second = 1
  • 使用 while 循环逐步推进,直到 first >= n
  • 若最终 first == n,则说明 n 是斐波那契数
fun checkIfFibonacciUsingIteration(n: Int): Boolean {
    var first = 0
    var second = 1

    while (first < n) {
        val temp = first
        first = second
        second = temp + second
    }

    return n == first
}

⚠️ 注意:循环中 first 始终代表当前生成的斐波那契数,每轮更新后逼近目标值。

测试验证:

assertTrue(checkIfFibonacciUsingIteration(13))  // ✅ 13 是斐波那契数
assertFalse(checkIfFibonacciUsingIteration(14)) // ❌ 14 不是

✅ 时间复杂度 O(log n),空间 O(1),适合单次查询。

4. 使用递归(带缓存)

递归虽然代码简洁,但直接使用会导致大量重复计算(指数级时间复杂度)。因此必须引入缓存机制进行优化。

定义获取第 n 项的函数:

fun getNthFibonacci(n: Int, cache: HashMap<Int, Int>): Int {
    if (n == 0) return 0
    if (n == 1) return 1

    cache[n]?.let {
        return it
    }
    val result = getNthFibonacci(n - 1, cache) + getNthFibonacci(n - 2, cache)
    cache[n] = result
    return result
}

✅ 使用 HashMap 缓存已计算结果,避免重复调用,将时间复杂度降至 O(n)。

判断是否为斐波那契数:

fun checkIfFibonacciUsingRecursion(num: Int): Boolean {
    var n = 0
    val cache = HashMap<Int, Int>()
    
    while (getNthFibonacci(n, cache) <= num) {
        if (getNthFibonacci(n, cache) == num) return true
        n++
    }
    return false
}

测试用例:

assertTrue(checkIfFibonacciUsingRecursion(8))   // ✅
assertFalse(checkIfFibonacciUsingRecursion(25)) // ❌

⚠️ 踩坑提醒:如果不加缓存,checkIfFibonacciUsingRecursion(40) 就可能卡住。生产环境慎用无优化的递归!

5. 利用完全平方性质(数学法)

这是一个非常巧妙的数学结论:

一个正整数 x 是斐波那契数,当且仅当 5x² + 45x² - 4 中至少有一个是完全平方数。

这个性质来源于比奈公式(Binet's Formula),能将问题转化为简单的数学判断。

先实现判断完全平方数的辅助函数:

fun isPerfectSquare(x: Int): Boolean {
    val s = Math.sqrt(x.toDouble()).toInt()
    return s * s == x
}

主判断逻辑:

fun checkIfFibonacciUsingPerfectSquareProperty(n: Int): Boolean {
    return isPerfectSquare(5 * n * n + 4) || isPerfectSquare(5 * n * n - 4)
}

验证测试:

assertTrue(checkIfFibonacciUsingPerfectSquareProperty(13)) // ✅
assertFalse(checkIfFibonacciUsingPerfectSquareProperty(19)) // ❌

✅ 优势:时间复杂度 O(1),无需生成序列,适合高频单值判断。
⚠️ 注意:对于大整数可能存在浮点精度问题,必要时可用 BigDecimal 或整数开方算法替代。

6. 使用 HashSet(预计算+缓存)

适用于需要频繁查询多个数值的场景。通过预先生成一定范围内的斐波那契数并存入 HashSet,实现接近 O(1) 的查询速度。

初始化状态:

var currentLimit = 0
var currentFibonacciHashSet: HashSet<Int> = hashSetOf(0, 1)

生成指定范围内的斐波那契集合:

fun generateFibonacciNumberHashSet(limit: Int): HashSet<Int> {
    val fibonacciNumberSet = HashSet<Int>()
    var first = 0
    var second = 1
    while (first <= limit) {
        fibonacciNumberSet.add(first)
        val temp = first
        first = second
        second = temp + second
    }
    return fibonacciNumberSet
}

查询主函数(动态扩容):

fun checkIfFibonacciUsingHashSet(n: Int): Boolean {
    if (n > currentLimit) {
        currentFibonacciHashSet.addAll(generateFibonacciNumberHashSet(n))
        currentLimit = n
    }
    return currentFibonacciHashSet.contains(n)
}

✅ 动态扩展机制保证了灵活性:首次查大数会慢一点,后续相同范围查询极快。

多组测试验证:

assertTrue(checkIfFibonacciUsingHashSet(233))   // ✅
assertFalse(checkIfFibonacciUsingHashSet(4))    // ❌
assertTrue(checkIfFibonacciUsingHashSet(13))    // ✅
assertFalse(checkIfFibonacciUsingHashSet(2500)) // ❌

📌 适用场景:批量校验、API 接口中频繁调用该判断逻辑时,可显著提升整体性能。

7. 总结

本文系统介绍了四种在 Kotlin 中判断斐波那契数的有效方法:

方法 时间复杂度 空间复杂度 适用场景
迭代法 O(log n) O(1) 单次查询,推荐默认方案
递归(带缓存) O(n) O(n) 学习用途,实际慎用
完全平方性质 O(1) O(1) 高频单值判断,性能最优
HashSet 缓存 均摊 O(1) O(k) 批量查询,需预热

📌 最佳实践建议:

  • 日常开发首选 迭代法完全平方法
  • 高并发服务中可考虑 HashSet 预加载模式
  • 避免使用原始递归,容易引发性能事故

所有示例代码均可在 GitHub 获取:https://github.com/Baeldung/kotlin-tutorials/tree/master/kotlin-math


原始标题:How to Check if a Number Is Part of the Fibonacci Series in Kotlin