【问题标题】:Test if a number is fibonacci测试一个数字是否是斐波那契
【发布时间】:2011-01-26 19:43:40
【问题描述】:

我知道如何制作斐波那契数字列表,但我不知道如何测试给定数字是否属于斐波那契列表 - 想到的一种方法是生成 fib 列表。数到那个数字,看看它是否属于数组,但必须有另一种更简单、更快的方法。

有什么想法吗?

【问题讨论】:

  • 对我来说好像是作业,所以我添加了作业标签。
  • 相关问题请参见stackoverflow.com/questions/1525521/…
  • 请允许OP自行添加作业标签(请随时要求澄清)。很多事情看起来不像家庭作业。
  • 请不要仅仅因为它“看起来很合适”而添加标签。它“在我看来”就像 OP 想要在 Brainf*ck 中执行此操作一样,我应该添加那个标签吗?

标签: algorithm testing math fibonacci


【解决方案1】:

一个非常好的测试是当且仅当5 N^2 + 45N^2 – 4 是一个平方数时N 是一个斐波那契数。有关如何有效测试数字是否为正方形的想法,请参阅SO discussion

希望对你有帮助

【讨论】:

  • +1 因为说“或”比说“一个”+“和”更清楚前 4 次我读了其他答案我认为他们说的是不同的东西,因为我没有看到“一个”部分
  • 我对这个解决方案持怀疑态度,因为它涉及对斐波那契数进行平方。斐波那契数字增长得非常快,而且大多数都非常大。对它们进行平方不会在计算上变得昂贵吗?
  • 嗯,是的,超过 2^63(类似于 Fib(300))你将不得不使用一些任意精度的算法,这将是昂贵的。随着数字的增长,您必须使用近似方法,使用比内公式或其他方法。我会惊讶地发现任何适用于大量数字的有效精确方法!
  • 嗯...如果命题 A 和 B 中的一个需要成立(但不能同时成立!),您不能写“A 或 B”,因为如果 A 为真,则此复合陈述为真如果 A 为假 B 为真,且 A 和 B 都为真,则 B 为假。然后你需要明确地写出“恰好一个”,或者使用逻辑“xor”运算符而不是“or”。
  • 但似乎“或”确实是正确的运算符。要看到这一点,设置 N = 1。那么 N 是一个斐波那契数,both 5*N^2 + 4 和 5*N^2 - 4 都是完美的正方形。如果我们有一个异或运算符,那么“A xor B”将是错误的,即使 1 是斐波那契,我们也有矛盾。 (这里我假设这个定理对于“或”或“异或”是正确的。)
【解决方案2】:

正整数 ω 是斐波那契数当且仅当 5ω2 + 4 或 5ω2 - 4 是完美平方。

请参阅The Fabulous Fibonacci Numbers 了解更多信息。

【讨论】:

    【解决方案3】:

    虽然有几个人指出了完美平方解决方案,但它涉及对斐波那契数进行平方,通常会产生大量乘积。

    甚至可以保存在标准 64 位整数中的斐波那契数少于 80 个。

    这是我的解决方案,它的操作完全小于要测试的数字。
    (用 C# 编写,使用 doublelong 等基本类型。但该算法应该适用于更大的类型。)

    static bool IsFib(long T, out long idx)
    {
        double root5 = Math.Sqrt(5);
        double phi = (1 + root5) / 2;
    
        idx    = (long)Math.Floor( Math.Log(T*root5) / Math.Log(phi) + 0.5 );
        long u = (long)Math.Floor( Math.Pow(phi, idx)/root5 + 0.5);
    
        return (u == T);
    }
    


    在我写完这个答案 4 年多之后,一位评论者询问了第二个参数,由 out 传递。

    参数 #2 是斐波那契数列的“索引”。
    如果要测试的值,T 是一个斐波那契数,那么 idx 将是该数在斐波那契数列中从 1 开始的索引。 (有一个明显的例外)

    斐波那契数列是1 1 2 3 5 8 13,等等。
    3 是序列中的第四个数字:IsFib(3, out idx); 将返回 true 和值 4
    8 是序列中的第 6 个数字:IsFib(8, out idx); 将返回 true 和值 6
    13 是第 7 个数字; IsFib(13, out idx); 将返回 true 和值 7

    一个例外是IsFib(1, out idx);,它将返回2,即使值 1 出现在索引 1 和 2 上。

    如果IsFib被传递一个非斐波那契数,它将返回falseidx的值将是小于T的最大斐波那契数的索引。

    16 不是斐波那契值。
    IsFib(16, out idx); 将返回 false 和值 7
    您可以使用Binet's Formula 将索引 7 转换为斐波那契值 13,这是小于 16 的最大数。

    【讨论】:

    • 简洁的实现。我在比赛中实际使用过这个功能:hackerrank.com/contests/codesprint5/challenges/is-fibo :)
    • 谢谢。它看起来像魔术。 @Michal 我也在hackerrank比赛中使用过这个功能。
    • 非常好 - 谢谢!我用它来获得最接近的斐波那契数:) 但在现实生活中,我认为没有必要计算这些数字,而是将它们存储在数据库中(就像你在另一篇文章中建议的那样)
    • 只有一个问题,第二个参数到底是什么,为什么要通过引用传递它?
    • 只是出于好奇,你是怎么想到这个的?
    【解决方案4】:
    #!/bin/bash
    victim="144"
    curl http://aux.planetmath.org/files/objects/7680/fib.txt | sed 's/^[0-9]*//;s/[ \t]//g' | grep "^$victim$" >/dev/null 2>/dev/null
    if [[ $? -eq 0 ]] ; then
        echo "$victim is a fibonacci number"
    else
        echo "$victim aint"
    fi
    

    【讨论】:

    • 外包。喜欢它!
    【解决方案5】:

    如果你的数字是有界的,那么简单地将所有低于上限的斐波那契数放入一个哈希表并测试包含性就可以了。斐波那契数很少(例如,只有 38 个低于 500 万),因为它们呈指数增长。

    如果您的数字不是有界大小,那么建议的平方测试技巧几乎肯定会比生成斐波那契数列要慢,直到找到或超过数字。

    【讨论】:

      【解决方案6】:

      正整数ω是斐波那契数

      当且仅当其中之一2 + 4 和 5ω2 - 4 是一个完美的正方形

      来自 Alfred Posamentier 和 Ingmar Lehmann 的(神话般的)斐波那契数字

      bool isFibonacci(int  w)
      {
             double X1 = 5 * Math.Pow(w, 2) + 4;
             double X2 = 5 * Math.Pow(w, 2) - 4;
      
             long X1_sqrt = (long)Math.Sqrt(X1);
             long X2_sqrt = (long)Math.Sqrt(X2);   
      
             return (X1_sqrt*X1_sqrt == X1) || (X2_sqrt*X2_sqrt == X2) ;
      }
      

      I copied it from this source


      打印1k10k 之间的斐波那契数的代码段。

      for (int i = 1000; i < 10000; i++)
      {
               if (isFibonacci(i))
                    Console.Write(" "+i);
      }
      

      天哪,只有四个!!!

      用其他方法

      from math import *
      
      phi = 1.61803399
      sqrt5 = sqrt(5)
      
      def F(n):
          return int((phi**n - (1-phi)**n) /sqrt5)
      
      def isFibonacci(z):
          return F(int(floor(log(sqrt5*z,phi)+0.5))) == z
      
      print [i for i in range(1000,10000) if isFibonacci(i)]
      

      【讨论】:

      • 不需要 "?true : false" 部分:前面的表达式已经是布尔值。
      • 我在 python 中编写了第二种方法,因为我不知道 C# Math.Log 也适用于其他基础。你们要我也写吗:P??大声笑
      【解决方案7】:

      要找到解决方案,请查看比内公式。
      (在 Wikipedia 上的 Fibonacci Number 下查找“封闭式表达式”)

      它说斐波那契数列是由一个简单的封闭公式创建的:

      我相信如果你求解n,并测试n 是否为整数,你就会得到答案。

      编辑正如@psmears 指出的那样,同一篇维基百科文章也有一个关于检测斐波那契数的部分。维基百科是一个很好的来源。

      【讨论】:

        【解决方案8】:

        请参阅wikipedia article about the Fibonacci numbers 上的“识别斐波那契数”部分。

        【讨论】:

        • 嘿,你是林肯的 P Smears 吗?
        【解决方案9】:

        由于斐波那契数呈指数增长,您建议的方法非常快。 另一个是this

        【讨论】:

        • 我真的很喜欢闭区间解决方案,应该比检查正方形容易得多!
        【解决方案10】:

        根据我和 psmears 之前的回答,我编写了这段 C# 代码。

        逐步走的很慢,可以明显的减少和优化:

        // Input: T: number to test.
        // Output: idx: index of the number in the Fibonacci sequence.
        //    eg: idx for 8 is 6. (0, 1, 1, 2, 3, 5, 8)
        // Return value: True if Fibonacci, False otherwise.
        static bool IsFib(long T, out int idx)
        {
            double root5 = Math.Sqrt(5);
            double PSI = (1 + root5) / 2;
        
            // For reference, IsFib(72723460248141) should show it is the 68th Fibonacci number
        
            double a;
        
            a = T*root5;
            a = Math.Log(a) / Math.Log(PSI);
            a += 0.5;
            a = Math.Floor(a);
            idx = (Int32)a;
        
            long u = (long)Math.Floor(Math.Pow(PSI, a)/root5 + 0.5);
        
            if (u == T)
            {
                return true;
            }
            else
            {
                idx = 0;
                return false;
            }
        }
        

        测试表明这适用于前 69 个斐波那契数,但在第 70 个时失效。

        F(69) = 117,669,030,460,994 - Works
        F(70) = 190,392,490,709,135 - Fails
        

        总的来说,除非您使用某种 BigInt 库,否则最好有一个简单的斐波那契数查找表并检查它,而不是运行算法。

        前 300 个号码的列表可在线获取。

        但此代码确实概述了一个可行的算法,前提是您有足够的精度,并且不会溢出您的数字表示系统。

        【讨论】:

        • phi 的问题是它不能完全使用浮点数,所以你必须近似。
        【解决方案11】:

        来自维基百科:http://en.wikipedia.org/wiki/Fibonacci_number

        正整数 z 是斐波那契 数字当且仅当 5z^2 + 4 之一 或者 5z^2 − 4 是一个完美的正方形。

        【讨论】:

        • 很奇怪。经过 15 年的数学,我不知道这一点。
        【解决方案12】:

        Re:Ahmad 的代码 - 一种更简单的方法,没有递归或指针,相当幼稚,但几乎不需要计算能力来处理真正巨大的数字(大约 2N 次加法来验证第 N 个 fib 数,这在现代机器上)最坏的情况下需要几毫秒)

        // 如果找到任何东西,则返回 pos,如果没有,则返回 0(C/C++ 将任何值 !=0 视为 true,因此最终结果相同)

        int isFib (long n)
        {
            int pos = 2;
            long last = 1;
            long current = 1;
            long temp;
        
            while (current < n)
            {
                temp = last;
                last = current;
                current = current + temp;
                pos++;
            }
        
            if (current == n)
                return pos;
            else
                return 0;
        
        }
        

        【讨论】:

        • 很确定这是最有效的方法。
        • ` def is_fibonacci?(i) a,b=0,1 until b >= i a,b=b,a+b return true if b == i end end`
        【解决方案13】:

        斐波那契数的一般表达式是 F(n) = [ [(1+sqrt(5))/2] 支持 n+1 - [(1-sqrt(5))/2] 支持 n+1 ]/ sqrt(5) ..... (*) 对于较大的 n,第二个指数变为零并执行 数值运算我们得到 F(n) = [ (1.618) sup n+1 ] / 2.236

        如果K是要测试的数log(k*2.2336)/log(1.618)应该是整数!

        K 等于 13 的示例我的计算器给出的答案是 7.00246 对于 K 等于 14,答案是 7.1564。

        您可以通过取最接近的整数来增加结果的可信度 回答并代入 (*) 以确认结果为 K

        【讨论】:

          【解决方案14】:

          您处理的数字有多大?

          查找表对您有用吗? (您可以搜索的预先计算的数字列表)

          还有一个closed-form expression,我猜你可以倒过来分析得到答案(虽然我不是数学家,所以我不能保证这个建议是有道理的)

          【讨论】:

          【解决方案15】:

          我对此处介绍的方法进行了一些基准测试,以及简单的加法、预先计算数组并​​将结果存储在哈希中。至少对于 Perl,平方方法比对数方法快一点,可能快 20%。正如 abelenky 所指出的,这是在是否有空间对位数进行平方之间进行权衡。

          当然,最快的方法是散列您域空间中的所有斐波那契数。根据 abelenky 提出的另一点,这些吸盘中只有 94 个小于 2^64。

          您应该预先计算它们,并将它们放入 Perl 哈希、Python 字典或其他任何东西中。

          斐波那契数的性质很有趣,但是用它们来确定计算机程序中的某个整数是否为整数,这有点像在每次程序启动时编写一个子程序来计算 pi。

          【讨论】:

            【解决方案16】:

            这是我的解决方案,我不确定它是否具有基准。我希望这会有所帮助!

            def is_fibonacci?(i)
              a,b=0,1
                until b >= i
                    a,b=b,a+b
                    return true if b == i
                end
            end
            

            a,b=b,a+b在做什么

             0, 1 = 1, 0 +1
             1, 1 = 1, 1 + 1
             1, 2 = 2, 1 + 2
             2, 3 = 3, 2 + 3
            
            fib1 = fib2
            fib2 = fib1 + fib2
            

            【讨论】:

              【解决方案17】:

              Scala 版本-

              def isFib(n: Int): Boolean = {
              
              def checkFib(f1: Int = 1, f2: Int = 1): Boolean = {
              
              if(n == f1 || n == f2) true
              else if(n < f2) false
              else checkFib(f2, f1+f2)
              
              }
              
              checkFib()
              
              }
              

              【讨论】:

                【解决方案18】:

                Java 解决方案可以如下完成。但是还是可以优化的

                以下解决方案适用于

                1. 1≤T≤10 ^5
                2. 1≤N≤10 ^10

                T 是测试用例的数量, N是数字的范围

                    import java.util.Scanner;
                    import java.math.BigDecimal;
                    import java.math.RoundingMode;
                
                    public class FibonacciTester {
                        private static BigDecimal zero = BigDecimal.valueOf(0);
                        private static BigDecimal one = BigDecimal.valueOf(1);
                        private static BigDecimal two = BigDecimal.valueOf(2);
                        private static BigDecimal four = BigDecimal.valueOf(4);
                        private static BigDecimal five = BigDecimal.valueOf(5);
                
                        public static void main(String[] args) {
                            Scanner sc = new Scanner(System.in);
                            int n = sc.nextInt();
                            BigDecimal[] inputs = new BigDecimal[n];
                            for (int i = 0; i < n; i++) {
                                inputs[i] = sc.nextBigDecimal();
                            }
                
                            for (int i = 0; i < inputs.length; i++) {
                                if (isFibonacci(inputs[i]))
                                    System.out.println("IsFibo");
                                else
                                    System.out.println("IsNotFibo");
                            }
                
                
                        }
                
                        public static boolean isFibonacci(BigDecimal num) {
                            if (num.compareTo(zero) <= 0) {
                                return false;
                            }
                
                            BigDecimal base = num.multiply(num).multiply(five);
                            BigDecimal possibility1 = base.add(four);
                            BigDecimal possibility2 = base.subtract(four);
                
                
                            return (isPerfectSquare(possibility1) || isPerfectSquare(possibility2));
                        }
                
                        public static boolean isPerfectSquare(BigDecimal num) {
                            BigDecimal squareRoot = one;
                            BigDecimal square = one;
                            BigDecimal i = one;
                            BigDecimal newSquareRoot;
                            int comparison = -1;
                
                            while (comparison != 0) {
                                if (comparison < 0) {
                                    i = i.multiply(two);
                                    newSquareRoot = squareRoot.add(i).setScale(0, RoundingMode.HALF_UP);
                                } else {
                                    i = i.divide(two);
                                    newSquareRoot = squareRoot.subtract(i).setScale(0, RoundingMode.HALF_UP);
                                }
                
                                if (newSquareRoot.compareTo(squareRoot) == 0) {
                                    return false;
                                }
                
                                squareRoot = newSquareRoot;
                                square = squareRoot.multiply(squareRoot);
                                comparison = square.compareTo(num);
                            }
                
                            return true;
                        }
                    }
                

                【讨论】:

                  【解决方案19】:

                  基本上所有的答案都给出了。我想添加一个非常快的 C++ 示例代码。

                  基础是这里已经多次提到的查找机制。

                  通过比内特的公式,我们可以计算出只有极少数的斐波那契数可以适合 C++ unsigned long long 数据类型,而这种数据类型在 2021 年通常是 64 位。环形交叉口 93。现在这个数字非常低。

                  借助现代 C++ 17(及更高版本)功能,我们可以在编译时轻松创建 64 位数据类型的所有斐波那契数的 std::array

                  因此,我们将只为查找数组花费 93*8= 744 BYTE 非运行时内存。

                  然后使用std::binary_search 查找值。所以,整个函数将是:

                  bool isFib(const unsigned long long numberToBeChecked) {
                      return std::binary_search(FIB.begin(), FIB.end(), numberToBeChecked);
                  }
                  

                  FIB 是一个编译时间,constexpr std::array。那么,如何构建这个数组呢?

                  我们首先将计算斐波那契数的默认方法定义为constexpr 函数:

                  // Constexpr function to calculate the nth Fibonacci number
                  constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
                      // Initialize first two even numbers 
                      unsigned long long f1{ 0 }, f2{ 1 };
                  
                      // Calculating Fibonacci value 
                      while (index--) {
                          // get next value of Fibonacci sequence 
                          unsigned long long f3 = f2 + f1;
                          // Move to next number
                          f1 = f2;
                          f2 = f3;
                      }
                      return f2;
                  }
                  

                  这样,斐波那契数可以在运行时轻松计算。然后,我们用所有斐波那契数填充std::array。我们还使用了constexpr,并使其成为带有可变参数包的模板。

                  我们使用std::integer_sequence 为索引 0、1、2、3、4、5、...创建一个斐波那契数。

                  这很简单,并不复杂:

                  template <size_t... ManyIndices>
                  constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
                      return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
                  };
                  

                  这个函数将输入一个整数序列 0,1,2,3,4,... 并返回一个 std::array&lt;unsigned long long, ...&gt; 和相应的斐波那契数。

                  我们知道我们最多可以存储 93 个值。因此我们创建了一个 next 函数,它将使用整数序列 1,2,3,4,...,92,93 调用上述函数,如下所示:

                  constexpr auto generateArray() noexcept {
                      return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
                  }
                  

                  现在,终于,

                  constexpr auto FIB = generateArray();
                  

                  将给我们一个编译时std::array&lt;unsigned long long, 93&gt;,名称为 FIB,包含所有斐波那契数。如果我们需要第 i 个斐波那契数,那么我们可以简单地写成FIB[i]。运行时不会进行计算。


                  整个示例程序如下所示:

                  #include <iostream>
                  #include <array>
                  #include <utility>
                  #include <algorithm>
                  #include <iomanip>
                  // ----------------------------------------------------------------------
                  // All the following will be done during compile time
                  
                  // Constexpr function to calculate the nth Fibonacci number
                  constexpr unsigned long long getFibonacciNumber(size_t index) noexcept {
                      // Initialize first two even numbers 
                      unsigned long long f1{ 0 }, f2{ 1 };
                  
                      // calculating Fibonacci value 
                      while (index--) {
                          // get next value of Fibonacci sequence 
                          unsigned long long f3 = f2 + f1;
                          // Move to next number
                          f1 = f2;
                          f2 = f3;
                      }
                      return f2;
                  }
                  // We will automatically build an array of Fibonacci numbers at compile time
                  // Generate a std::array with n elements 
                  template <size_t... ManyIndices>
                  constexpr auto generateArrayHelper(std::integer_sequence<size_t, ManyIndices...>) noexcept {
                      return std::array<unsigned long long, sizeof...(ManyIndices)>{ { getFibonacciNumber(ManyIndices)... } };
                  };
                  
                  // Max index for Fibonaccis that for an 64bit unsigned value (Binet's formula)
                  constexpr size_t MaxIndexFor64BitValue = 93;
                  
                  // Generate the required number of elements
                  constexpr auto generateArray()noexcept {
                      return generateArrayHelper(std::make_integer_sequence<size_t, MaxIndexFor64BitValue>());
                  }
                  
                  // This is an constexpr array of all Fibonacci numbers
                  constexpr auto FIB = generateArray();
                  
                  // All the above was compile time
                  // ----------------------------------------------------------------------
                  
                  
                  // Check, if a number belongs to the Fibonacci series
                  bool isFib(const unsigned long long numberToBeChecked) {
                      return std::binary_search(FIB.begin(), FIB.end(), numberToBeChecked);
                  }
                  
                  // Test
                  int main() {
                  
                      const unsigned long long testValue{ 498454011879264ull };
                  
                      std::cout << std::boolalpha << "Does '" <<testValue << "' belong to Fibonacci series?  --> " << isFib(498454011879264) << '\n';
                  
                      return 0;
                  }
                  

                  使用 Microsoft Visual Studio Community 2019 版本 16.8.2 开发和测试

                  使用 gcc 10.2 和 clang 11.0.1 进行了额外测试

                  语言:C++ 17

                  【讨论】:

                    【解决方案20】:
                    int isfib(int n /* number */, int &pos /* position */)
                    {
                       if (n == 1)
                       {
                          pos=2;  // 1 1
                          return 1;
                       }
                       else if (n == 2)
                       {
                          pos=3;  // 1 1 2
                          return 1;
                       }
                       else
                       {
                          int m = n /2;
                          int p, q, x, y;
                          int t1=0, t2 =0;
                          for (int i = m; i < n; i++)
                          {
                            p = i;
                            q = n -p;    // p + q = n
                            t1 = isfib(p, x);
                            if (t1) t2 = isfib(q, y);
                            if (t1 && t2 && x == y +1)
                            {
                               pos = x+1;
                               return 1; //true
                            }
                          }
                          pos = -1;
                          return 0; //false
                       }
                    }
                    

                    这个怎么样?

                    【讨论】:

                    • 逻辑很好,但几乎完全不可读。必须处理变量命名
                    【解决方案21】:
                    #include <stdio.h>
                    #include <math.h>
                    
                    int main()
                    {
                    int number_entered, x, y;
                    
                    printf("Please enter a number.\n");
                    scanf("%d", &number_entered);
                    x = y = 5 * number_entered^2 + 4;        /*Test if 5N^2 + 4 is a square number.*/
                    x = sqrt(x);
                    x = x^2;
                    if (x == y)
                    {
                            printf("That number is in the Fibonacci sequence.\n");
                        }
                    x = y = 5 * number_entered^2 - 4;        /*Test if 5N^2 - 4 is a square number.*/
                    x = sqrt(x);
                    x = x^2;
                    if (x == y)
                    {
                        printf("That number is in the Fibonacci sequence.\n");
                    }
                    else
                    {
                        printf("That number isn't in the Fibonacci sequence.\n");
                    }
                    return 0;
                    }
                    

                    这行得通吗?

                    【讨论】:

                    • 没有。在 C 中,^按位异或 运算符。您需要x * xpow(x,2) 来计算一个数字。程序的逻辑也有问题。
                    猜你喜欢
                    • 2015-06-02
                    • 1970-01-01
                    • 2014-12-10
                    • 2022-01-04
                    • 1970-01-01
                    • 2012-11-09
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多