一个汉明重量题目的延伸,讨论一下

PcrazyC 2007-10-20 05:08:18
当然对于TheBeet来说,数1个数字的二进制形式太没有挑战性了。最近TheBeet想到了一个问题来挑战自己——数出一个从0, 1, 2, .. , n的序列中数字2进制表示后1的个数。TheBeet想了很久也没想出来,于是他找到了您来帮忙。

Input

输入包含多个测试数据。
每个测试数据为一行,每行包括一个非负十进制整数n(n < 2^63)。
输入数据以-1结束。

Output

对于每个测试点,先输出"Case #:",#代表第几个测试点,然后在下一行输出对应数列二进制表示后1的个数。

Sample Input


5
1164
20071010
-1

Sample Output


Case 1:
7
Case 2:
5744
Case 3:
239720074



这个题目给大家讨论一下,做出来了我给你们验证,我这有正确的程序,要注意效率哦,从0到2^63次方

只要算法正确即可,一些ACM格式之类的都可省略
...全文
167 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
blue_zyb 2007-10-21
  • 打赏
  • 举报
回复
怪了,你是说,
printf("%d\n", GetOnes(5));
printf("%d\n", GetOnes(1164));
printf("%d\n", GetOnes(20071010));
得到的结果不是
7
5744
239720074

??


PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
一点注释都没有,还真看不出来你是怎么做的,只知道GetArrayIndex的意图
PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
我把你的程序改了一点,但没有改变算法方面的内容,在DEV_C++上运行得到的结果不是和我的一样,不知道怎么回事,下面是我改后的程序



#include<stdio.h>
#include<conio.h>

long long a[64];

void init()
{
long long pwOf2;
long long i;
a[0] = 0;
for (i = 1, pwOf2 = 1; i <= 63; i++, pwOf2 *= 2)
{
a[i] = a[i-1] * 2 + pwOf2;
}
}

int GetArrayIndex(long long x)
{
int i = 0;
x >>= 1;

while ( x!=0)
{
x >>= 1;
i++;
}
return i;
}

long long GetOnes(long long x)
{
if (x == 0)
return 0;
if (x == 1)
return 1;
else
{
int index = GetArrayIndex(x);
long long near2 = (long long)1 << index;
return a[index] + (x - near2 + 1) + GetOnes(x - near2);
}
}

int main()
{
printf("%d\n", GetOnes(5));
printf("%d\n", GetOnes(1164));
printf("%d\n", GetOnes(20071010));
getch();
return 0;
}
PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
我写了一下,好像可以解决了,如果还有问题我也不想追究了,只是随便写了下,结果应该是正确的(2^63-1这个数除外,我的程序会溢出)



#include<stdio.h>
#include<math.h>

int number(__int64 a)
{
int s=0;
while(a!=0)
{
a=a>>1;
s++;
}
return s;
}

int num(__int64 a)
{
int s=0;
while(a)
{
a/=10;
s++;
};
return s;
}

int main()
{
__int64 a,b,t,v,s[2];
int i,j,k=1;
while(scanf("%I64d",&a)&&a!=-1)
{
printf("Case %d:\n",k++);
if(a==0)
{
printf("0\n");
continue;
}
s[0]=0;
s[1]=0;
j=number(a);
for(i=1;i<=j;i++)
{
t=(a+1)%(__int64)pow(2,i);
v=t>(__int64)pow(2,i-1)?t-(__int64)pow(2,i-1):0;
b=(a+1)/(__int64)pow(2,i)*(__int64)pow(2,i-1)+v;
s[0]+=b/(__int64)pow(10,12);
s[1]+=b%(__int64)pow(10,12);
s[0]+=s[1]/(__int64)pow(10,12);
s[1]%=(__int64)pow(10,12);
}


if(s[0]!=0)
{
printf("%I64d",s[0]);
i=num(s[1]);
for(j=0;j<12-i;j++)
printf("0");
}
printf("%I64d\n",s[1]);
}
return 0;
}

blue_zyb 2007-10-21
  • 打赏
  • 举报
回复
很奇怪,我刚在VC下试就不会出现除0崩溃,我上面说的crash是在VS2003中出现的。。。

“你是在DOS下输入这个数的吧,编译器会把0赋值给A的,遇到X就终止了,编译器才不会认为这是一个十六进制数的”
--------------------------------------------------------------------------
这个你也太想当然了吧,我才不会犯这么低级的错误呢。。。

由于溢出了8字节int的范围就要考虑自己实现“大整数”了,由于重要的是解决问题的算法思想,这个大整数我就暂时不想实现了。。。
PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
感觉只能把这个数拆分了,用两个数组来组成,输出的时候先输出高位的,再输出低位的,都试试看
PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
好像是当这个数接近2^63的时候,结果会超过__int64的范围,出现溢出,看有没有办法能够保存这个数据


我的用0x80000000可以吧,你是在DOS下输入这个数的吧,编译器会把0赋值给A的,遇到X就终止了,编译器才不会认为这是一个十六进制数的
blue_zyb 2007-10-21
  • 打赏
  • 举报
回复
其实也不会出现溢出.看看下面的程序,在VC 6.0下可以运行
--------------------------------------------------
那是因为你的测试数据比2^63还要小的多。。。
我构造a函数的时候就得到负值了,明显溢出了吗。

另外,lz的代码我直接用a = 0x1000000000000000(2^60);测试,程序就crash了,遇到除0错误。。。

int main()
{
__int64 a,s,t,v;
int i,j,k=1;

a = 0x1000000000000000;
{
s=0;
j=number(a);
for(i=1;i<=j;i++)
{
t=(a+1)%(__int64)pow(2,i);
v=t>(__int64)pow(2,i-1)?t-(__int64)pow(2,i-1):0;
s+=(a+1)/(__int64)pow(2,i)*(__int64)pow(2,i-1)+v;
}

printf("Case %d:\n%I64d\n",k++,s);
}
return 0;
}


后来又用0x80000000测试,也除0错误crash了。
不知道为啥,lz再看看?
PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
感觉你的算法和我的差不多,只不过你用的是递归,而我用的是非递归的



#include<stdio.h>
#include<math.h>

int number(__int64 a)
{
int s=0;
while(a!=0)
{
a=a>>1;
s++;
}
return s;
}

int main()
{
__int64 a,s,t,v;
int i,j,k=1;
while(scanf("%I64d",&a)&&a!=-1)
{
s=0;
j=number(a);
for(i=1;i<=j;i++)
{
t=(a+1)%(__int64)pow(2,i);
v=t>(__int64)pow(2,i-1)?t-(__int64)pow(2,i-1):0;
s+=(a+1)/(__int64)pow(2,i)*(__int64)pow(2,i-1)+v;
}

printf("Case %d:\n%I64d\n",k++,s);
}
return 0;
}

PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
哦,是我搞丢了,好像没有错

其实也不会出现溢出.看看下面的程序,在VC 6.0下可以运行


#include<stdio.h>
#include<conio.h>

__int64 a[64];

void init()
{
__int64 pwOf2;
__int64 i;
a[0] = 0;
for (i = 1, pwOf2 = 1; i <= 63; i++, pwOf2 *= 2)
{
a[i] = a[i-1] * 2 + pwOf2;
}
}

int GetArrayIndex(__int64 x)
{
int i = 0;
x >>= 1;

while ( x!=0)
{
x >>= 1;
i++;
}
return i;
}

__int64 GetOnes(__int64 x)
{
if (x == 0)
return 0;
if (x == 1)
return 1;
else
{
int index = GetArrayIndex(x);
__int64 near2 = (__int64)1 << index;
return a[index] + (x - near2 + 1) + GetOnes(x - near2);
}
}

int main()
{
init();
printf("%d\n", GetOnes(5));
printf("%d\n", GetOnes(1164));
printf("%d\n", GetOnes(20071010));
printf("%I64d\n",GetOnes(123456789123456789));
getch();
return 0;
}




