计算Fibonacci数系统中设置的位数?

时间:2012-03-30 12:59:25

标签: algorithm math numbers fibonacci

我们知道,每个非负十进制数可以用Fibonacci数的和来唯一表示(这里我们关注最小表示,即在数字的表示中没有连续的Fibonacci数,并且每个Fibonacci数也被采用表示中至多有一个。)

例如:

1->  1
2-> 10
3->100
4->101, here f1=1 , f2=2 and f(n)=f(n-1)+f(n-2);

因此每个十进制数可以在Fibonacci系统中表示为二进制序列。如果我们在Fibonacci系统中连续写出所有自然数,我们将获得如下序列:110100101 ...这被称为“自然数的Fibonacci比特序列”。

我的任务是计算第1位出现在此序列的前N位中的次数。由于N可以取值1到10 ^ 15,我可以不存储斐波纳契数列吗?

例如:如果N为5,答案为3。

8 个答案:

答案 0 :(得分:4)

所以这只是一个算法的初步草图。它的作用是当上界本身是斐波那契数时,但我不确定如何使其适应一般上界。希望有人可以改进这一点。

一般的想法是研究Fibonacci编码的结构。以下是前几个数字:

     0
     1
    10
   100
   101
  1000
  1001
  1010
 10000
 10001
 10010
 10100
 10101
100000

这些数字中的每个数字的不变量是从来没有一对连续的1。给定这个不变量,我们可以使用以下模式从一个数字增加到下一个数字:

  1. 如果最后一位为0,则将其设置为1.
  2. 如果最后一位是1,那么由于没有连续的1,所以将最后一位设为0,将下一位设为1.
  3. 通过将它们都设置为0并将下一个数字设置为1来消除任何加倍的1,重复直到所有加倍的1都被消除。
  4. 这很重要的原因是属性(3)告诉我们这些数字的结构。让我们再次重温前几个Fibonacci编码的数字。例如,看看前三个数字:

      00
      01
      10
    

    现在,查看所有四位数字:

    1000
    1001
    1010
    

    下一个号码将有五位数字,如下所示:

      

    1011→1100→10000

    需要注意的有趣细节是,四位数的数字等于最多两位数的数字。事实上,我们通过在最多两位数字前加10来获得四位数字。

    现在,看看三位数字:

    000
    001
    010
    100
    101
    

    查看五位数字:

    10000
    10001
    10010
    10100
    10101
    

    请注意,五位数字只是带有10个前缀的三位数字。

    这给了我们一个非常有趣的方法来计算有多少1。具体来说,如果你看(k + 2) - 数字,它们中的每一个都只是一个带有10个前缀的k位数字。这意味着如果所有k位数字中总共有B 1,那么只有k + 2位数的Bs总数等于B加上k位数字的数量,因为我们只是重播序列,每个数字前加一个额外的1。

    我们可以利用它来计算Fibonacci编码中最多有k个数字的1的数量。诀窍如下 - 如果我们跟踪每个数字的数字

    1. 最多有多少个数字(称为N(d))和
    2. 多少个1代表最多d位的数字(称为B(d))。
    3. 我们可以使用此信息再计算这两个信息。这是一个美丽的DP复发。最初,我们按如下方式播种。对于一位数,N(d)= 2且B(d)为1,因为对于一位数字,数字为0和1.对于两位数,N(d)= 3(只有一位两位数,10,两个一位数字0和1)和B(d)是2(一个从1,一个从10)。从那里,我们有那个

      • N(d + 2)= N(d)+ N(d + 1)。这是因为最多d + 2位数的数字是最多d + 1位数(N(d + 1))的数字,加上前缀10到d数字的数字(N( d))
      • B(d + 2)= B(d + 1)+ B(d)+ N(d)(最多d + 2的长度数中的总1位数是1位的总数长度最多为d + 1,加上我们从d + 2位数得到的额外数字)

      例如,我们得到以下内容:

       d     N(d)      B(d)
      ---------------------
       1       2          1
       2       3          2
       3       5          5
       4       8         10
       5      13         20
      

      我们实际上可以检查一下。对于1位数字,总共使用1位。对于2位数字,有两个(1和10)。对于3位数字,有5个1(1,10,100,101)。对于四位数字,有10个(前五个,加上1000,1001,1010)。向外延伸可以得到我们想要的序列。

      这非常容易计算 - 如果我们重用之前的空间,我们可以使用O(1)内存使用来计算时间O(k)中k个数字的值。由于Fibonacci数迅速呈指数增长,这意味着如果我们有一些数N并想要找到所有1位的总和到最小的Fibonacci数小于N,我们可以在时间O(log N)和空间O中这样做(1)。

      那就是说,我不知道如何调整它以适应一般的上限。但是,我很乐观有一些方法可以做到这一点。这是一个美丽的复发,只需要一个很好的方法来概括它。

      希望这有帮助!谢谢你有一个很棒的问题!

