簡體   English   中英

在C中使用數組的大整數

[英]Big integers using array in C

#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,我得到了正確的答案。 但是對於給定的數字> = 45,我得到的答案比正確答案小2。我想知道為什么會這樣。 我也想知道其他處理大整數的更簡單方法。 謝謝。

編輯:謝謝大家的回答。 現在問題已解決。

我認為這行是錯誤的:

    if(a[j]>=pow(10,(j+1))){

我不知道這是錯誤還是唯一的錯誤。

這表示“如果放置j的digit大於或等於10 ^(j + 1)”。

我認為進位測試只是“ j位digit大於或等於10”。

該數字的“順序”由其位置標識。 有時也稱為地方價值表示法! 好像“十”列中的“數字”可以升至99,而“千”列中的“數字”可以升至999。我認為這不是您想要的。

{999,99,9}不是十進制數字! 那應該是{9,9,9,9,9,9}。

正如其他人也建議的那樣,我強烈建議您實施一個低位字節序方案,其中最低有效位在數組的開頭。

這樣一來,整個負載將變得更加容易,因為您不需要為了減少空間而改組代碼。

然后,您實現的算法將變為:

  1. 將i加到單位(位於a[0] )。
  2. 如果a [0]> 9,則溢出! 取“多余”並反復向上移動數字(向上移動數組)加上多余的數字(除以10),以尋找每一步的進一步溢出。

您應該跟蹤數字的“順序”(a [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

如果它對您的Google搜索(並且您還不知道)有所幫助,則稱為“二進制編碼的十進制”。 這是將基數10轉換為計算機程序的非常自然的方法,但(實際上)不是處理大整數的非常有效的方法。

它被認為是有點頑固,但您應該參考《計算機編程藝術》。 1第4.3.1節“經典算法”。 如果您不是計算機科學專業的本科生,那么您可以錯過。 如果您是計算機科學專業的本科生,則必須立即去圖書館閱讀。

編輯:我只是看了你的個人資料。 “ CSE本科生,一年級”。 是圖書館。

編輯:提示,如果您堅持異端的big-endian實現!

  1. 設置j = length-1
  2. 設置a [j] + = i(加法)
  3. 如果a [j]> = 10,將其除以10,然后取余數。
  4. 將余數設置為[j]
  5. 如果j為零,請轉到完全溢出!
  6. 否則,減小j並將步驟3中的除法結果加到a [j],然后再次返回步驟3。
  7. 完蛋了!
  8. 完全上溢:如果j達到零,則將一個插槽中的所有內容混洗,增加長度,然后像步驟3一樣將步驟3的下溢部分放入a [0]。

對於您的實現,您可能會多次下溢,因此需要循環。

我要說的是,您的程序中存在結構性錯誤,對於新手來說,比經驗豐富的程序員更常見。 您只是試圖在一個循環中做太多事情。 分而治之! 這就是我們解決計算問題的方式。 將您的函數划分為一個普通的加法循環,然后將此改組循環將溢出的數字布置在數字的末尾。

正如我一直指出的那樣,如果您是小端人士,那會更簡單!

這是我編寫的用於重新實現該解決方案的代碼,使用的是“ 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 in 1,因為∑x = n·(n + 1)÷2)。 .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
$

(是的,當我測試早期版本的代碼時,確實得到了一些錯誤的輸出。)

實現簡單。
(這一小格是為了確保進行進位。)

#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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM