- 论坛徽章:
- 0
|
我这里给出一个解法。
我们知道,二进制数
((1<<n)-1)
的0、1是连续,不会出现0、1混合出现的情形。
将此数右移一位即为
((1<<(n-1))-1)
此两数相异或的结果就是(1<<(n-1))。
按题意,
A <= B = (1<<n) < 2A
则
A <= B = (1<<n) < 2A <= (1<<(n+1))
如果我们能够通过A构造出((1<<(n+1))-1),就可以将其右移一位而得到((1<<n)-1),再将这两个数想异或,就能得到(1<<n)了,即为所示之B。
按上式,(2A-1)肯定会小于(1<<(n+1)),但又会大于(1<<n),
所以,我们先求
B = (2A-1)
将B右移一位后再与B做或操作,则得到的B值就会更接近于((1<<(n+1))-1)。如果我们重复这样的步骤,就肯定能得到((1<<(n+1))-1)。
但要重复多少次呢?
假设最开初的B为(1<<n),则一次之后为3B/2,再一次后为7B/4,再一次后为15B/8……
很明显,数据有多少位,就要重复多少次。
按前假设,
第一次后数据至少有两位1相邻的,可以将得到的B右移两位再或到B上,就可以得到4位相邻的1了……
如此,若总共有数N位,则最多需要重复(1+log2(N))次就行了。
这样也存在问题,我们给出的数是没有限制的,但这个算法却需要限制。还请哪位给完善一下。
下面给出一个检测程序,
#!/usr/bin/perl -w
for ($n=1; $n<(1<<16); $n++)
{
printf "%016b %6d", $n, $n;
$_ = 2 * $n - 1;
printf "\t%016b %6d", $_, $_;
$_ = $_ | ($_ >> 1);
printf "\t%016b %6d", $_, $_;
$_ = $_ | ($_ >> 2);
printf "\t%016b %6d", $_, $_;
$_ = $_ | ($_ >> 4);
printf "\t%016b %6d", $_, $_;
$_ = $_ | ($_ >> ;
printf "\t%016b %6d", $_, $_;
$_ = $_ ^ ($_ >> 1);
printf "\t%016b %6d", $_, $_;
$t=$_;
while ($t)
{
$_ = 1 & $t;
$t = $t >> 1;
if ($_ && $t)
{
print STDERR "!!!\t$n";
last;
}
}
print "\n";
}
[ 本帖最后由 wolfkin 于 2007-7-13 12:07 编辑 ] |
|