答案 1 :(得分:1)

以免解决3个问题。接下来的每一个都比之前更难,每个都使用之前的结果。

1。如果你写下从0到fib [i] -1的每个数字,会设置多少个。

拨打此dp[i]。让我们看一下数字

     0
     1
    10
   100
   101
  1000
  1001
  1010 <-- we want to count ones up to here
 10000

如果您将所有数字写入fib [i] -1,首先将所有数字写入fib [i-1] -1(dp [i-1]),然后编写最后一个数字块。这些数字中只有fib [i-2],第一个位置各有一个,所以我们加上fib [i-2],如果你删除那些

000
001
010

然后删除前导零,你可以看到写下从0到fib [i-2] -1的每个数字。一个数字等于dp [i-2],这给了我们:

dp[i] = fib[i-2] + dp[i-2] + dp[i-1];

2。如果你写下从0到n的每个数字,设置了多少个。

     0
     1
    10
   100
   101
  1000
  1001 <-- we want to count ones up to here
  1010 

让我们称之为solNumber(n)

假设你的数字是f [i] + x,其中f [i]是最大可能的斐波纳契数。然后anser如果dp [i] + solNumber(x)。这可以用与第1点相同的方式证明。

3。前n位数设置了多少个。

3A。有多少个数字的表示长度恰好为l

如果l = 1,则答案为1,否则其fib [l-2] + 1。 你可以注意到,如果你删除前导零,然后是所有前导零,你将每个数字从0到fib [l-1] -1。完全是[1]号。

// 3a结束

现在你可以找到这样的数字m,如果你写下从1到m的所有数字,它们的总长度将是&lt; = n。但是如果你从1到m + 1写全部,则总长度将> ñ。手动解决问题m + 1并添加solNumber(m)。

所有3个问题都在 O(log n)

中解决
#include <iostream>

using namespace std;

#define FOR(i, a, b) for(int i = a; i < b; ++i)
#define RFOR(i, b, a) for(int i = b - 1; i >= a; --i)
#define REP(i, N) FOR(i, 0, N)
#define RREP(i, N) RFOR(i, N, 0)

typedef long long Long;


const int MAXL = 30;



long long fib[MAXL];

//How much ones are if you write down the representation of first fib[i]-1 natural numbers
long long dp[MAXL];

void buildDP()
{
    fib[0] = 1;
    fib[1] = 1;
    FOR(i,2,MAXL)
        fib[i] = fib[i-1] + fib[i-2];


    dp[0] = 0;
    dp[1] = 0;
    dp[2] = 1;

    FOR(i,3,MAXL)
        dp[i] = fib[i-2] + dp[i-2] + dp[i-1];
}

//How much ones are if you write down the representation of first n natural numbers
Long solNumber(Long n)
{   
    if(n == 0)
        return n;
    Long res = 0;
    RREP(i,MAXL)
        if(n>=fib[i])
        {           
            n -= fib[i];
            res += dp[i];
            res += (n+1);
        }
    return res;
}

int solManual(Long num, Long n)
{
    int cr = 0;

    RREP(i,MAXL)
    {
        if(n == 0)
            break;

        if(num>=fib[i])
        {
            num -= fib[i];
            ++cr;
        }

        if(cr != 0)
            --n;
    }
    return cr;
}

Long num(int l)
{
    if(l<=2)
        return 1;
    return fib[l-1];
}

