【问题标题】:Multiplication of very large numbers using character strings使用字符串将非常大的数字相乘
【发布时间】:2015-03-16 16:19:27
【问题描述】:

我正在尝试编写一个不直接使用乘法运算符来执行两个数字相乘的 C 程序,并且它应该考虑足够大的数字,这样即使通常这两个数字的相加也不能由直接加法。

当我尝试(并成功地)编写一个使用字符串执行加法的 C 程序时,我为此受到激励,我做了以下事情:

#include<stdio.h>
#define N 100000
#include<string.h>
void pushelts(char X[], int n){
int i, j;
for (j = 0; j < n; j++){
    for (i = strlen(X); i >= 0; i--){
        X[i + 1] = X[i];
    }
        X[0] = '0'; 
    }
}

int max(int a, int b){
    if (a > b){ return a; }
    return b;
}

void main(){
    char E[N], F[N]; int C[N]; int i, j, a, b, c, d = 0, e;
    printf("Enter the first number: ");
    gets_s(E);
    printf("\nEnter the second number: ");
    gets_s(F);
    a = strlen(E); b = strlen(F); c = max(a, b);
    pushelts(E, c - a); pushelts(F, c - b);
    for (i = c - 1; i >= 0; i--){
        e = d + E[i] + F[i] - 2*'0';
        C[i] = e % 10; d = e / 10;
    }
    printf("\nThe answer is: ");
    for (i = 0; i < c; i++){
        printf("%d", C[i]);
    }
    getchar();
}

它可以添加任意两个带有“N”位的数字。现在,我将如何使用它来执行大数的乘法?首先,我编写了一个函数,该函数将作为字符串输入的数字乘以数字 n(即 0

a1a2...ak = a1 x 10^(k - 1) + a2 x 10^(k - 2) + ... + ak-1 x 10 + ak

所以两个数的乘法可以使用为加法设计的解和函数(*)来实现。

如果第一个数字是 x1x2.....xn,第二个数字是 y1y2....yk,那么:

x1x2...xn x y1y2...yk = (x1x2...xn) x y1 x 10^(k-1) + .....

现在函数 (*) 可以将 (x1x2...xn) 与 y1 相乘,乘以 10^(k-1) 只是在数字旁边添加 k-1 个零;最后我们将所有这些 k 项相互相加得到结果。但困难在于只知道每个数字包含多少位,以便每次在设计用于将它们加在一起的循环内执行加法。我曾考虑过做一个空数组,每次将 (x1x2..xn) 乘以 yi x 10^(i-1) 得到的结果添加到它,但就像我说过的那样,我无法精确所需的界限,我不知道每次应该在每个获得的结果前面添加多少个零,以便使用上述算法将其添加到空数组中。当我必须从 char 类型转换为 int 类型时会出现更多困难,反之亦然。也许我让这变得比它应该的更复杂;我不知道是否有更简单的方法可以做到这一点,或者是否有我不知道的工具。我是编程初学者,除了基本工具我不懂。

有没有人可以提出解决方案、想法或算法?谢谢。

【问题讨论】:

  • 为什么是字符串而不是整数数组?
  • 在上述情况下,我怎样才能做到这一点?我希望用户输入两个大数,我最终必须使用间接加法和乘法;我没有看到字符串是如何不必要的(至少在几个地方)。能否请您详细说明您所说的内容?
  • 换句话说,除非你有充分的理由更喜欢基数 10,否则使用基数 2^16 或 2^32(或 2^64,如果你的 C 编译器提供访问 64x64 位乘法的 128 位结果)。此外,表示以 10 为底的数字的字符串浪费了它们占用的空间的 246/256 (96%)。
  • 不可以使用 BigInt 包吗?
  • 这是我基于 NTT 的 Schönhage-Strassen 乘法 stackoverflow.com/q/18577076/2521214 与字符串乘法示例

标签: c string algorithm


【解决方案1】:

我在 SPOJ 上做 Small Factorials 问题时开发了一个算法。

这个算法是基于小学乘法的。在学生时代,我们通过将第一个数字的每个数字与第二个数字的最后一个数字相乘来学习两个数字的乘法。然后将第一个数字的每个数字与第二个数字的倒数第二个数字相乘,依此类推:

               1234
               x 56
         ------------
               7404
             +6170-   // - is denoting the left shift    
         ------------
              69104  

实际发生了什么:

  1. num1 = 1234, num2 = 56, left_shift = 0;
  2. char_array[] = num1 中的所有数字
  3. result_array[]
  4. while(num2)
    • n = num2%10
    • num2 /= 10
    • 进位 = 0, i = left_shift, j = 0
    • while(char_array[j])
      一世。 partial_result = char_array[j]*n + 进位
      ii.部分结果 += 结果数组[i]
      iii. result_array[i++] = partial_result%10
      iv.携带 = partial_result/10
    • left_shift++
  5. 以相反的顺序打印result_array

您应该注意,如果num1num2 不超出其数据类型的范围,则上述算法有效。如果您想要更通用的程序,那么您必须读取 char 数组中的两个数字。逻辑将是相同的。将num1num2 声明为char 数组。查看实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char num1[200], num2[200];
    char result_arr[400] = {'\0'};
    int left_shift = 0;

    fgets(num1, 200, stdin);
    fgets(num2, 200, stdin);

    size_t n1 = strlen(num1);
    size_t n2 = strlen(num2);   

    for(size_t i = n2-2; i >= 0; i--)
    {
        int carry = 0, k = left_shift;
        for(size_t j = n1-2; j >= 0; j--)
        {
            int partial_result = (num1[j] - '0')*(num2[i] - '0') + carry;
            if(result_arr[k])
                partial_result += result_arr[k] - '0';
            result_arr[k++] = partial_result%10 + '0';
            carry = partial_result/10;  
        }
        if(carry > 0)
            result_arr[k] = carry +'0'; 
        left_shift++;
    }
    //printf("%s\n", result_arr);

    size_t len = strlen(result_arr);
    for(size_t i = len-1; i >= 0; i-- )
        printf("%c", result_arr[i]);
    printf("\n");   
}

这不是标准算法,但我希望这会有所帮助。

【讨论】:

    【解决方案2】:

    Bignum 算法很难有效地实现。这些算法很难理解(高效的算法比您尝试实现的幼稚算法要好),您可以找到几本关于它们的书籍。

    我建议使用现有的 Bignum 库,如 GMPLib 或使用一些本机提供 bignum 的语言(例如带有 SBCL 的 Common Lisp)

    【讨论】:

      【解决方案3】:

      您可以按如下方式重新使用您的字符串添加代码(使用 user300234 的 384 x 56 示例):

      Set result="0" /* using your character-string representation */
      repeat:
          Set N = ones_digit_of_multiplier /* 6 in this case */
          for (i = 0; i < N; ++i)
            result += multiplicand  /* using your addition algorithm */
          Append "0" to multiplicand /* multiply it by 10 --> 3840 */
          Chop off the bottom digit of multiplier /* divide it by 10 --> 5 */
          Repeat if multiplier != 0.
      

      【讨论】:

        猜你喜欢
        • 2022-06-15
        • 1970-01-01
        • 2015-07-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-24
        相关资源
        最近更新 更多