[Project Euler] 来做欧拉项目练习题吧: 题目020

                                 [Project Euler] 来做欧拉项目练习题吧: 题目020

                                                   周银辉 

 

题目描述:

n! means n x (n − 1) x ... x 3 x 2 x 1

For example, 10! = 10 x 9 x ... x 3 x 2 x 1 = 3628800,
and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27.

Find the sum of the digits in the number 100! 

 

题目分析:

求100!所得结果的各位数字之和

做法可以有两种:

一是做大数乗法,对于Java内置了BigInteger,或者Scheme可以实现无限精度的语言而已,问题就很简单。

否则自己模拟大数乗法,正如下面的multiply。 

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define BF_SZ 1000 //buffer size
//将整数转换成字符串,并传出长度, 请手动释放返回值
//stdlib中的itoa也能完成类似的功能,
//但stdlib传入的buffer长度未知,一般会设置得比较大,而造成浪费
char* int_to_str(int n, int* length)
{
*length = floor(log10(abs(n!=0?n:1))) + 1;
char *str = (char*)calloc(sizeof(char)*(*length+1));
int i = *length-1;
while(n!=0)
{
str[i] = n%10+48;
n = n/10;
i--;
}
return str;
}
//大数乗法,结果放在r中
void multiply(const char *a,const char *b, char *r)
{
int i, j, *t, length_a, length_b, length_t; // t for temp
length_a = strlen(a);
length_b = strlen(b);
length_t = length_a + length_b;
t=(int*)malloc(sizeof(int)*length_t);
for(i=0; i<length_t; i++)
{
t[i]=0;
}
for (i=0; i<length_a; i++)
{
for (j=0;j<length_b;j++)
{
t[i+j+1] += (a[i]-48)*(b[j]-48);
}
}
   // 这里实现进位操作
for (i=length_t-1; i>=0; i--)      
{
if(t[i]>=10)
{
t[i-1] += t[i]/10; 
t[i]   %= 10;
}
}
i=0;
while(t[i]==0) 
{
i++;   // 跳过数前面的0
}
for (j=0; i<length_t; i++,j++)
{
r[j] = t[i]+48;
}
r[j]='\0';
free(t);
 
}
int test(int n, char* buffer, char* temp)
{
int i, j, k, a, b, c, p; //p for product
int len;
for(i=2; i<=n; i++)
{
char *str = int_to_str(i, &len);
//printf("%s\t", str);
//做buffer和str的大数乘法
multiply(buffer, str, buffer);
free(str);
}
return 0;
}
int main()
{
char* buffer = (char*)malloc(BF_SZ*sizeof(char));
char* temp   = (char*)malloc(BF_SZ* sizeof(char));
memset(buffer, '0', BF_SZ);
buffer[BF_SZ-1] = '1';
memset(temp, '0', BF_SZ);
test(100, buffer, temp);
printf("buffer is %s\n", buffer);
int i, len=strlen(buffer), count=0;
for (i=0; i<len; i++) 
{
count += buffer[i]-48;
}
printf("the count is %d\n", count);
free(buffer);
free(temp);
return 0;

 

二是做大数阶乘:
#include <stdio.h>
#include <stdlib.h>
int buffer[1000];
void multiply(int buffer[],int n,int *length)
{
int c=0, i, len=*length;
for(i=0; i<len; i++)
{
buffer[i] = buffer[i]*n + c;
c         = buffer[i]/10;
buffer[i]%=10;
}
while(c!=0)
{
buffer[len++] = c%10;
c /= 10;
}
*length=len;
}
main()
{
buffer[0]=1;
int i, n=100, len=1;
for(i=2; i<=n; i++)
{
multiply(buffer, i, &len);
}
for(i=len-1; i>=0; i--)
{
printf("%d",buffer[i]);
}
printf("\n");

 

这种模拟大数运算的题目要求细心和耐性,其它便没什么了

posted @ 2011-04-20 22:58 周银辉 阅读(1622) 评论(9) 编辑 收藏

 回复 引用 查看   
#1楼 2011-04-21 08:27 徐少侠      
1-100里面的质数应该不多。
乘100在本题内无意义。
因此所有成对的2,5因数都可以省略

一个变态的思路就是从1到100逐个分解质因数。
然后剔除成对的2,5。
就能把最终结果缩小很多倍了。

有时间搞搞

 回复 引用 查看   
#2楼 2011-04-21 08:34 徐少侠      
查了一下,100!有157位长,按上面能缩小24位。似乎帮助不大。
 回复 引用 查看   
#3楼[楼主] 2011-04-21 10:20 周银辉      
@徐少侠
恩,感谢提醒...

 回复 引用 查看   
#4楼 2011-04-22 08:35 徐少侠      
回家又粗略统计了一下。
100以内的质数一共是25个,其中就是2和3的次数最多,不过也就刚刚大于一个4字节的整形。
因此剔除成对的2和5以后,多数都可以直接运算得到中间结果。
然后做25次大数乘法就有结果了。

当然,没仔细比较上述做法和直接做大数乘法的效率哪个高。
毕竟找质数等等也是要费时间的,不一定比直接做快。

 回复 引用 查看   
#5楼 2011-04-23 12:05 take it and go      
这种题目应该就是专门练习高精度,所以一般的优化其实没有必要
 回复 引用 查看   
#6楼 2011-06-10 16:38 且听风吟......      
厉害。。我发现第296题还没有人做出来哦。。
 回复 引用 查看   
#7楼 2011-06-16 11:18 Influence      
本题和前面的求2的1000次方属于同一类型的题目,我发现大部分解法都是“暴力”方式的,包括在ProjectEuler内部的讨论,个人以为这类题目应该属于数论中的同余问题:任何十进制数用9除其余数与该数的各位数之和用9除其余数相同。
主要是这个同余定理的应用,首先估算出各数位和的范围,我这个题目和2的1000次方的题目都是在纸上手算出来的。

 回复 引用 查看   
#8楼 2011-06-16 11:18 Influence      
本题和前面的求2的1000次方属于同一类型的题目,我发现大部分解法都是“暴力”方式的,包括在ProjectEuler内部的讨论,个人以为这类题目应该属于数论中的同余问题:任何十进制数用9除其余数与该数的各位数之和用9除其余数相同。
主要是这个同余定理的应用,首先估算出各数位和的范围,我这个题目和2的1000次方的题目都是在纸上手算出来的。

 回复 引用 查看   
#9楼 2011-08-17 17:14 cutebear      
下面是我的一个c#实现,测试通过了的。
private static string JieCh(int n)
{
// 使用万进制,每个数组元素四位,估计用来存计算结果所需数组的长度。
int arraylength = 50;
int[] numbers = new int[arraylength];
numbers[arraylength - 1] = 1;
for (int j = 2; j <= n; j++)
{
int jinweinumber = 0;
for (int i = arraylength - 1; i >= 0; i--)
{
numbers[i] *= j;
numbers[i] += jinweinumber;
if (numbers[i] > 9999)
{
var quyu = numbers[i] % 10000;
jinweinumber = (numbers[i] - quyu) / 10000;
numbers[i] = quyu;
}
else
jinweinumber = 0;
}
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < arraylength; i++)
{
result.Append(numbers[i].ToString("0000"));
}

return result.ToString().TrimStart('0');
}

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 2023016 4UcYHVndBRU=