【问题标题】:nth non-Fibonacci number第 n 个非斐波那契数
【发布时间】:2015-07-25 14:42:11
【问题描述】:

如何在 o(logn) 中找到第 n 个非斐波那契数
非斐波那契数是:4,6,7,9,10....
下面的函数给出给定 n 值的非斐波那契数

static int nonFibonacci(int n){
    int a=1,b=2,c=3;
    while(n>0){
        a=b;
        b=c;
        c=a+b;
        n-=(a-1);
    }
    n+=(a-1);
    return (n+b);
}

它给出了正确的答案,但我想优化它,使用矩阵乘法可以在 o(logn) 中找到斐波那契数,如何应用该属性来找到第 n 个非斐波那契数。

【问题讨论】:

  • 由于斐波那契数列的指数分布,我相信这是 O(log n)。

标签: java fibonacci number-theory


【解决方案1】:

您的 sn-p 中的算法不是O(n),因为斐波那契数列呈指数增长。因此,你需要O(log n) 步才能获得大于 n 的值。以下(和非常简单的“基准”)似乎确认您的代码已经具有正确的复杂性:

public class Benchmark {
    public static void main(String[] args) {
        long n = 2;
        for (int i=0 ; i<62 ; i++, n *= 2) {
            long executionTimeNanos = timedExecution(n);
            System.out.println(n + " " + executionTimeNanos + " " + executionTimeNanos/Math.log(n));
        }
    }

    private static long timedExecution(long n) {
        long start = System.nanoTime();
        nonFibonacci(n);
        return System.nanoTime() - start;
    }

    private static long nonFibonacci(long n) {
        long a = 1, b = 2, c = 3;
        while (n > 0) {
            a = b;
            b = c;
            c = a + b;
            n -= (a - 1);
        }
        n += (a - 1);
        return (n + b);
    }
}

输出:

2 3421 4935.459734881144
4 2566 1850.97773746054
8 855 411.16808665335464
16 2566 925.48886873027
32 1283 370.195547492108
64 855 205.58404332667732
128 1283 264.4253910657915
256 1283 231.3722171825675
512 856 137.21632833343918
1024 1283 185.097773746054
2048 1711 224.40465590554695
4096 1711 205.70426791341805
8192 1283 142.38290288158
16384 1710 176.2148942800091
32768 1283 123.39851583070268
65536 1283 115.68610859128376
131072 1283 108.88104338003177
262144 1710 137.05602888445154
524288 1711 129.91848499794824
1048576 1283 92.548886873027
2097152 1710 117.47659618667274
4194304 1711 112.20232795277347
8388608 1710 107.26123999652728
16777216 1711 102.85213395670903
33554432 1711 98.73804859844066
67108864 1710 94.88494307385106
134217728 1710 91.37068592296768
268435456 2138 110.16007133645014
536870912 2138 106.36144818691737
1073741824 1711 82.28170716536722
2147483648 2138 99.49941927163238
4294967296 2566 115.68610859128376
8589934592 2138 93.46915143698799
17179869184 2993 126.99959580531375
34359738368 2565 105.72893656800545
68719476736 2138 85.68005548390566
137438953472 2566 100.0528506735427
274877906944 2566 97.41988091897579
549755813888 6415 237.30483813596666
1099511627776 2138 77.11204993551509
2199023255552 2566 90.29159694929464
4398046511104 2138 73.44004755763342
8796093022208 2566 86.09198778886233
17592186044416 2566 84.13535170275182
35184372088832 2566 82.26567722046845
70368744177664 2566 80.47729293306696
140737488355328 2566 78.76501010470383
281474976710656 2565 77.09401624750399
562949953421312 2566 75.55011173308327
1125899906842624 2994 86.38857904843113
2251799813685248 2994 84.69468534159914
4503599627370496 2993 83.03819725732053
9007199254740992 2994 81.498659479652
18014398509481984 5132 137.10946203411407
36028797018963968 2994 78.53507186221012
72057594037927936 2993 77.10689745322621
144115188075855872 2993 75.7541448663275
288230376151711744 13257 329.7553130528446
576460752303423488 3421 83.65185991323972
1152921504606846976 2994 71.99048254035928
2305843009213693952 3421 80.9091759816581
4611686018427387904 3421 79.60418927227651

如您所见,即使对于 n = 2^62,执行时间也只有 3421 纳秒,这显然不是线性的。 executionTime/log(n) 的比率最终看起来相当稳定,约为 80,这使您的解决方案成为 O(n)

我认为我们一开始观察到的变化是由于斐波那契数列的第二项。确实,Fn = (phi^n - psi^n)/sqrt(5) with phi = (1 + sqrt(5))/2 &gt; 1 and psi = (1 - sqrt(5))/2 &lt; 1。当 n 较小时,psi^n 仍然会影响结果,但因为它小于 1,所以当 n 很大时它会变得非常小,这就是为什么它在结尾处取对数而不是在开头取对数。

【讨论】:

  • 是否有进一步优化的可能?
  • 我没有尝试过它的运行时分析。但是这个问题给了我时间,所以我认为它会是 o(n)....谢谢你的建议。我删除了 o(n)时间复杂度
【解决方案2】:

证明我们的目标值为 (n + k) 很简单,其中 Fk 是小于我们目标的最大斐波那契数。因此我们需要做的就是找到k

您可以使用二分搜索(使用矩阵乘法)在 O(log log n) 时间内完成此操作。

您可以在 O(1) 时间内完成此操作,方法是将 Fkclosed-form expression 反转为 k

【讨论】:

  • 您是如何提出第二个复杂性的?通过这样做,在我看来,您可以在 O(1) 中计算 Fn,但在恒定时间内找不到小于 n 的最大斐波那契数。我错过了什么吗?
  • @Dici:我没有真正考虑过,所以它可能是错误的。但我的假设是您可以反转表达式,并传入 n (然后将结果四舍五入)。这给了我们k',它在大多数情况下将等于所需的k。确实存在 Fk' n Fk (n+k) 的情况(即我们四舍五入到错误的斐波那契指数,因为 n )。但是,我们知道 F(k'+1) - Fk' >> k (一般而言)。所以我们最多需要检查Fk'F(k'+1)。所以还是 O(1)。
  • 我不确定,它确实适用于很少的代码,这是一个好主意,但评估 n 中的逆是有代价的(例如,不确定计算日志的复杂性是多少,特别是如果基数不是 2)。无论如何我+1你的数学思想:p
  • @Dici:嗯,这就进入了询问我们的机器模型是什么的领域。实际上,这里的基本操作(加法、乘法、对数)都不是字长不变的……
  • 对 :) 顺便说一句,我想问你一个简单的问题,你介意加入我的聊天室吗? chat.stackoverflow.com/rooms/84311/quick-chat
猜你喜欢
  • 2022-12-10
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-17
  • 1970-01-01
  • 1970-01-01
  • 2021-07-18
相关资源
最近更新 更多