简体   繁体   English

寻找最接近的斐波那契数

[英]Finding the closest fibonacci numbers

I am trying to solve a bigger problem, and I think that an important part of the program is spent on inefficient computations. 我正在尝试解决更大的问题,并且我认为程序的重要部分花费在低效的计算上。

I need to compute for a given number N, the interval [P, Q], where P is the biggest fibonacci number that is <= to N, and Q is the smallest fibonacci number that is >= to N. 我需要计算给定数字N的间隔[P,Q],其中P是最大斐波纳契数,即<=到N,而Q是最小斐波纳契数,是> =到N。

Currently, I am using a map to record the value of the fibonacci numbers. 目前,我正在使用地图记录斐波那契数字的值。 A query normally involves searching all the fibonacci numbers up to N, and it is not very time efficient, as it involves a big number of comparisons. 查询通常涉及搜索直到N的所有斐波那契数,并且由于涉及大量比较,因此效率不高。

This type of queries will occur quite often in my program, and I am interested in ways that I could improve the lookup, preferably with sub-linear complexity. 这种查询会经常在我的程序中发生,并且我对可以改善查找(最好具有亚线性复杂度)的方式感兴趣。

The Fibonacci numbers are given by Binet's formula 斐波那契数由Binet公式给出

F(n) = ( phi^n - (1-phi)^n ) / \sqrt{5}

where phi is the golden ratio, phi是黄金分割率

phi = (1 + \sqrt{5}) / 2. 

This can be implemented straightforwardly (Python example): 这可以直接实现(Python示例):

<<fibonacci_binet.py>>=
phi = (1 + 5**0.5) / 2

def fib(n):
    return int(round((phi**n - (1-phi)**n) / 5**0.5))

Because of floating-point rounding errors, this will however only give the right result for n < 70 . 由于存在浮点舍入误差,因此对于n < 70而言,这只会给出正确的结果。

Binet's formula can be inverted by ignoring the (1-phi)^n term, which disappears for large n . 可以通过忽略(1-phi)^n项来反转Binet公式,该项在大n消失。 We can therefore define the inverse Fibonacci function that, when given F(n) , returns n (ignoring that F(1) = F(2) ): 因此,我们可以定义反斐波那契函数,当给定F(n) ,它返回n (忽略F(1) = F(2) ):

<<fibonacci_binet.py>>=
from math import log

def fibinv(f):
    if f < 2:
        return f
    return int(round(log(f * 5**0.5) / log(phi)))

Here rounding is used to our advantage: it removes the error introduced by our modification to Binet's formula. 在这里,四舍五入是我们的优势:它消除了我们对Binet公式的修改所引入的错误。 The function will in fact return the right answer when given any Fibonacci number that can be stored as an exact integer in the computer's memory. 实际上,如果给定任何可以作为精确整数存储在计算机内存中的斐波那契数,该函数将返回正确的答案。 On the other hand, it does not verify that the given number actually is a Fibonacci number; 另一方面,它不验证给定的数字实际上是斐波那契数; inputting a large Fibonacci number or any number close to it will give the same result. 输入大的斐波那契数或接近它的任何数将得到相同的结果。 Therefore you can use this idea to find the Fibonacci number closest to a given number. 因此,您可以使用此想法来找到最接近给定数字的斐波那契数。

The idea, then is to apply the inverse Fibonacci map to find N and M , the two closest Fibonacci numbers on either side, then use the direct Fibonacci map to compute P = F(N) and Q = F(M) . 然后,该想法是应用逆斐波那契图来查找NM ,即在任一侧上两个最接近的斐波那契数,然后使用直接斐波那契图来计算P = F(N)Q = F(M) This involves more computation, but less searching. 这涉及更多的计算,但搜索更少。