结果:(正确的)

7
5744
239720074
3486030038228980373
blue_zyb 2007-10-21
  • 打赏
  • 举报
回复
啊,我发现了,你的main函数里面没有初始化a数组,我后来不是补上了吗。。。

这样:
int main()
{
init();
printf("%d\n", GetOnes(5));
printf("%d\n", GetOnes(1164));
printf("%d\n", GetOnes(20071010));
getch();
return 0;
}
就对了
PcrazyC 2007-10-21
  • 打赏
  • 举报
回复
我用DEV_C++得到的结果是:

3
160
4656249


不知道是怎么回事?
PcrazyC 2007-10-20
  • 打赏
  • 举报
回复
嗯,好像结果不正确,明天我看看你的算法,看是哪的问题,今天快要停电了
blue_zyb 2007-10-20
  • 打赏
  • 举报
回复
问题在于我构造的那个a数组,它的增长速度要快于2的幂次,也就是a[i]的值要比2^i大。所以实际上如果输入的数据是2^61, 2^62等,所包含的1的个数应该是溢出8字节int的。

比如,上面程序运行的结果对5,1164,20071010这几个数据的输出和lz给出的一样。

但是,如果是:
printf("%I64u\n", GetOnes((long long)0x1000000000000000));
printf("%I64u\n", GetOnes((long long)0x2000000000000000));
printf("%I64u\n", GetOnes((long long)0x4000000000000000));

得到的结果将是:
16140901064495857665
14987979559889010689
13835058055282163713

显然由于溢出的原因,导致结果不对了。。。
blue_zyb 2007-10-20
  • 打赏
  • 举报
回复
恩,这个我知道。我此处用的long long数据类型,在VS2003 cpp项目下是8个字节的,跟__int64应该是一样的。。。
PcrazyC 2007-10-20
  • 打赏
  • 举报
回复
解决64位的数据可以用__int64这种数据类型,其输入数据和输出格式如下:

scanf("%I64d",&a);
printf("%I64d",a);
blue_zyb 2007-10-20
  • 打赏
  • 举报
回复
上面的main函数忘记加init()了。。。

void main()
{
init();
printf("%d\n", GetOnes(5));
printf("%d\n", GetOnes(1164));
printf("%d\n", GetOnes(20071010));
}



不过这个程序还是有个问题,就是实际上a数组在下标大于58以后8字节的long long就溢出了,所以对于特别大的数字比如2^62的时候,程序的结果是不对的。。。
blue_zyb 2007-10-20
  • 打赏
  • 举报
回复


long long a[64];

void init()
{
long long pwOf2;
long long i;
a[0] = 0;
for (i = 1, pwOf2 = 1; i <= 63; i++, pwOf2 *= 2)
{
a[i] = a[i-1] * 2 + pwOf2;
}
}

int GetArrayIndex(long long x)
{
int i = 0;
x >>= 1;

while ( x!=0)
{
x >>= 1;
i++;
}
return i;
}

long long GetOnes(long long x)
{
if (x == 0)
return 0;
if (x == 1)
return 1;
else
{
int index = GetArrayIndex(x);
long long near2 = (long long)1 << index;
return a[index] + (x - near2 + 1) + GetOnes(x - near2);
}
}

#include <windows.h>

void main()
{
printf("%d\n", GetOnes(5));
printf("%d\n", GetOnes(1164));
printf("%d\n", GetOnes(20071010));

}
PcrazyC 2007-10-20
  • 打赏
  • 举报
回复
一般这么大的数字都需要寻找规律的,记住:不是求一个数字的二进制数中1的位数,而是从1到这个数所有二进制数中1的个数
PcrazyC 2007-10-20
  • 打赏
  • 举报
回复
我运行了几个数据,结果是正确的,但效率太低了

题目要求解决0-2^63之间的数.要求在1S之内得到数据
加载更多回复(2)

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