[英]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}。
正如其他人也建議的那樣,我強烈建議您實施一個低位字節序方案,其中最低有效位在數組的開頭。
這樣一來,整個負載將變得更加容易,因為您不需要為了減少空間而改組代碼。
然后,您實現的算法將變為:
a[0]
)。 您應該跟蹤數字的“順序”(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實現!
對於您的實現,您可能會多次下溢,因此需要循環。
我要說的是,您的程序中存在結構性錯誤,對於新手來說,比經驗豐富的程序員更常見。 您只是試圖在一個循環中做太多事情。 分而治之! 這就是我們解決計算問題的方式。 將您的函數划分為一個普通的加法循環,然后將此改組循環將溢出的數字布置在數字的末尾。
正如我一直指出的那樣,如果您是小端人士,那會更簡單!
這是我編寫的用於重新實現該解決方案的代碼,使用的是“ 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.