Long sol(Long n)
{
    //length of fibonacci representation
    int l = 1;
    //totatl acumulated length
    int cl = 0;
    while(num(l)*l + cl <= n)
    {
        cl += num(l)*l;
        ++l;
    }   
    //Number of digits, that represent numbers with maxlength
    Long nn = n - cl;

    //Number of full numbers;
    Long t = nn/l;

    //The last full number
    n = fib[l] + t-1;

    return solNumber(n) + solManual(n+1, nn%l);



}

int main(int argc, char** argv)
{
    ios_base::sync_with_stdio(false);
    buildDP();

    Long n;
    while(cin>>n)
        cout<<"ANS: "<<sol(n)<<endl;
    return 0;
}

答案 2 :(得分:0)

这是O((log n)^ 3)。

让我们计算出前N位中有多少个数

想象一下,我们有功能:

long long number_of_all_bits_in_sequence(long long M); 

它计算由所有不大于M的数字创建的“自然数的斐波那契位序列”的长度。

使用此功能,我们可以使用二进制搜索来查找前N位中有多少个数字。

在前M个数字

的表示中有多少位是1

让我们创建函数来计算多少个数&lt; = M在第k位有1个。

long long kth_bit_equal_1(long long M, int k);

首先让所有小值预处理此函数的结果,假设M <= 1000000.

M的实施&gt; PREPROCESS_LIMIT:

long long kth_bit_equal_1(long long M, int k) {
   if (M <= PREPROCESS_LIMIT) return preprocess_result[M][k];

   long long fib_number = greatest_fib_which_isnt_greater_than(M);
   int fib_index = index_of_fib_in_fibonnaci_sequence(fib);

   if (fib_index < k) {
      // all numbers are smaller than k-th fibbonacci number
      return 0;
   }

   if (fib_index == k) {
      // only numbers between [fib_number, M] have k-th bit set to 1
      return M - fib_number + 1;       
   } 

   if (fib_index > k) {
      long long result = 0;

      // all numbers between [fib_number, M] have bit at fib_index set to 1
      // so lets subtrack fib_number from all numbers in this interval
      // now this interval is [0, M - fib_number]
      // lets calculate how many numbers in this inteval have k-th bit set.
      result += kth_bit_equal_1(M - fib_number, k);

      // don't forget about remaining numbers (interval [1, fib_number - 1])
      result += kth_bit_equal_1(fib_number - 1, k);

      return result;
   }
}

此功能的复杂性为O(M / PREPROCESS_LIMIT)。

请注意,在reccurence中,其中一个加数始终是fibbonaci数字之一。

kth_bit_equal_1(fib_number - 1, k);

因此,如果我们记住所有计算结果,那么复杂度将提高到T(N)= T(N / 2)+ O(1)。 T(n)= O(log N)。

让我们回到number_of_all_bits_in_sequence

我们可以轻微地修改kth_bit_equal_1,这样它也可以计算等于0的位。

答案 3 :(得分:0)

  1. 计算m,负责序列第(N + 1)位的数字。计算m对计数的贡献。

  2. 我们已经将问题减少到计算[1,m]范围内的一位数。在interval trees的样式中,将此范围划分为O(log N)个子范围,每个子范围都有一个关联的glob,如10100 ????匹配属于该范围的数字的表示。很容易计算前缀的贡献。

  3. 我们已经将问题减少到计算长度为k的所有Fibonacci字中的一位的总数T(k)(即,球的部分)。 T(k)由以下重复给出。

    T(0) = 0
    T(1) = 1
    T(k) = T(k - 1) + T(k - 2) + F(k - 2)
    
  4. Mathematica说有一个封闭的表单解决方案,但它看起来很糟糕,并且不需要这个 polylog(N)-time 算法。

答案 4 :(得分:0)

[编辑]:基本上我已经跟随了属性,对于要在斐波那契基数中表示的任何数字n,我们可以将其分解为n = n - x,其中x是最大的斐波那契刚好小于n。使用此属性,任何数字都可以以位形式中断。

第一步是找到十进制数,使Nth位结束。 我们可以看到斐波纳契数F(n)F(n+1)之间的所有数字都具有相同的位数。使用它,我们可以预先计算一个表并找到合适的数字。

