【问题标题】:How to turn integers into Fibonacci coding efficiently?如何有效地将整数转换为斐波那契编码?
【发布时间】:2016-05-27 09:23:18
【问题描述】:

斐波那契数列是从0和1开始,然后将最后两个数字相加得到下一个。

所有正整数都可以表示为一组斐波那契数的总和,而无需重复。例如:13 可以是集合 {13}、{5,8} 或 {2,3,8} 的总和。但是,正如我们已经看到的,一些数有不止一个集合,其总和就是这个数。如果我们添加集合不能有两个连续的斐波那契数的约束,那么我们对每个数都有一个唯一的表示。

我们将使用二进制序列(只有零和一)来做到这一点。例如,17 = 1 + 3 + 13。那么,17 = 100101。详细解释见图2。

我想把一些整数变成这个表示,但是整数可能非常大。如何有效地做到这一点。

【问题讨论】:

标签: algorithm math


【解决方案1】:

问题本身很简单。您总是选择小于余数的最大斐波那契数。您可以忽略连续数字的约束(因为如果您需要两者,则下一个是两者的总和,因此您应该选择那个而不是最初的两个)。

所以问题仍然是如何快速找到小于某个数 X 的最大斐波那契数。 有一个已知的技巧是从矩阵开始(称为 M)

1 1
1 0

您可以通过矩阵乘法计算斐波那契数(第 x 个数是 M^x)。更多细节在这里:https://www.nayuki.io/page/fast-fibonacci-algorithms。最终结果是您可以在 O(logN) 矩阵乘法中计算出您要查找的数字。

如果它们不适合现有类型,您将需要大量计算(乘法和加法)。 还要存储与您第一次计算的 2 的幂相对应的矩阵,因为您将再次需要它们来获得结果。

总的来说,这应该是 O((logN)^2 * large_number_multiplications/additions))。

【讨论】:

    【解决方案2】:

    首先我想告诉你,我真的很喜欢这个问题,我不知道所有正整数都可以表示为一组斐波那契数的总和而无需重复,我看到了归纳证明,太棒了.
    为了回答您的问题,我认为我们必须弄清楚演示文稿是如何创建的。我认为找到这个的简单方法是从我们找到最接近的次要斐波那契项目的数字中。
    例如,如果我们要呈现 40:
    我们有 Fib(9)=34 和 Fib(10)=55,所以演示文稿中的第一个元素是 Fib(9)
    由于 40 - Fib(9) = 6 且 (Fib(5) =5 且 Fib(6) =8) 下一个元素是 Fib(5)。
    所以我们有 40 = Fib(9) + Fib(5)+ Fib(2)
    请允许我用 C# 编写此代码

     class Program
        {
            static void Main(string[] args)
            {
                List<int> fibPresentation = new List<int>();
                int numberToPresent = Convert.ToInt32(Console.ReadLine());
                while (numberToPresent > 0)
                {
                    int k =1;
                    while (CalculateFib(k) <= numberToPresent)
                    {
                        k++;
                    }
                    numberToPresent = numberToPresent - CalculateFib(k-1);
                    fibPresentation.Add(k-1);
                }
            }
            static int CalculateFib(int n)
            {
                if (n == 1)
                    return 1;
    
                int a = 0;
                int b = 1;
                // In N steps compute Fibonacci sequence iteratively.
                for (int i = 0; i < n; i++)
                {
                    int temp = a;
                    a = b;
                    b = temp + b;
                }
                return a;
            }
        }
    

    您的结果将在 fibPresentation 中

    【讨论】:

      【解决方案3】:

      这种编码更准确地称为“Zeckendorf 表示”:参见https://en.wikipedia.org/wiki/Fibonacci_coding

      贪婪的方法有效(请参阅https://en.wikipedia.org/wiki/Zeckendorf%27s_theorem),这里有一些将数字转换为这种表示的 Python 代码。它使用前 100 个斐波那契数,并且对于 927372692193078999175 以内的所有输入都可以正常工作(对于任何更大的输入都不正确)。

      fibs = [0, 1]
      for _ in xrange(100):
          fibs.append(fibs[-2] + fibs[-1])
      
      def zeck(n):
          i = len(fibs) - 1
          r = 0
          while n:
              if fibs[i] <= n:
                  r |= 1 << (i - 2)
                  n -= fibs[i]
              i -= 1
          return r
      
      print bin(zeck(17))
      

      输出是:

      0b100101
      

      【讨论】:

        【解决方案4】:

        由于贪婪方法似乎有效,因此能够反转关系 N=Fn 就足够了。

        通过比内公式Fn=[φ^n/√5],其中括号表示最接近的整数。然后使用n=floor(lnφ(√5N)),您就非常接近解决方案了。

        17 => n = floor(7.5599...) => F7 = 13
        4 => n = floor(4.5531) => F4 = 3
        1 => n = floor(1.6722) => F1 = 1
        

        (我不排除某些n 值可以减一。)

        【讨论】:

          【解决方案5】:

          我不确定这对你来说是否足够有效,但你可以简单地使用回溯来找到一个有效的表示。

          我会尝试通过采用尽可能大的 fib 数字来开始回溯步骤,并且仅在违反 连续only once 约束时才切换到较小的步骤。

          【讨论】:

            猜你喜欢
            • 2010-09-22
            • 2012-12-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-05
            • 2014-05-23
            • 2014-05-03
            相关资源
            最近更新 更多