我们知道,每个非负十进制数可以用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。
答案 0 :(得分:4)
所以这只是一个算法的初步草图。它的作用是当上界本身是斐波那契数时,但我不确定如何使其适应一般上界。希望有人可以改进这一点。
一般的想法是研究Fibonacci编码的结构。以下是前几个数字:
0
1
10
100
101
1000
1001
1010
10000
10001
10010
10100
10101
100000
这些数字中的每个数字的不变量是从来没有一对连续的1。给定这个不变量,我们可以使用以下模式从一个数字增加到下一个数字:
这很重要的原因是属性(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的数量。诀窍如下 - 如果我们跟踪每个数字的数字
我们可以使用此信息再计算这两个信息。这是一个美丽的DP复发。最初,我们按如下方式播种。对于一位数,N(d)= 2且B(d)为1,因为对于一位数字,数字为0和1.对于两位数,N(d)= 3(只有一位两位数,10,两个一位数字0和1)和B(d)是2(一个从1,一个从10)。从那里,我们有那个
例如,我们得到以下内容:
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个问题。接下来的每一个都比之前更难,每个都使用之前的结果。
拨打此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];
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点相同的方式证明。
如果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)。
想象一下,我们有功能:
long long number_of_all_bits_in_sequence(long long M);
它计算由所有不大于M的数字创建的“自然数的斐波那契位序列”的长度。
使用此功能,我们可以使用二进制搜索来查找前N位中有多少个数字。
让我们创建函数来计算多少个数&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)。
我们可以轻微地修改kth_bit_equal_1,这样它也可以计算等于0的位。
答案 3 :(得分:0)
计算m,负责序列第(N + 1)位的数字。计算m对计数的贡献。
我们已经将问题减少到计算[1,m]范围内的一位数。在interval trees的样式中,将此范围划分为O(log N)个子范围,每个子范围都有一个关联的glob,如10100 ????匹配属于该范围的数字的表示。很容易计算前缀的贡献。
我们已经将问题减少到计算长度为k的所有Fibonacci字中的一位的总数T(k)(即,球的部分)。 T(k)由以下重复给出。
T(0) = 0
T(1) = 1
T(k) = T(k - 1) + T(k - 2) + F(k - 2)
Mathematica说有一个封闭的表单解决方案,但它看起来很糟糕,并且不需要这个 polylog(N)-time 算法。
答案 4 :(得分:0)
[编辑]:基本上我已经跟随了属性,对于要在斐波那契基数中表示的任何数字n
,我们可以将其分解为n = n - x
,其中x
是最大的斐波那契刚好小于n
。使用此属性,任何数字都可以以位形式中断。
第一步是找到十进制数,使Nth
位结束。
我们可以看到斐波纳契数F(n)
和F(n+1)
之间的所有数字都具有相同的位数。使用它,我们可以预先计算一个表并找到合适的数字。
假设您有D
位的十进制数Nth
。
现在,让X
成为小于或等于D
的最大斐波那契数。
要查找从1
到D
的所有数字的设置位,我们将其重新表示为......
X+0, X+1, X+2, .... X + D-X
。因此,所有X
最终都会被1
回复,我们已将问题分解为更小的子问题。也就是说,我们需要找到所有设置位,直到D-X
。我们继续这样做。使用相同的逻辑,我们可以构建一个表,该表具有适用于所有斐波那契数的设定位数(直到极限)。我们将使用此表来查找从1
到X
的设置位数。
所以,
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的数量由两部分组成:
F(n-1)
个这样的数字,因此F(n-1)
领先1个。F(n-1)
。因此,如果我们调用序列中的总位数但不包括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;