O(1)求解自然数异或和

又是一个不眠之夜。

求:

[f_i=1 igoplus 2 igoplus 3 igoplus...igoplus (i-1) igoplus i ]

思路1:周期分析

(O(logn))算法

考虑按位分析

对于(f_i)的第(j)位,它的值只与该位1出现次数有关。

而第(j)位1的出现又是呈周期性分布的。

我们考虑(f_i=0 igoplus 1 igoplus 2 igoplus 3 igoplus...igoplus (i-1) igoplus i)

注意这里多加了一个0。

那么,在上式的各数中,第1位的变化为01010101

而第2位为00110011

第3位为00001111

以此类推。

周期分析

所以我们可以发现,第(j)位的值的出现是连续的,且每连续一组的相同值的个数为(2^{j-1}),这恰好是第(j)位的位权!

而对于数字的总个数,我们可以用(x=i+1)来表示。

分析第(j)位的值((j ge 2))

则第(j)位的出现的整组共有(t=lfloor{{x}over{2^{j-1}}} floor)个,其中奇数组为0,偶数组为1,且其中出现数字1的总个数必定为偶数。

(t)为奇数,说明剩余的不完整组的值为1,同时若(x)也为奇数,说明(f_i)的第(j)位为1;否则(f_i)的第(j)位为0。

由此,我们可以得到第(j)位的值((j ge 2))

对于第1位,它出现的组共有(x)个,其中值为1的有(lfloor{{x}over{2}} floor)个,故(f_i)的第1位等于(x)的第2位。

综上可以在(O(logn))时间复杂度内求解。

(O(1))算法

其实就是对(O(logn))的算法作了一个小的总结。

分析第(j)位的值((j ge 2))

我们知道,当且仅当(t = lfloor{{x}over{2^{j-1}}} floor)为奇数,同时(x)也为奇数时,第(j)位才为1;否则第(j)位为0。

体现在(x)这个数本身上,就是当(x)第1位为1时,(f_i)的第2位及以上与(x)的相同。

而当(x)第1位为0时,(f_i)的第2位及以上都为0。

然后第1位的特判很好处理,就是(f_i)的第1位等于(x)的第2位。

由此可以在(O(1))时间复杂度内求解。

代码实现

闲来无事写个代码(因为太菜所以不会更简单的写法

int xorsum(int x)
{
	++x;
	return ( (x&1) ? (x&(INT_MAX-1)) : 0 ) | ( (x&2) ? 1 : 0 );
}

数据检验

顺便学了一下二进制输出的方法

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<bitset>

using namespace std;

int xorsum(int x)
{
	++x;
	return ( (x&1) ? (x&(INT_MAX-1)) : 0 ) | ( (x&2) ? 1 : 0 );
}

int main()
{
	int n=10;
	for(int i=1;i<=n;++i)
	{
		cout<<"number:"<<bitset<sizeof(i)*8>(i)<<'
';
		cout<<"xorsum:"<<bitset<sizeof(i)*8>(xorsum(i))<<'
';
	}
	return 0;
}

输出如下:

思路2:数学化简

看了ltao思路和Limit给的正解,发现自己的做法实在是......太菜了

一个很明显的劣势在于:我们的周期性分析是以(x=i+1)算出总数字个数再来分析性质的。

这样的转义分析最大的缺点就在于,性质推广,或者说移植的时候,会将定义转来转去,非常难以处理。

所以在这里再整理一下(ltao)的思路:

核心性质

定义(g_{i,j}=i igoplus (i+1) igoplus ... igoplus j)

有性质:(g_{0,2^{k}-1} = g_{2^{k},2^{k+1}-1}(k ge 1)),即右式第(k+1)位全部被异或消掉

将左右两式异或可得:(g_{0,2^{k+1}-1} = g_{0,2^{k}-1} igoplus g_{2^{k},2^{k+1}-1} = 0)

得到核心性质:(g_{0,2^{k-1}-1} = 0(k ge 3)),即可以用这个性质消掉函数中第(k)位为0的所有数,注意这个(k)的边界很重要!

(O(logn))算法

有了这个性质,我们就可以很方便地对原式最高位进行化简,设所求函数(g_{0,i})(i)的最高位为(k),有:

(g_{0,i} = g_{0, 2^{k-1}-1} igoplus g_{2^{k-1},i} = g_{2^{k-1},i})

然后组成(g_{2^{k-1},i}) 的数的第(k)位都为1,且共有(m = i - 2^{k-1} + 1)个这样的数

这样就可以化简掉第(k)位:

(i)为奇数,则(m)为偶数,结果的第(k)位必然为0,即(g_{2^{k-1},i} = g_{0,i-2^{k-1}});

(i)为偶数,则(m)为奇数,结果的第(k)位必然为1,即(g_{2^{k-1},i} = g_{0,i-2^{k-1}} + 2^{k-1})

递归处理,至(k le 2),达到了我们上面所说性质的边界以外,特判即可。

由此可以在(O(logn))时间复杂度内求解。

(O(1))算法

然后再对这个算法作一个小的总结:

我们可以发现上式的(g_{0,i}-> g_{0,i-2^{k-1}})的过程当中,(i)的奇偶性始终不变。

因此只需一次分析起始状态(g_{0,i})

(i)为奇数,则(m)为偶数,结果的第3位及以上都为0。

(i)为偶数,则(m)为奇数,结果的第3位及以上与(i)的相同。

剩下两位特判即可。

由此可在(O(1))时间复杂度内完成求解。

代码实现

int f[4]={0,1,3,0};
int xorsum(int x)
{
	return ( (x&1) ? 0 : (x&(INT_MAX-3)) ) | f[x&3];
}

或者更直观的写法:

int xorsum(int x)
{
	switch(x&3)
	{
		case 0:
			return x;
		case 1:
			return 1;
		case 2:
			return x+1;
		case 3:
			return 0;
	}
}

非常重要的一点在于,这种直接将x&3的值和xorsum的值对应的直观函数表示,能够有效地解决一些扩展问题,有兴趣的可见这道基于Limit's idea的水题

原文地址:https://www.cnblogs.com/-SingerCoder/p/13171117.html