I posted a complete Proof-Of-Concept implementation of this on https://ideone.com/H6SAd 我在https://ideone.com/H6SAd上发布了完整的概念验证实现

  • it is blazingly fast 它非常快
  • it uses an adhoc binary search 它使用即席二进制搜索
  • Edit after reading the other responses, I have a feeling that mathematical ideas outlined there (PengOne) will lead to a quicker lookup (basically: a calculation of the inverted formula plus a floor()/ceil() call?) 阅读其他答复后进行编辑 ,我感觉在那里概述的数学思想(PengOne)将导致更快的查找(基本上是:计算反向公式再加上floor()/ ceil()调用?)

.

#include <cmath>
#include <iostream>

const double pheta = 0.5*(std::sqrt(5)+1);

double fib(unsigned int n)
{
    return (std::pow(pheta, n) - std::pow(1 - pheta, n)) / std::sqrt(5);
}

unsigned int fibo_lowerbound(double N, unsigned min=0, unsigned max=1000)
{
    unsigned newpivot = (min+max)/2;
    if (min==newpivot)
        return newpivot;

    if (fib(newpivot) <= N)
        return fibo_lowerbound(N, newpivot, max);
    else
        return fibo_lowerbound(N, min, newpivot);
}

std::pair<double, double> fibo_range(unsigned int n)
{
    unsigned int lbound = fibo_lowerbound(n);
    return std::make_pair(fib(lbound), fib(lbound+1));
}

void display(unsigned int n)
{
    std::pair<double, double> range = fibo_range(n);
    std::cout << "Fibonacci range wrapping " << n << " is "
              << "[" << (unsigned long long) range.first << ", " << (unsigned long long) range.second << "]"
              << std::endl;
}

int main()
{
    display(1044);
    display(8999913);
    display(7);
    display(67);
}

The output is: 输出为:

Fibonacci range wrapping 1044 is [987, 1597]
Fibonacci range wrapping 8999913 is [5702887, 9227465]
Fibonacci range wrapping 7 is [5, 8]
Fibonacci range wrapping 67 is [55, 89]

You can use the closed-form expression of the fibonacci numbers. 您可以使用斐波那契数字的闭式表达式

Since the second term in it is very small, you can approximate it with just the first term, so n can be found with base-golden ratio logarithm. 由于第二项非常小,您可以仅使用第一项对其进行近似,因此可以使用底黄金比率对数找到n

Use the closed form formula: http://en.wikipedia.org/wiki/Fibonacci_number#Closed-form_expression 使用封闭式公式: http : //en.wikipedia.org/wiki/Fibonacci_number#Closed-form_expression

Then binary search 然后二进制搜索

