O(1)求解自然數異或和


又是一個不眠之夜。

求:

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

思路1:周期分析

\(O(logn)\)算法

考慮按位分析

對於\(f_i\)的第\(j\)位,它的值只與該位1出現次數有關。

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

我們考慮\(f_i=0 \bigoplus 1 \bigoplus 2 \bigoplus 3 \bigoplus...\bigoplus (i-1) \bigoplus 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}}}\rfloor\)個,其中奇數組為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}}\rfloor\)個,故\(f_i\)的第1位等於\(x\)的第2位。

綜上可以在\(O(logn)\)時間復雜度內求解。

\(O(1)\)算法

其實就是對\(O(logn)\)的算法作了一個小的總結。

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

我們知道,當且僅當\(t = \lfloor{{x}\over{2^{j-1}}}\rfloor\)為奇數,同時\(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)<<'\n';
		cout<<"xorsum:"<<bitset<sizeof(i)*8>(xorsum(i))<<'\n';
	}
	return 0;
}

輸出如下:

思路2:數學化簡

看了ltao思路和Limit給的正解,發現自己的做法實在是......太菜了

一個很明顯的劣勢在於:我們的周期性分析是以\(x=i+1\)算出總數字個數再來分析性質的。

這樣的轉義分析最大的缺點就在於,性質推廣,或者說移植的時候,會將定義轉來轉去,非常難以處理。

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

核心性質

定義\(g_{i,j}=i \bigoplus (i+1) \bigoplus ... \bigoplus 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} \bigoplus 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} \bigoplus 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的水題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM