转载请注明出处,http://blog.youkuaiyun.com/Bule_Zst/article/details/77111983
问题描述:
任意一个整数x都可以写成一系列2的幂的组合,这里的组合是指用加减法把它们接连起来。令f(x)为x需要的最少的2的幂的项数,求f(x)。
如:
7=20+21+22
7=23−20
∴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 1
return 1 + min(f(x - l), f(u - x))
算法2:
观察算法1会发现,有很多的计算都是重复的
如对于对于求
f(23)
:
测试代码:
def floor2( x ):
a = 1
while a < x:
a = a * 2
if a == x:
return a
else:
return a / 2
def ceil2( x ):
a = 1
while a < x:
a = a * 2
return a
def f(x):
print x
if x == 0:
return 0
l = floor2(x)
u = ceil2(x)
if l == u:
return 1
return 1 + min(f(x - l), f(u - x))
print f( 23 )
因此可以使用一个数组存储已经计算过的结果:
def f(x):
if x == 0: return 0
if 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−floor2(x)=(0010110)2
ceil2(x)−x=(10000000)2−(1010110)2=(0101010)2=
~
x+1
我们会发现, x−floor2(x) 表示的是自身串从左往右数,第二个1之后的子串, ceil2(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 0
while 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
2017年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
以此类推
参考文章:
http://blog.youkuaiyun.com/howardemily/article/details/74938030
http://blog.youkuaiyun.com/howardemily/article/details/74938033