在C中使用数组的大整数

时间:2015-01-09 06:19:08

标签: c arrays biginteger

#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的正确答案。我想知道为什么会这样。我还想知道处理大整数的其他更简单的方法。谢谢。

编辑:谢谢大家的回答。现在问题已经解决了。

3 个答案:

答案 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}。

正如其他人也建议我强烈建议你实施一个小端方案,其中最低位数位于数组的开头。

然后它变得更加容易,因为你不需要为了腾出空间而拖拽代码。

然后,您实施的算法变为:

  1. 将i添加到单位(位于a[0])。
  2. 如果a [0]> 9,溢出!取“多余”并迭代向上移动数字(向上移动数组)加上多余的(除以10),寻找每一步的进一步溢出。
  3. 您应该跟踪数字的'顺序'([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. 设置j = length-1
    2. 设置[j] + = i(添加)
    3. 如果a [j]&gt; = 10除以10并取余数。
    4. 在[j]
    5. 中设置余数
    6. 如果j为零,请转到完全溢出!
    7. 否则,减少j并将步骤3中的除法结果添加到[j]并再次返回步骤3。
    8. 完成!
    9. 完全溢出:如果j达到零,则将所有内容拖放到一个插槽中,增加长度并将步骤3中的下溢分区放入类似步骤3的[0]中。
    10. 鉴于您的实现,您可能会多次完全下溢,因此需要循环。

      我会说你的程序中存在一个结构错误,对于初学者而言比经验丰富的程序员更常见。你只是试图在一个循环中做太多。 分而治之!这就是我们解决计算问题的方法。 将你的函数划分为一个普通的加法循环,然后将这个混洗循环划分为数字末尾的溢出数字。

      正如我一直指出的那样,如果你是小端的话会更简单!

答案 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;
}