二冪拆分問題

2021-08-06 02:50:43 字數 3222 閱讀 6205

問題描述:

任意乙個整數x都可以寫成一系列2的冪的組合,這裡的組合是指用加減法把它們接連起來。令f(x)為x需要的最少的2的冪的項數,求f(x)。如:7

=20+

21+2

2 7=

23−2

0 ∴

f(7)

=2宣告:因為f

(x)=

f(−x

) , 所以後文一致假設x非負。另外令f(

0)=0

。下面介紹三種演算法,由易到難,且演算法之間存在聯絡。

演算法1:

令floor2(x)表示不大於x的最大2冪, ceil2(x)表示不小於x的最小2冪,最優的組合要麼是floor2(x)加上其它一些項,或者是ceil2(x)減去其它一些項。

於是可以得到求f(x)的乙個遞迴過程如下:

def

f(x):

if x == 0: return

0 l = floor2(x)

u = ceil2(x)

if l == u: return

1return

1 + min(f(x - l), f(u - x))

演算法2:

觀察演算法1會發現,有很多的計算都是重複的

如對於對於求f(

測試**:

def

floor2

( x ):

a = 1

while a < x:

a = a * 2

if a == x:

return a

else:

return a / 2

defceil2

( x ):

a = 1

while a < x:

a = a * 2

return a

deff

(x):

print x

if x == 0:

return

0 l = floor2(x)

u = ceil2(x)

if l == u:

return

1return

1 + min(f(x - l), f(u - x))

print f( 23 )

因此可以使用乙個陣列儲存已經計算過的結果:

def

f(x):

if x == 0: return

0if x in table: return table[x]

l = floor2(x)

u = ceil2(x)

if l == u: return

1 r = 1 + min(f(x - l), f(u - x))

table[x] = r

return r

演算法3:

演算法2是乙個從本身串層層向下遞推的過程,因此需要額外記憶體儲存結果,因此想到,可以改進演算法2為由下向上的過程。

分析演算法2的遞推過程:如x

=(1010110)2

x−floo

r2(x

)=(0010110)2

ceil2(x

)−x=

(10000000)2

−(1010110)2

=(0101010)2

= ~x+

1 我們會發現,x−

floo

r2(x

) 表示的是自身串從左往右數,第二個1之後的子串,ce

il2(

x)−x

表示的是自身串從左往右數,第乙個0之後的子串取反加1

因此,演算法3的思路就是,從右往左遍歷自身串,用u表示以1開頭的串的f(

x)值,用d表示以0開頭的串取反加一的f(

x)值從右往左掃,掃到1則更新u,掃到0則更新d

自身串最右邊剛開始的那些0是沒有用的,一直掃到1開始更新,u、d賦初值1

**中的》1不要當成除以2,而是看做二進位制字串的右移。&1 == 1表示最後一位是1.

def

f(x):

if x == 0: return

0while x & 1 == 0: x = x >> 1

u = 1

d = 1

x = x >> 1

while x > 0:

if x & 1 == 1:

u = min(u, d) + 1

else:

d = min(u, d) + 1

x = x >> 1

return u

2023年9月6日更新

今天重新看了這篇文章,發現自己竟然看不懂了,what?現在終於又懂了,再把思路重新梳理一下。

前兩個方法沒什麼好說的,關鍵是第三個方法,裡面的u與v。

u指的是當前二進位制字串(以1開頭)從左往右第二個1開頭的新串的f(

x)值,v指的是當前二進位制字串從左往右第乙個0開頭的新串取反加一得到的新新串的f(

x)值(f(

x)中的x指的是新串與新新串)

舉個例子就懂了,這個方法確實很神奇

1010110

初始,

u: 10: 1

v: 10: 1

ps:10表示代表的串,1表示需要二進位制數的個數

計算110,因為1開頭,所以更新u u=

u+1 : 100 + u 或u

=v+1

: 1000 - v

結果:u: 110: 2

計算1010(原串:0110),更新v v=

u+1 : 10000 - u 或v

=v+1

: 1000 + v

結果:v: 1010: 2

計算10110,更新u u=

u+1 : 10000 + u 或u

=v+1

: 100000 - v

結果:u: 10110: 3

計算101010(原串:010110),更新v v=

u+1 : 1000000 - u 或v

=v+1

: 100000 + v

以此類推

整數拆分問題

問題 對於1個正整數n,將其拆分成幾個正整數的和,如何拆分可使得其乘積最大?csdn使用者pathuang68給出的結論是 如果不在乎是否為整數的話,那麼把每份平均分為e 2.71828459045.時,所得到的乘積是最大的,如果要是整數的話,那麼就選盡可能地靠近e的整數即可,比如3。具體證明請參見...

硬幣拆分問題。

include using namespace std int main else cout x 2 y 1 endl return 0 1.這個硬幣拆分源自hdoj1085題,有1 2 5,3種硬幣,x,y,z分別為其個數,首先x 0時,最小的即為1,其次x不等於0時,要找出5以內有個臨界點,當x...

快速冪問題

所謂的快速冪,實際上是快速冪取模的縮寫 首先,最基本的辦法是 int ans 1 for int i 1 i b i ans ans c ans是對answer的縮寫但是如果a很大,那麼a b的結果就容易非常大,所以在求之前可以先對a做乙個變化 如下 int ans 1 a a c 加上這一句 fo...