假设您有D位的十进制数Nth。 现在,让X成为小于或等于D的最大斐波那契数。 要查找从1D的所有数字的设置位,我们将其重新表示为...... X+0, X+1, X+2, .... X + D-X。因此,所有X最终都会被1回复,我们已将问题分解为更小的子问题。也就是说,我们需要找到所有设置位,直到D-X。我们继续这样做。使用相同的逻辑,我们可以构建一个表,该表具有适用于所有斐波那契数的设定位数(直到极限)。我们将使用此表来查找从1X的设置位数。 所以,

Findsetbits(D) { // finds number of set bits from 1 to D.
find X; // largest fibonacci number just less than D
ans = tablesetbits[X];
ans += 1 * (D-x+1); // All 1s at the end due to X+0,X+1,...
ans += Findsetbits(D-x); 
return ans;
}

我手工尝试了一些例子并看到了模式。

我编写了一个粗略的解决方案,我已经手动检查了N <= 35.对于大数字,它的工作速度非常快,但我无法确定它是否正确。如果是在线裁判问题,请提供链接。

#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;

#define pb push_back
typedef long long LL;
vector<LL>numbits;
vector<LL>fib;
vector<LL>numones;
vector<LL>cfones;

void init() {
    fib.pb(1);
    fib.pb(2);
    int i = 2;
    LL c = 1;
    while ( c < 100000000000000LL ) {
        c = fib[i-1] + fib[i-2];
        i++;
        fib.pb(c);
    }
}


   LL answer(LL n) {
    if (n <= 3) return n;
    int a = (lower_bound(fib.begin(),fib.end(),n))-fib.begin();
    int c = 1;
    if (fib[a] == n) {
        c = 0;
    }
    LL ans = cfones[a-1-c] ;
    return ans + answer(n - fib[a-c]) + 1 * (n - fib[a-c] + 1);
}
int fillarr(vector<int>& a, LL n) {
    if (n == 0)return -1;
    if (n == 1) {
        a[0] = 1;
        return 0;
    }
    int in = lower_bound(fib.begin(),fib.end(),n) - fib.begin(),v=0;
    if (fib[in] != n) v = 1;
    LL c = n - fib[in-v];
    a[in-v] = 1;
    fillarr(a, c);
    return in-v;
}
int main() {
    init();
    numbits.pb(1);
    int b = 2;
    LL c;
    for (int i = 1; i < fib.size()-2; i++) {
        c = fib[i+1] - fib[i] ;
        c = c*(LL)b;
        b++;
        numbits.pb(c);
    }
    for (int i = 1; i < numbits.size(); i++) {
        numbits[i] += numbits[i-1];
    }
    numones.pb(1);
    cfones.pb(1);
    numones.pb(1);
    cfones.pb(2);
    numones.pb(1);
    cfones.pb(5);
    for (int i = 3; i < fib.size(); i++ ) {
        LL c = 0;
        c += cfones[i-2]+ 1 * fib[i-1];
        numones.pb(c);
        cfones.pb(c + cfones[i-1]);
    }
    for (int i = 1; i < numones.size(); i++) {
        numones[i] += numones[i-1];
    }
    LL N;
    cin>>N;
    if (N == 1) {
        cout<<1<<"\n";
        return 0;
    }
    // find the integer just before Nth bit
    int pos;
    for (int i = 0;; i++) {
        if (numbits[i] >= N) {
            pos = i;
            break;
        }
    }

    LL temp = (N-numbits[pos-1])/(pos+1);
    LL temp1 = (N-numbits[pos-1]);
    LL num = fib[pos]-1 + (temp1>0?temp+(temp1%(pos+1)?1:0):0);
    temp1 -= temp*(pos+1);
    if(!temp1) temp1 = pos+1;
    vector<int>arr(70,0);
    int in = fillarr(arr, num);
    int sub = 0;
    for (int i = in-(temp1); i >= 0; i--) {
        if (arr[i] == 1)
            sub += 1;
    }
    cout<<"\nNumber answer "<<num<<" "<<answer(num) - sub<<"\n";
    return 0;
}

答案 5 :(得分:0)

这不是一个完整的答案,但它确实概述了如何在不使用蛮力的情况下进行此计算。

Fn的斐波纳契表示为1,后跟n-1零。

对于Fn但不包括F(n+1)的数字,1的数量由两部分组成:

  1. F(n-1)个这样的数字,因此F(n-1)领先1个。
  2. 前导数字后面的二进制数字只是所有数字的二进制数字,但不包括F(n-1)
  3. 因此,如果我们调用序列中的总位数但不包括nth Fibonacci数an,那么我们有以下递归:

    a(n+1) = an + F(n-1) + a(n-1)
    

    您还可以轻松获取序列中的位数,最多为Fn

    如果需要k斐波纳契数来(但不能通过)N,那么你可以用上面的公式计算这些位,经过一些进一步的操作后,可以减少计算数量的问题其余序列中的位数。

答案 6 :(得分:0)

这是一种计算数字集中所有一位数的方法,直到给定的数字长度范围。在我看来,这似乎是解决方案的合理起点

考虑10位数。从写作开始;

0000000000

现在我们可以将这些零中的一些数转换成1,将最后一位数保持为0.根据具体情况考虑可能性。

0 只有一种方法可以选择其中0个为1。在这种情况下对1位求和得到0。

1 有{9选择1}方法将其中一个零转换为一个零。每个都贡献1。

2 有{8选择2}种方式将两个零变成一个零。每个都贡献2.

...

5 有{5选择5种方法将五个零变成一个零。这些中的每一个都为位数贡献了5个。

很容易将此视为平铺问题。 10个零的字符串是10x1板,我们想要用1x1正方形和2x1多米诺骨牌平铺。选择一些零作为一个零是与选择一些瓷砖成为多米诺骨牌相同。我的解决方案与Benjamin和Quinn的“真正重要的证明”中的身份4密切相关。

第二步现在尝试使用上述构造来解决原始问题

假设我们想要前100100010位中的一位(当然,该数字是Fibonacci表示)。首先计算所有方法的总和,用10xxxxx0中的零和1代替x。要过度补偿过度计数,请减去10xxx0的计数。继续过度计算和过度补偿的程序。

答案 7 :(得分:-1)

此问题具有动态解决方案,如下面的测试算法所示。 要牢记的一些要点,这在代码中很明显:

每个数字i的最佳解决方案将通过使用斐波那契数f获得,其中f == i   或者,当f小于i时,则必须为f且最大数n <= f:i = f + n。

请注意,fib序列会在整个算法中被记忆。

public static int[] fibonacciBitSequenceOfNaturalNumbers(int num) {
    int[] setBits = new int[num + 1];
    setBits[0] = 0;//anchor case of fib seq
    setBits[1] = 1;//anchor case of fib seq
    int a = 1, b = 1;//anchor case of fib seq
    for (int i = 2; i <= num; i++) {
        int c = b;
        while (c < i) {
            c = a + b;
            a = b;
            b = c;
        }//fib
        if (c == i) {
            setBits[i] = 1;
            continue;
        }
        c = a;
        int tmp = c;//to optimize further, make tmp the fib before a
        while (c + tmp != i) {
            tmp--;
        }
        setBits[i] = 1 + setBits[tmp];
    }//done

    return setBits;
}

测试:

 public static void main(String... args) {
    int[] arr = fibonacciBitSequenceOfNaturalNumbers(23);
    //print result
    for(int i=1; i<arr.length; i++)
        System.out.format("%d has %d%n", i, arr[i]);
  }

测试结果:我有x设置位

1 has 1
2 has 1
3 has 1
4 has 2
5 has 1
6 has 2
7 has 2
8 has 1
9 has 2
10 has 2
11 has 2
12 has 3
13 has 1
14 has 2
15 has 2
16 has 2
17 has 3
18 has 2
19 has 3
20 has 3
21 has 1
22 has 2
23 has 2

基于评论的编辑:

//to return total number of set between 1 and n inclusive
//instead of returning as in original post, replace with this code

            int total = 0;
            for(int i: setBits)
                total+=i;
            return total;