I just did a CodeChef puzzle that was this exact problem (http://www.codechef.com/problems/DPC204). 我只是做了一个CodeChef难题,而这正是这个确切的问题(http://www.codechef.com/problems/DPC204)。 I simply calculated the Fibonacci sequence from 0 to the end of the range, and counted how many were after the beginning of the range. 我只是简单地计算了从0到范围结束的斐波那契数列,并计算了范围开始之后的斐波那契数列。 My test for whatever their sample inputs were took 2.6M, and 0.00s, so the nieve solution is plenty fast enough. 无论他们输入什么样本,我的测试都花费了2.6M和0.00s,因此nieve解决方案足够快。

Basically, I made a big-unsigned-int class made of unsigned int[333] , and calculate two numbers per loop, to avoid swaps. 基本上,我制作了一个由unsigned int[333]组成的big-unsigned-int类,并为每个循环计算两个数字,以避免交换。

start with A=0,B=1;
A+=B;B+=A; 
now A==1,B==2, the next two Fib. numbers, with no swaps.
A+=B;B+=A; 
now A==3,B==5, the next two Fib. numbers, with no swaps.

It is slightly complicated by the fact you have to stop and check if the neither, one, or both numbers are in the range, but A 由于必须停止并检查两个,两个或两个数字均不在范围内,因此稍微有点复杂,但是A

My solution on CodeChef clocked in at 0.00 seconds, so I think this method ought to be fast enough, you just have to write a function that adds one uint[333] to another uint[333] (using all 32 bits, just chars for each decimal digit) 我在CodeChef上的解决方案的时钟为0.00秒,因此我认为该方法应该足够快,您只需要编写一个函数即可将一个uint[333]到另一个uint[333] (使用所有32位,仅用于chars每个十进制数字)

Since you consider only 64 bit integers, there are at most about 100 Fibonacci numbers to consider. 由于仅考虑64位整数,因此最多要考虑100个斐波纳契数。 You can precompute them using their definition F n = F n-1 + F n-2 . 您可以使用它们的定义F n = F n-1 + F n-2对其进行预计算。

Then precompute another table that maps the number of leading zero bits to an index in the table of Fibonacci numbers, to the first number with that many leading zero bits. 然后预先计算另一个表,该表将前导零位的数量映射到斐波纳契数表中的索引,再映射到具有那么多前导零位的第一个数字。

Now to find the interval use the number of leading zero bits of your number (this can be computed quickly as many processors have a special instruction for it) to find a starting point using the second table, and linearly search through the first table for the interval. 现在要找到时间间隔,请使用数字的前导零位的数目(由于许多处理器对此有特殊的指令,因此可以快速计算出该数目),使用第二个表查找起点,并在第一个表中线性搜索间隔。 Since there are at most two Fibonacci numbers between adjacent powers of two this takes at most 2 steps. 由于两个相邻幂之间最多存在两个斐波那契数,因此最多需要2个步骤。

This has the advantage that it only uses integer arithmetic, which is exact and tends to be faster than floating point computations. 这样做的好处是,它仅使用整数算法,该算法是精确的,并且比浮点计算要快。

On Scala find the closet Fibonaci number looks also very simple: 在Scala上,找到壁橱的斐波那契数字也非常简单:

val phi = (1 + sqrt(5))/2
def fib(n: Long): Long =  round( ( pow(phi,n) -  pow((1-phi),n) ) / sqrt(5)  )
def fibinv(f: Long): Long =  if (f < 2) f else round( log(f * sqrt(5) ) /log(phi))

Using the last form here for the inverse you can find the two indexes for the Fib numbers around the current number. 使用此处的最后一种形式进行逆运算,您可以在当前数字附近找到Fib数的两个索引。 http://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding http://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding

log(N * sqrt(5)) / log((1+sqrt(5))/2) should give you a number which is between the two integer indexes for P and Q . log(N * sqrt(5)) / log((1+sqrt(5))/2)应该为您提供一个介于PQ的两个整数索引之间的数字。 You can then used the closed-form (as shown in the other answers) to give the actual numbers P and Q . 然后,您可以使用闭合形式(如其他答案所示)给出实际的数字PQ

Note that you may be off by one depending on your initial Fib conditions. 请注意,根据您的初始Fib状况,您可能会相差一倍。

I think that an important part of the program is spent on inefficient computations. 我认为该程序的重​​要部分用于效率低下的计算。

Have you profiled your code? 您是否配置代码? As a general principle don't prematurely optimize; 作为一般原则,不要过早优化; measure what parts are slowing it down. 测量哪些零件在减慢速度。 That way when you try optimizing, you can tell if the optimizations helped or hurt (often a sounds-good optimization will make it run worse; as say the compiler will not be able to do its optimizations or you aren't able to use your cpu's registers/cache as optimally). 这样,当您尝试进行优化时,就可以判断出优化是有用还是有害(通常良好的优化会使它运行得更糟;例如,编译器将无法进行优化,或者您将无法使用优化最佳的cpu寄存器/缓存)。

If this is what's slowing you down, I would do similar to Peng's great solution but pre-computing all the Fib numbers up to your largest value and store them into an array indexed by the corresponding expoential (n) from the closed-form ( phi^**n - (1-phi)**n)/sqrt(5) . 如果这使您放慢了速度,那么我将执行类似于Peng的出色解决方案,但将所有Fib数预先计算为最大值,然后将它们存储到由闭式( phi^**n - (1-phi)**n)/sqrt(5) )对应的指数(n)索引的数组中phi^**n - (1-phi)**n)/sqrt(5) His method will miscompute Fib numbers for large n with floating point arithmetic; 他的方法将使用浮点算法对大n的Fib数进行错误计算; unless you use arbitrary high-precision (which is slow). 除非您使用任意高精度(这很慢)。 So your starting array is fib_array = [0,1,1,2,3,5,8,13,... ] . 因此,您的起始数组是fib_array = [0,1,1,2,3,5,8,13,... ] Then neglecting the small (1-phi)**n term, invert fib to find n (eg, Peng's fib_inv ), and take fib_array[n] as your first bound. 然后忽略小的(1-phi)**n项,将fib求反(找到,例如Peng的fib_inv ),并将fib_array[n]作为第一个边界。 If this bound is smaller (larger) than your value; 如果此界限小于(大于)您的值; you've found the lower (upper) bound, and so the other bound should be fib_array[n+1] ( fib_array[n-1] ). 您已经找到了下(上限)边界,因此另一个边界应该是fib_array[n+1]fib_array[n-1] )。
Or if you want to compute it use something from given a N that is better than Binet's formula. 或者,如果要计算它,则使用给定N的值比Binet公式更好。 http://en.literateprograms.org/Fibonacci_numbers_%28Python%29 http://en.literateprograms.org/Fibonacci_numbers_%28Python%29

Personally, I'd check to make sure that the second bound is on the opposite side of the term as the first bound (in rare cases where we shouldn't have neglected the (1-phi)**n term; you could possibly have do to another lookup seeing if the term is bounded by eg, fib_array[n+1] and fib_array[n+2] ). 就我个人而言,我要确保第二个边界与第一个边界在术语的另一侧(在极少数情况下,我们不应该忽略(1-phi)**n术语;您可能请参阅另一个查询,以查看该术语是否受到例如fib_array[n+1]fib_array[n+2] (This check may be redundant; but you'd have to prove that first, and the one extra comparison to be safe seems worth it in my book). (此检查可能是多余的;但是您必须首先证明这一点,为了安全起见,进行一次额外的比较在我的书中似乎是值得的)。

Build a table of Fibonacci numbers that will fit in 8 bytes; 建立一个可容纳8个字节的斐波那契数字表; there's only 94. That will save you calculating them through each iteration. 只有94。这将节省您在每次迭代中计算它们的时间。 There's no need for floating point math here. 此处无需进行浮点运算。

Then use a binary search to find the number below and above your number in the time. 然后使用二分查找法来找到该时间下方和上方的数字。 That will save you comparing all the numbers, and reduce your search to a constant search time. 这样可以节省比较所有数字的时间,并将搜索减少到恒定的搜索时间。

This meets your requirements, but note that your requirements do not specify what should be returned for N such that there is no Q in 64 bit integer space, ie N > 12,200,160,415,121,876,738. 这符合您的要求,但请注意,您的要求未指定N的返回值,以使64位整数空间中没有Q,即N> 12,200,160,415,121,876,738。 If you care about that, decide how you want to handle it. 如果您对此感到担心,请决定如何处理它。 :) :)

#include "stdint.h"
#include "stdio.h"
#include "stdlib.h"
#include "time.h"

/* build a table of all fibonacci numbers that fit in a uint64_t. */
static const int fibonacciCount = 94;
uint64_t fibonacciSequence[fibonacciCount];
static void precalc(void) {
    fibonacciSequence[0] = 0;
    fibonacciSequence[1] = 1;
    for (int i = 2; i < fibonacciCount; ++i) {
        fibonacciSequence[i] = fibonacciSequence[i-2] + fibonacciSequence[i-1];
    }
}

/* do a binary search for the Fibonacci numbers >= N and <= N */
static void find_closest_fibonacci(uint64_t N, uint64_t *P, uint64_t *Q) {
    int upper = fibonacciCount;
    int lower = 0;
    do {
        int mid = ((upper - lower) >> 1) + lower;
        uint64_t midValue = fibonacciSequence[mid];
        if ( midValue > N ) {
            upper = mid;
        } else if ( midValue < N ) {
            lower = mid + 1;
        } else {
            *P = fibonacciSequence[ mid ];
            *Q = fibonacciSequence[ mid ];
            return;
        }
    } while ( upper > lower );
    *P = fibonacciSequence[ lower - 1 ];
    *Q = fibonacciSequence[ lower ];
}

/* hacked together 64 bit random number generator,
 used just in tester only */
static uint64_t rand64(void) {
    /* totally flawed as a random number generator,
     but that's not the point here. */
    uint64_t v = 0;
    for (int i = 0; i < 8; ++i) {
        v = (v << 8) + (rand() % 256);
    }
    return v;
}

int main (int argc, const char * argv[]) {
    srand( (unsigned)time( NULL ) );

    precalc(); /* do this once only */

    uint64_t upperBound = fibonacciSequence[fibonacciCount - 1];
    printf( "Upper bound is %qu\n", upperBound );

    /* build a sample to run against the algorithm
     we favor mostly numbers below RAND_MAX, because
     if we test across all of UINT64_MAX the results are
     pretty boring. */
    static const int sampleCount = 100;
    static const int normalSampleCount = 90;
    uint64_t numbers[sampleCount];
    for (int i = 0; i < normalSampleCount; ++i) {
        numbers[i] = rand();
    }
    for (int i = normalSampleCount; i < sampleCount; ++i) {
        uint64_t number;
        do {
            number = rand64();
        } while ( number > upperBound );
        numbers[i] = number;
    }

    /* use described algorithm */
    for (int i = 0; i < 100; ++i) {
        uint64_t P;
        uint64_t Q;
        uint64_t N = numbers[i];
        find_closest_fibonacci(N, &P, &Q);
        printf( "%qu [%qu,%qu]\n", N, P, Q );
    }

    return 0;
}

Put whatever other algorithm you have in the same file, and run it against the same tester. 将您拥有的所有其他算法放在同一文件中,并在同一测试器上运行它。

A number is Fibonacci if and only if one or both of (5*n^2 + 4) or (5*n^2 – 4) is a perfect square. 当且仅当(5 * n ^ 2 + 4)或(5 * n ^ 2-4 – 4)中的一个或两个为完美平方时,数字才是斐波那契数。 I am using this premise to verify if the input number belongs to a fibonacci series or not. 我正在使用此前提来验证输入数字是否属于斐波那契数列。

#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

typedef struct node{

    int64_t value;
    struct node *next;

}Node;

Node *head ;

void readElements(int);
int isPerfectSquare(int64_t sqrValue);

int main(){

    int input_count , flag=0;
    Node *temp_node = NULL;
    int64_t sqrValue = 0;

    scanf("%d" , &input_count);

    if((input_count < 1 )||(input_count > 100000)){
        printf("Total number of Inputs out of Range ..!!\n");
        return 1;
    }

    readElements(input_count);

    /*Reading the elements from the list*/

    temp_node = head;

    while(temp_node != NULL){

        sqrValue = 5*pow(temp_node->value , 2);
        flag = (isPerfectSquare(sqrValue+4) || isPerfectSquare(sqrValue-4));

        if(flag == 1){
            printf("IsFibo\n");
        }
        else{
            printf("IsNotFibo\n");
        }

        temp_node = temp_node->next;

    }   



    return 0;

}


void readElements(int input_count){

    int temp = 0;
    int64_t val = 0;
    Node *temp_node =NULL , *cur = NULL;
    char b[20];


    while (temp < input_count) {

        scanf("%s" , b);
        val = atol(b);

        if(val < 0 || val >10000000000)
            continue;

        temp_node = (Node*) malloc(sizeof(Node));

        temp_node->value = val;
        temp_node->next = NULL;

        if(head == NULL){
            head = cur = temp_node;
        }
        else{
            cur->next = temp_node;
            cur = temp_node;
        }

        temp++;

    }

}

int isPerfectSquare(int64_t sqrValue){

    int64_t s = 0;

    s = sqrt(sqrValue);

    return(s*s == sqrValue);

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM