一種變進製數及其應用

2021-06-18 07:56:42 字數 4510 閱讀 2634

我們經常使用的數的進製為

「常數進製」,即

始終逢p進1

。例如,

p進製數

k可表示為

k = a0*p^0 + a1*p^1 + a2*p^2 + ... + an*p^n 

(其中0 <= ai <= p-1

),它可以表示任何乙個自然數。

對於這種常數進製表示法,以及各種進製之間的轉換大家應該是很熟悉的了,但大家可能很少聽說

變進製數。這裡我要介紹一種特殊的變進製數,它能夠被

用來實現全排列的hash函式,

並且該hash函式能夠實現完美的防碰撞和空間利用(不會發生碰撞,且所有空間被完全使用,不多不少)。這種全排列

hash

函式也被稱為全排列數化技術。下面,我們就來看看這種變進製數。

設從第n位到第1位進製依次是pn,pn-1...p2,p1,則任何乙個數可以表示為

下面證明上式的正確性:

假設變進製數的第m位am=pm,要進製,那麼

即正確的向高位進1。這說明該變進製數能夠正確進製,從而是一種合法的計數方式。

舉個例子:

假設從第4位到第1位進製依次是4,9,6,8,k=1024

則可表示成k=2*(9*6*8) + 3*(6*8)+2*8+0=2*432+3*48+2*8=1024

以下內容是**

我們考查這樣一種變進製數:第1位逢

2進1,第

2位逢3進

1,……,第n位逢

n+1進

1。它的表示形式為

k = a1*1

!+ a2*2

!+ a3*3

!+ ... + an*n

!(其中

0 <= ai <= i

),也可以擴充套件為如下形式(因為按定義

a0始終為

0),以與

p進製表示相對應:

k = a0*0

!+ a1*1

!+ a2*2

!+ a3*3

!+ ... + an*n

!(其中

0 <= ai <= i

)。(後面的變進製數均指這種變進製數,且採用前一種表示法)

先讓我們來考查一下該變進製數的進製是否正確。假設變進製數k的第

i位ai為

i+1,需要進製,而

ai*i

!=(i+1)*i

!=1*(i+1)

!,即正確的向高位進

1。這說明該變進製數能夠正確進製,從而是一種合法的計數方式。

接下來我們考查

n位變進製數

k的性質:(1

)當所有位

ai均為

i時,此時

k有最大值

max[k] = 1*1

!+ 2*2

!+ 3*3

!+ ... + n*n

!= 1

!+ 1*1

!+ 2*2

!+ 3*3

!+ ... + n*n

!- 1

= (1+1)*1

!+ 2*2

!+ 3*3

!+ ... + n*n

!- 1

= 2!

+ 2*2

!+ 3*3

!+ ... + n*n

!- 1

= ...

= (n+1)!-1

因此,n位k進製數的最大值為(n+1)!-1。(2

)當所有位

ai均為

0時,此時

k有最小值0

因此,n

位變進製數能夠表示0到

(n+1)!-1

的範圍內的所有自然數,

共(n+1)!個

在一些狀態空間搜尋演算法中,我們需要快速判斷某個狀態是否已經出現,此時常常使用

hash

函式來實現。其中,有一類特殊的狀態空間,它們是由全排列產生的,比如

n數碼問題。對於

n個元素的全排列,共產生

n!個不同的排列或狀態。下面將討論如何使用這裡的變進製數來實現乙個針對全排列的

hash

函式。從數的角度來看,全排列和變進製數都用到了階乘。如果我們能夠用0到

n!-1這

n!個連續的變進製數來表示

n個元素的所有排列,那麼就能夠把全排列完全地數化,建立起全排列和自然數之間一一對應的關係,也就實現了乙個完美的

hash

函式。那麼,我們的想法能否實現呢?答案是肯定的,下面將進行討論。

假設我們有b0,

b1,b2,

b3,...,bn共

n+1個不同的元素,並假設各元素之間有一種次序關係

b0。對它們進行全排列,共產生

(n+1)

!種不同的排列。對於產生的任一排列c0,

c1,c2,

..,cn,其中第

i個元素ci(

1 <= i <= n

)與它前面的

i個元素構成的

逆序對的個數為di

0 <= di <= i

),那麼我們得到乙個逆序數序列d1,d2,...,dn(0 <= di <= i)。這不就是前面的

n位變進製數的各個位麼?於是,我們用

n位變進製數

m來表示該排列:

m = d1*1! + d2*2! + ... + dn*n!

因此,每個排列都可以按這種方式表示成乙個n位變進製數。下面,我們來考查

n位變進製數能否與

n+1個元素的全排列建立起一一對應的關係。由於n

位變進製數能表示

(n+1)

!個不同的數,而

n+1個元素的全排列剛好有

(n+1)

!個不同的排列,且每乙個排列都已經能表示成乙個

n位變進製數。如果我們能夠證明任意兩個不同的排列產生兩個不同的變進製數,那麼我們就可以得出結論:

★ 定理1——n+1個元素的全排列的每乙個排列對應著乙個不同的n位變進製數。

對於全排列的任意兩個不同的排列p0,

p1,p2,

...,

pn(排列p)和

q0,q1,

q2,...,

qn(排列

q),從後往前查詢第乙個不相同的元素,分別記為pi和

qi(0 < i <= n)。(

1)如果

qi > pi

,那麼,

如果在排列q中

qi之前的元素x與

qi構成逆序對,即有

x > qi

,則在排列p中

pi之前也有相同元素

x > pi

(因為x > qi

且qi > pi

),即在排列p中

pi之前的元素x也與

pi構成逆序對,所以

pi的逆序數大於等於

qi的逆序數。又qi與

pi在排列

p中構成

pi的逆序對,所以

pi的逆序數大於

qi的逆序數。(2

)同理,如果

pi > qi

,那麼qi

的逆序數大於

pi的逆序數。

因此,由(

1)和(

2)知,排列

p和排列

q對應的變進製數至少有第

i位不相同,

即全排列的任意兩個不同的排列具有不同的變進製數。至此,定理

1得證。

計算n個元素的乙個排列的變進製數的演算法大致如下(時間複雜度為

o(n^2)):

int factorials[8] = ;  

template size_t permutationtonumber(const t permutation, int n)

// factorials[j]儲存著j!

result += count * factorials[j];

}return result;

}

說明:(1

)由於n

!是乙個很大的數,因此一般只能用於較小的n。

(2)有了計算排列的變進製數的演算法,我們就可以使用乙個大小為

n!的陣列來儲存每乙個排列的狀態,使用排列的變進製數作為陣列下標,從而實現狀態的快速檢索。如果只是標記狀態是否出現,則可以用一位來標記狀態。

一種隨機數生成演算法

隨機數生成類 class randnumber randnumber randnumber unsigned long s 0 else unsigned short randnumber random unsigned long n double randnumber frandom unsign...

回文數的另一種解法

題目描述 判斷乙個整數是否是回文數。回文數是指正序 從左向右 和倒序 從右向左 讀都是一樣的整數。示例 1 輸入 121 輸出 true 示例 2 輸入 121 輸出 false 解釋 從左向右讀,為 121 從右向左讀,為 121 因此它不是乙個回文數。示例 3 輸入 10 輸出 false 解釋...

親和數是一種古老的數

題目 實現100000以內的相親數對輸出 相親數 除自身以外的約數和,比如 220 1 2 4 5 10 11 20 22 44 55 110 284,284 1 2 4 71 142 220,所以220和284構成相親數對。輸出格式 從小到大每行一對相親數對,中間乙個空格隔開。看到這個題目,第乙個...