Как я могу умножить две строки, содержащие «огромные числа» (более 30 цифр)? - PullRequest
1 голос
/ 14 января 2020

Я делаю школьный проект, который мне нужен, чтобы сначала получить 2 огромных числа (неограниченный размер, например, скажем, более 30 цифр), второй шаг - взять 2 входных номера и создать новый номер. умножения двух, что я действительно стараюсь изо всех сил, пытаясь сделать это.

Мой код до сих пор:

Определение типа, чтобы убедиться, что я обрабатываю правильные переменные:

typedef char* verylong;
#define MAX_SIZE 100

Метод ввода:

verylong input_long() {
    int i, len; //i for loop, len for strlen - using integer for it to avoid invoking the method more than 1 time
    verylong number;
    char temp_str[MAX_SIZE];    //the input from user - limited to 100

    gets(temp_str); //user input
    len = strlen(temp_str); //saving the length of the input
    number = (char*)calloc(len + 1, sizeof(char));  //allocating memory for the verylong and saving space for \0

    for (i = 0; i < len; i++) {
        if (temp_str[i] - '0' < 0 || temp_str[i] - '0' > 9) {   //the input is not a digit
            printf("\nBad input!\n");
            return NULL;
        }
        number[i] = temp_str[i];    //all is good -> add to the verylong number
    }
    number[i] = '\0';   //setting last spot

    return number;
}

Моя грустная попытка выполнить мою задачу:

verylong multiply_verylong(verylong vl1, verylong vl2) {
    verylong mult;
    int cur, i, j, k, lrg, sml, temp_size;
    char *temp;

    j = 1;
    temp = (char*)calloc(lrg + sml + 1, sizeof(char)); //maximum amount of digits


    if (strlen(vl1) > strlen(vl2)) {
        lrg = strlen(vl1);
        sml = strlen(vl2);
    }
    else {
        lrg = strlen(vl2);
        sml = strlen(vl1);
    }

    cur = 0;

    for (i = sml-1; i >= 0; i--) {
        k = 0;
        temp_size = 0;
        cur = (vl1[i] - '0')*(vl2[i] - '0');
        printf("\ncur=%d", cur);
        if (cur > 9)
            temp_size = 2;
        else
            temp_size = 1;
        while (k < temp_size) {
            if (cur > 9)
                temp[j++] = (cur % 10) + '0';
            else
                temp[j++] = cur + '0';
            cur /= 10;
            k++;
        }
    }

    mult = (char*)calloc(j + 1, sizeof(char));
    for (i = 0; i < j; i++) {
        mult[i] = temp[i];
    }
    mult[i] = '\0';
    free(temp);
    return mult;
}

Короче говоря, я знаю, что делаю ошибку в мой метод умножения, так как я добавляю числа, просто добавляя мульт из 2 цифр за раз, и я действительно потерян.

Спасибо.

Ответы [ 4 ]

3 голосов
/ 14 января 2020

Мой совет - разбить задачу на несколько простых задач.

Как бы вы делали умножение на бумаге?

123 * 456 ->  1 * (456 * 100) + 2 * (456 * 10) + 3 * (456 * 1)

или писали иначе

   3 * (  1 * 456)
 + 2 * ( 10 * 456)
 + 1 * (100 * 456)
   ---------------
  SUM TO GET RESULT

or

   3 *   456
 + 2 *  4560
 + 1 * 45600
   ---------------
  SUM TO GET RESULT

Из этого вы можете определить 3 задачи

  • Умножение со степенями 10, то есть 1, 10, 100 и т. Д. c. (т.е. добавить нули в конец)
  • Умножение строкового числа на один ди git
  • Добавление двух строковых чисел.

Напишите простые функции для каждого из этих шагов.

char* mulPowerOf10(char* sn, unsigned power)
{
    ...
}

char* mulDigit(char* sn, char digit)
{
   ...
}

char* addNumbers(char* snA, char* snB)
{
   ...
}

Используя эти 3 простые функции, вы можете сложить настоящее умножение. В пседо-коде:

char* mulNumbers(char* snA, char* snB)
{
    char* result = malloc(2);
    strcpy(result, "0");
    unsigned power = 0;
    for_each_digit D in snA
    {
        char* t1 = mulPowerOf10(snB, power)
        char* t2 = mulDigit(t1, D)
        result = addNumbers(result, t2)
        ++power;
    }

    free(.. what needs to be freed ..);

    return result;
}
1 голос
/ 14 января 2020

Вот пример кода.

Мне было проще хранить число в виде последовательности цифр вместе с длиной в структуре. Число может иметь начальные нули.

#define MAX_SIZE 1024

typedef struct Number {
    int len;
    char digits[];
} Number;

// Instantiate a number with room for len digits.
Number *newNumber(int len) {
    Number *n = malloc(sizeof(Number)+len);
    n->len = len;
    memset(n->digits, 0, len);
    return n;
}

// inputNumber reads a number from stdin. It return NULL if the input 
// is invalid, otherwise it returns a Number containing the given digits. 
Number *inputNumber() {
    char temp[MAX_SIZE];
    if (fgets(temp, sizeof temp, stdin) == NULL)
        return NULL; // use fgets because gets is deprecated since C11

    // remove trailing \n if any
    int len = strlen(temp);
    if (len > 0 && temp[len-1] == '\n')
        temp[--len] = '\0';

    // check input validity
    if (len == 0)
        return NULL;
    for (int i = 0; temp[i] != '\0'; i++)
        if (temp[i] < '0' || temp[i] > '9')
            return NULL;

    Number *n = newNumber(len);
    for (int i = 0; temp[i] != '\0'; i++)
        n->digits[i] = temp[i] - '0';
    return n;
}

Чтобы умножить два числа n1 и n2, мы умножаем n1 с каждым di git из n2 и накапливаем результат, сдвинутый влево на позицию n2 di git в финале. результат.

Например, чтобы умножить 123 * 456, мы вычисляем 123 * 4 + 123 * 5 * 10 + 123 * 6 * 100. Обратите внимание, что * 10 и * 100 - это просто сдвиги влево.

Таким образом, нам нужна функция, которая умножает число на di git, и другая функция, которая накапливает число с левым сдвигом в результирующем числе.

// multiply stores the result of n time digit in result.
// Requires the len of result is the len of n + 1.
void multiplyNumber(Number *n, char digit, Number *result) {
    char carry = 0;
    for (int i = r->len-1, j = n->len-1; i > 0; i--, j--) {
        char x = n->digits[j] * d + carry;
        r->digits[i] = x%10;
        carry = x/10;
    }
    r->digits[0] = carry;
}

// accumutateNumber adds n with the left shift s to the number r. 
// Requires the len of r is at least len of n + s + 1. 
void accumulateNumber(Number *n, int s, Number *r) {
    char carry = 0;
    for (int i = r->len-1-s, j = n->len-1; j >= 0; i--, j--) {
        char x = r->digits[i] + n->digits[j] + carry;
        r->digits[i] = x%10;
        carry = x/10;
    }
    r->digits[r->len-1-s-n->len] = carry;
}

Наконец, мы Также нужна функция для печати числа

void printNumber(Number *n) {
    int i = 0;
    // skip 0 at the front
    while (i < n->len && n->digits[i] == 0)
        i++;
    if (i == n->len) {
        printf("0\n");
        return;
    }
    while (i < n->len)
        putchar(n->digits[i++] + '0');
    putchar('\n');
}

И это все. Теперь мы можем написать основную функцию с вводом чисел, умножением числа 1 на каждое число di git числа 2 и накапливанием результата со сдвигом для получения окончательного результата.

int main() {
    printf("number 1: ");
    Number *n1 = inputNumber();
    if (n1 == NULL) {
        printf("number 1 is invalid\n");
        return 1;
    }

    printf("number 2: ");
    Number *n2 = inputNumber();
    if (n2 == NULL) {
        printf("number 2 is invalid\n");
        return 1;
    }

    Number *r = newNumber(n1->len+n2->len);
    Number *tmp = newNumber(n1->len+1);
    for (int i = 0; i < n2->len; i++) {
        multiplyNumber(n1, n2->digits[n2->len-1-i], tmp);
        accumulateNumber(tmp, i, r);
    }

    printf("result: ");
    printNumber(r);

    return 0;
}
0 голосов
/ 14 января 2020

В школе они хотят научить вас алгоритму каратсуба .

. Псевдокод находится по указанной ссылке.

0 голосов
/ 14 января 2020

Здесь вы можете взглянуть на «строковую» версию, умноженную, как если бы вы делали карандашом.

Работает с 2 циклами. Внешний l oop берет цифры значения2 справа и умножается на внутренний l oop с каждым di git значения1 справа. Право di git умножения сохраняется в результате, остальное идет в переносе для следующего внутреннего l oop.

В конце внутреннего l oop к результату добавляется перенос.

После первого внешнего l oop мы должны добавить предыдущие результаты к нашему умножению.

Это делается в if(!first && *lresp) r += toI(*lresp)

Последний l oop перемещает результат в начало массива char.

#include <stdio.h>
#include <stdlib.h>
#define toI(x) ((x)-'0')
#define toC(x) ((x)+'0')
#define max(a,b) ((a)>(b)) ? (a):(b)

char *mul(char *buf1, char *buf2) {
    int size, v1, v2, r, carry=0, first=1;
    char *startp1, *endp1, *lendp1, *startp2, *endp2;
    char *startres, *endres, *resp, *lresp, *result;

    for(endp1 = startp1 = buf1; *endp1; endp1++); // start and endpointer 1st value
    for(endp2 = startp2 = buf2; *endp2; endp2++); // start and end pointer 2nd value

    size = endp2-startp2 + endp1-startp1;  // result size
    startres = endres = resp = result = malloc(size+10); // some reserve

    endres += size+10-1;  // result end pointer 
    for(resp = startres; resp <= endres; resp++) *resp = '\0';  // init result

    for(endp1--, endp2--, resp-=2; endp2>=startp2; endp2--, resp--, first=0) {
        v2 = toI(*endp2);   // current digit of value2
        for(lresp = resp, lendp1 = endp1; lendp1 >= startp1; lendp1--, lresp--) { 
            v1 = toI(*lendp1);  // current digit of value1
            r = v1 * v2 + carry;  // multiply + carry
            if(!first && *lresp) r += toI(*lresp);  // add result of previous loops
            *lresp = toC(r%10);   // store last digit
            carry = r/10;
        }
        for( ; carry != 0; carry /= 10)
            *lresp-- = toC(carry%10);
    }
    // we began right with reserve, now move to start of result
    for(lresp++; lresp < endres; lresp++, startres++)
        *startres=*lresp;
    *startres = '\0';
    return result;
}
int main() {
    char *result = mul("123456789", "12345678");
    printf("\n%s\n", result);
    free(result);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...