#include <stdio.h>
#include <math.h>
int main(void) {
int a[100], carry,i,j=0,length=0,temp,leftcarry=0,l,n;
clrscr();
scanf("%d",&n);
for(i=0;i<100;i++) a[i]=0;
for(i=1;i<=n;i++){
a[j]+=i;
while(a[j]>=10){
carry=a[j]%10;
if(a[j]>=pow(10,(j+1))){
if(leftcarry==0){
a[j]=a[j]/10;
j++;
a[j]+=carry;
if(j>length)length=j;
}
else{
for(l=j+1;l<=length;l++){
temp=a[l+1];
a[l+1]=a[l];
a[l+2]=temp;
}
a[j+1]=carry;
leftcarry=0;
length=length+1;
}
}
else{
a[j]=a[j]/10;
a[j-1]+=a[j];
a[j]=carry;
j--;
if(a[j]>=10) leftcarry=1;
}
}
j=length;
}
for(i=0;i<=length;i++){
printf("%d",a[i]);
}
return 0;
}
我想获得一些使用数组处理大整数的经验,所以我编写了这段代码来查找前n个自然数的总和。对于给定的数字<45,我得到了正确的答案。但是对于给定数字&gt; = 45,我得到的答案小于2的正确答案。我想知道为什么会这样。我还想知道处理大整数的其他更简单的方法。谢谢。
编辑:谢谢大家的回答。现在问题已经解决了。
答案 0 :(得分:1)
我认为这一行是错误的:
if(a[j]>=pow(10,(j+1))){
我不知道这是错误还是唯一错误。
这表示'如果j位 digit
大于或等于10 ^(j + 1)'。
我认为进位测试只是'j-place digit
大于或等于10。
该数字的'顺序'由它的位置标识。它有时被称为地方价值符号! 就好像“十位”栏中的“数字”可以达到99而千位栏中的“数字”可以达到999.我认为这不是你想要的。
{999,99,9}不是十进制数!那应该是{9,9,9,9,9,9}。
正如其他人也建议我强烈建议你实施一个小端方案,其中最低位数位于数组的开头。
然后它变得更加容易,因为你不需要为了腾出空间而拖拽代码。
然后,您实施的算法变为:
a[0]
)。您应该跟踪数字的'顺序'([i]!= 0的最大i)以检测固定长度数组的溢出。
你的下一个挑战是写一个函数int add_small(int a[100],int d,int p){}
。
这将数字d * 10 ^ p加到其中0 <= d <= 10和0
之后int add_big(int a[100],int b[100])
在循环中调用add_small
。
如果它帮助你的谷歌搜索(你还不知道)你在这里做什么被称为'二进制编码十进制'。将基数10转换为计算机程序是一种非常自然的方式,但实际上并不是处理大整数的一种非常有效的方法。
它被认为有点硬核但你应该参考计算机编程艺术卷。 1第4.3.1章“经典算法”。如果你不是计算机科学本科生,你可以给他一个错过。如果您是计算机科学本科生,您必须立即前往图书馆阅读。
编辑:我刚看了你的个人资料。 “CSE本科学生,第一年”。它是图书馆。 编辑:提示如果你坚持你的异端大端实现!鉴于您的实现,您可能会多次完全下溢,因此需要循环。
我会说你的程序中存在一个结构错误,对于初学者而言比经验丰富的程序员更常见。你只是试图在一个循环中做太多。 分而治之!这就是我们解决计算问题的方法。 将你的函数划分为一个普通的加法循环,然后将这个混洗循环划分为数字末尾的溢出数字。
正如我一直指出的那样,如果你是小端的话会更简单!
答案 1 :(得分:1)
这是我为重新实现解决方案编写的代码,使用'litle-endian'方法,其中a[0]
包含单位数字,a[1]
包含十位数字等。
#include <stdio.h>
int main(void)
{
int a[100];
int length = 0;
int n;
if (scanf("%d", &n) != 1 || n < 0)
return 1;
for (int i = 0; i < 100; i++)
a[i] = 0;
for (int i = 1; i <= n; i++)
{
int carry = 0;
int number = i;
int j;
for (j = 0; carry > 0 || number > 0; j++)
{
int digit = number % 10;
number /= 10;
a[j] += digit + carry;
carry = 0;
if (a[j] >= 10)
{
a[j] -= 10;
carry = 1;
}
}
if (j > length)
length = j;
}
printf("%d ", n);
for (int i = length; i > 0; i--)
printf("%d", a[i-1]);
long l = n;
printf(" %ld\n", (l * (l + 1)) / 2);
return 0;
}
它的输出是输入值,从“大数”数组打印的数字系列,以及公式的结果作为直接计算(因为Σx= n·(n + 1)÷2,对于x在1..n)。我测试了这个脚本的变体:
$ for i in $(range 40 50); do bn <<< $i; done
'bn' is up to date.
40 820 820
41 861 861
42 903 903
43 946 946
44 990 990
45 1035 1035
46 1081 1081
47 1128 1128
48 1176 1176
49 1225 1225
50 1275 1275
$
然后更全面地了解此脚本的变体:
$ for i in $(random -n 10 100000 999999 | sort -n)
> do
> bn <<< $i
> done |
> awk '{ print; if ($2 != $3) print "BUG: " $1 " -- " $2 " != " $3 }'
291478 42479857981 42479857981
393029 77236093935 77236093935
396871 78753493756 78753493756
490344 120218864340 120218864340
577519 166764386440 166764386440
580196 168313989306 168313989306
640090 204857924095 204857924095
876878 384457951881 384457951881
892825 398568686725 398568686725
974712 475032228828 475032228828
$
我实际上使用了1000而不是10的重复,范围向上移动了10,000..99,999然后向上移动了100,000..999,999;在此之前,我已经用较低的范围和序号做了类似的证明。
我向上扩展了测试范围:
$ for i in $(random -n 10 1000000 9999999 | sort -n); do bn <<< $i; done | awk '{ print; if ($2 != $3) print "BUG: " $1 " -- " $2 " != " $3 }'
1291994 834624894015 834624894015
2032157 2064832052403 2064832052403
2266405 2568296945215 2568296945215
3187934 5081463188145 5081463188145
6045841 18276099721561 18276099721561
7248630 26271322062765 26271322062765
8604056 37014894127596 37014894127596
9095266 41361936353011 41361936353011
9533328 45442176144456 45442176144456
9543073 45535125913201 45535125913201
$ for i in $(random -n 10 10000000 99999999 | sort -n); do bn <<< $i; done | awk '{ print; if ($2 != $3) print "BUG: " $1 " -- " $2 " != " $3 }'
11451834 65572256707695 65572256707695
44931846 1009435414949781 1009435414949781
55847914 1559494776999655 1559494776999655
72229304 2608536214276860 2608536214276860
81242212 3300148545947578 3300148545947578
88702606 3934076199946921 3934076199946921
89386055 3994933458924540 3994933458924540
93246667 4347470499927778 4347470499927778
95651750 4574628686857125 4574628686857125
97417038 4745039695055241 4745039695055241
$
(是的,当我测试代码的早期版本时,我确实得到了一些损坏的输出。)
答案 2 :(得分:0)
简单实施。
(这个小的一个单元格是为了确保进位。)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
//range of one cell represent : 00-99
#define Base 100
#define Width 2
#define PRN PRIu8
typedef struct _unums {
size_t size;
uint8_t *nums;
} UNums;
void UNums_init(UNums *num){
num->nums = malloc(sizeof(*num->nums));
num->nums[0] = 0;
num->size = 1;
}
void UNums_print(UNums *num){
size_t i = num->size;
int w = 0;
do{
--i;
printf("%0*" PRN, w, num->nums[i]);
if(!w) w = Width;
}while(i!=0);
}
void UNum_drop(UNums *num){
free(num->nums);
num->nums = NULL;
}
//num += n. (n + num->nums[0] <= UINT_MAX) to work properly.
void UNums_add1(UNums *num, unsigned n){
unsigned carry = n, wk;
size_t i;
for(i=0;i<num->size;++i){
wk = num->nums[i] + carry;
num->nums[i] = wk % Base;
carry = wk / Base;
}
while(carry){
num->size += 1;
num->nums = realloc(num->nums, num->size * sizeof(*num->nums));
num->nums[i++] = carry % Base;
carry /= Base;
}
}
int main(void){
UNums num;
unsigned i, n;
UNums_init(&num);
scanf("%u", &n);
for(i=1;i<=n;++i)
UNums_add1(&num, i);
UNums_print(&num);
UNum_drop(&num);
return 0;
}