免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3022 | 回复: 9
打印 上一主题 下一主题

给定自然数 A,试求一自然数 B,以满足等式 A <= B = (1<<n) < 2A [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-11 14:11 |只看该作者 |倒序浏览
给定一自然数 A,试求一自然数 B,以满足等式
A <= B = (1<<n) < 2A
n 亦为自然数。

请给出你认为最简单的方法及代码。

论坛徽章:
0
2 [报告]
发表于 2007-07-11 15:21 |只看该作者

回复 #1 wolfkin 的帖子

#include <stdio.h>
int main()
{
        int A , tempA ;
        int n = 0 ;
        scanf( "%d" , &A ) ;
        tempA = A ;
        for( ; A ; A/=2 )
                n++ ;
        if( (1<<n) == 2*tempA )
                n-- ;
        printf( "B = %d\n" , 1<<n ) ;
        exit(0) ;
}

[ 本帖最后由 sewenew 于 2007-7-11 16:57 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2007-07-11 16:59 |只看该作者
#!perl
use strict ;
my $A ;
chomp( $A = <STDIN> ) ;
my $tempA = $A ;
my $n ;
while( $A )
{
        $n++ ;
        $A/=2 ;
        $A =~ s/\..*// ;
}
$n-- if( (1<<$n) == 2 * $tempA ) ;
print 1<<$n ;

论坛徽章:
0
4 [报告]
发表于 2007-07-11 17:10 |只看该作者

  1. sub Fun {
  2.         my $min = shift;
  3.         my $max = $min << 1;
  4.         my $res = 1;
  5.         $res <<= 1 while ( ( $min >>= 1 ) != 1 );
  6.         $res <<= 2;
  7.         $res == $max ? $res >> 1 : $res;
  8. }

  9. print Fun($A);
复制代码

论坛徽章:
0
5 [报告]
发表于 2007-07-11 17:11 |只看该作者
o,和楼上的一样了。:)

论坛徽章:
0
6 [报告]
发表于 2007-07-12 03:38 |只看该作者

  1. sub func {
  2.     my $n = shift;
  3.     my $result = 1;
  4.     $result <<= 1 until $result >= $n;
  5.     return $result;
  6. }
复制代码

[ 本帖最后由 shhgs 于 2007-7-12 19:49 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2007-07-12 03:47 |只看该作者
如果是人思考的话,很简单,把数字转换成两进制的,看是不是2的n次方。是的话返回,不是的话,只保留第一个1,后面全部修改成 0,然后补一个零返回。

如果知道这个数的区间,找一个掩码与一下最快。不过现在不知道,所以直接移位了。

[ 本帖最后由 shhgs 于 2007-7-12 03:53 编辑 ]

论坛徽章:
0
8 [报告]
发表于 2007-07-12 09:51 |只看该作者
原帖由 shhgs 于 2007-7-12 04:38 发表

sub func {
    my $n = shift;
    my $result = 1;
    while ( $result < $n) {
       $result = $result  


我怎么会想到把原数除2..直接这样乘就行了.

论坛徽章:
0
9 [报告]
发表于 2007-07-12 10:05 |只看该作者
原帖由 shhgs 于 2007-7-12 04:47 发表
如果是人思考的话,很简单,把数字转换成两进制的,看是不是2的n次方。是的话返回,不是的话,只保留第一个1,后面全部修改成 0,然后补一个零返回。

如果知道这个数的区间,找一个掩码与一下最快。不过现在不 ...

甚是,改写一下

  1. my $result = oct('0b1'.('0' x (length sprintf "%b", $num)));

  2. print $result == $num << 1 ? $result >> 1 : $result;
复制代码

论坛徽章:
0
10 [报告]
发表于 2007-07-12 15:52 |只看该作者
我这里给出一个解法。

我们知道,二进制数
    ((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 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP