0
votes

Multiplier un tableau de chiffres par int en C

J'ai un très grand nombre (> 100 chiffres de long) donc il ne peut pas être stocké comme un int ou même un unsigned long long (aka uint64_t) . Le tableau ressemble à ceci:
{5, 1, 2 ... 8, 6}
Le tableau doit contenir un seul chiffre int .


Quelle serait une manière simple et surtout efficace de multiplier ce 'nombre' (le garder sous forme de tableau) par un seul chiffre?

Ce que j'ai essayé

Comme je suis assez nouveau en C, ce code n'est pas un chef-d'œuvre. Loin de là.

struct returnpointer { int * pointer; int length; };

returnpointer mul_arrays(int * x, int y, int lengthof_x) {
    returnpointer result_end;
    int result[lengthof_x * 2 + 1];
    int final_result[lengthof_x * 2 + 1];
    int carry = 0;
    int j = 0;
    //multiply 'y' by all digits of x, which creates multidigit ints
    //and also reverses the direction of the array (to allow carrying)
    for (int i = lengthof_x; i >= 0; i--) {
        result[j] = x[i] * y;
        j++;
    }
    int i = 0;
    j = lengthof_x
    //this is the bit that doesn't work: it attempts to work out the carry
    //however after hours of debugging I can't get it to work.
    while (carry > 0 || i < lengthof_x + 1) {
        if (result[j] > 9) {
            carry = result[j] / 10;
            final_result[i] = result[j] % 10;
            final_result[i + 1] = carry;
        } else {
            final_result[i] = result[j];
            carry = 0;
        }
        i++;
        j--;
    }
    result_end.pointer = result;
    result_end.length  = i + 1;
    return result_end;
}

Ce code ne fonctionne pas correctement. C'est juste une illustration de ce que j'ai essayé (si cela fonctionnait, je ne publierais pas ceci).

De plus, ce serait bien de savoir si l'approche que j'essaye d'utiliser est la plus efficace, car le programme dans lequel il sera incorporé prend beaucoup de temps, donc plus la fonction est rapide, moins le programme entier prendra de temps.

Merci d'avance.

EDIT:

Mon compilateur est g ++.


7 commentaires

Il est très difficile de dire que l'algorithme est le plus efficace, mais utiliser 3,2 bits d'un int 32 bits n'est probablement pas le plus efficace. en.wikipedia.org/wiki/…


@NeilEdelman oui, très vrai. Je me concentre principalement sur la vitesse de traitement et j'ai 64 Gio de RAM pour jouer, cependant.


Le produit peut être un élément de plus lorsqu'il est multiplié par un seul chiffre. Il est plus efficace de stocker le nombre dans une séquence petit-boutiste, puis le report peut être dans l'élément suivant (réalloué si nécessaire). C'est simple de le faire comme vous le feriez avec du papier et un crayon, et la fonction peut renvoyer le même pointeur (ou réalloué). Vous semblez l'avoir trop compliqué.


@WeatherVane pourriez-vous donner un exemple de code? Je ne comprends pas comment je ferais cela en C car je ne suis pas trop familier avec cela.


@NeilEdelman cela semble plutôt cool mais je n'aurais aucune idée de comment implémenter cela en C (voir le commentaire précédent). Si vous pouviez donner un exemple, ce serait très utile.


Comment le tableau doit-il être lu? {1, 2, 3} est-il le numéro 123 ou le numéro 321 ?


@ 4386427 lire comme 123


3 Réponses :


1
votes

Donc, quelques observations:

1) Je ne pense pas qu'il soit nécessaire d'inverser le tableau. Traitez-le simplement du chiffre le moins significatif au chiffre le plus significatif.

2) Il n'y a aucune raison de stocker des valeurs temporaires plus grandes que votre plage de chiffres autorisée. Faites simplement le transport au fur et à mesure, comme vous le feriez si vous le faisiez à la main:

carry = 0
for i in all_the_digits:
    product = x[i]*y + carry
    x[i] = product%10
    carry = product/10

3) vous pouvez stocker les chiffres sous le nom uint8_t sans crainte de débordement - cela rendra votre tableau 1/4 de la taille actuelle, ce qui devrait améliorer la vitesse en raison des effets de mise en cache.


2 commentaires

Merci, je vais traduire ça en C et l'essayer dans une heure environ quand j'aurai une seconde


Ce n'est pas la méthode la plus rapide possible - voir le lien de @Neil Edelman ci-dessus - mais c'est un bon début pour comprendre le calcul des nombres entiers de taille arbitraire.



2
votes

Comme demandé, voici un exemple de code qui multiplie un tableau par un seul chiffre. Le tableau est petit-boutiste. Pour un exemple simple, j'ai supposé que le tableau est de longueur fixe, un plus complexe allouerait de la mémoire du tableau et l'étendrait si le tableau devenait trop grand.

683215 * 7 = 4782505

Sortie du programme p >

#include <stdio.h>

#define BIGLEN 20

typedef struct {
    int length;
    int array[BIGLEN];
} biggy_t;

void bigmul(biggy_t *big, int mul)
{
    int carry = 0, partial;
    for(int i = 0; i < big->length; i++) {
        partial = big->array[i] * mul + carry;
        big->array[i] = partial % 10;
        carry = partial / 10;
    }
    if(carry) {
        big->array[big->length] = carry;
        big->length++;
    }
}

void bigout(biggy_t *big)
{
    for(int i = big->length-1; i >= 0; i--) {
        printf("%d", big->array[i]);
    }
}

int main(int argc, char *argv[])
{
    biggy_t big = { 6, { 5, 1, 2, 3, 8, 6 }};   // reverse order
    bigout(&big);
    printf(" * 7 = ");

    bigmul(&big, 7);
    bigout(&big);
    printf("\n");
}

J'ai écrit une implémentation bignum dans laquelle je peux choisir la base. 10 ou 100 pour le stockage d'octets, bien plus pour le stockage 32 bits. S'en tenir à une puissance de 10 rend la conversion en sortie décimale plus facile qu'une puissance de 2 radix, avec une petite pénalité de temps pour ne pas utiliser la pleine capacité du type de stockage.


5 commentaires

@Jachdich Remarque: comme l'indique Weather Vane au début "Le tableau est petit boutiste." Cela signifie que ce code traite {1, 2, 3} comme le nombre 321 . D'après votre commentaire, ce n'est pas ce dont vous avez besoin. Donc, pour utiliser ce code, vous devrez effectuer un "retour de tableau" (ou changer votre format en petit-boutiste "si c'est une option)


@ 4386427 ou initialisez le tableau à partir de sa source dans cet ordre en premier lieu. Je suppose que l'opérande provient d'une chaîne, alors trouvez la longueur de la chaîne et configurez la struct à partir de cela. Je n'avais pas l'intention de publier une solution complète, afin de laisser OP quelque chose à faire.


@WeatherVane True - c'est exactement ce que j'ai mentionné en: "... ou changez votre format en petit-boutiste" si c'est une option ".


@Jachdich sur l'endianness. Lorsque vous utilisez les opérateurs + - * / , le système little-endian convient, car lorsque le nombre de chiffres augmente ou diminue, vous pouvez l'adapter sans avoir à mélanger le tableau. Lors du choix d'une structure de données, choisissez ce qui conviendra à la machine et à l'algorithme. Si ce n'est pas ainsi que les humains y sont habitués, faites le changement au niveau de l'interface homme / machine: ne soyez pas gêné par la difficulté à travailler avec. Séparez le formulaire de la fonction . De même dans un jeu, disons les échecs, vous ne travailleriez pas avec le nom textuel "Queen", etc., mais avec un enum qui le représente.


@WeatherVane ouais merci. J'envisagerai de reconcevoir mon code pour ce faire.



1
votes

Il y a plusieurs problèmes dans votre code. Je ne suis pas sûr de les avoir tous repérés mais en voici quelques-uns pour commencer.

Cette boucle:

len=7
value=1024772

exécute "lengthof_x + 1" fois. En d'autres termes - une fois de trop! Vous voulez le changer en:

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

struct returnpointer { int * pointer; int length; };

void print_num(struct returnpointer * num)
{
        printf("len=%d\nvalue=", num->length);
    for(int i = 0; i <num->length; i++) {
        printf("%d", num->pointer[i]);
    }
}

struct returnpointer mul_arrays(int * x, int y, int lengthof_x) {
    struct returnpointer result_end;
    int result[lengthof_x + 1];

    // Multiply all element and revert array
    int j = 0;
    for (int i = lengthof_x-1; i >= 0; i--) {
        result[j] = x[i] * y;
        j++;
    }

    // Handle carry 
    int carry = 0;
    for (j = 0; j < lengthof_x; j++) {
        result[j] = result[j] + carry;
        carry = result[j] / 10;
        result[j] = result[j] % 10;
    }

    // Did length increase
    if (carry)
    {
        lengthof_x++;
        result[j] = carry;
    }

    // malloc result and revert back to desired format
    j = 0;
    int* final_result = malloc(lengthof_x * sizeof *final_result);
    for (int i = lengthof_x-1; i >= 0; i--) {
        final_result[j] = result[i];
        j++;
    }

    result_end.pointer = final_result;
    result_end.length  = lengthof_x;
    return result_end;
}


int main(int argc, char *argv[])
{
    int arr[] = { 5, 1, 2, 3, 8, 6}; 
    struct returnpointer num = mul_arrays(arr, 2, 6);  // 512386 * 2 -> 1024772
    print_num(&num);
}

De plus, vous avez:

result_end.length  = i + 1;

mais il semble que vous ayez calculé le résultat dans la variable final_result afin que vous retourniez le mauvais tableau.

Cependant - dans tous les cas, vous n’êtes pas autorisé à renvoie un pointeur vers un tableau local! Il sortira de la portée lors du retour de la fonction. Donc même si vous faites:

result_end.pointer = final_result;

c'est toujours du code invalide. Vous aurez besoin de malloc le tableau (et cela nuira aux performances).

Ensuite, vous avez:

result_end.pointer = result;

Donc vous incrémentez la longueur dans tous les cas. C'est faux. Vous ne devriez incrémenter que lorsque vous avez un report.

Ci-dessous, j'ai essayé de corriger votre code, c'est-à-dire que j'ai essayé de conserver la structure globale de votre code afin que vous puissiez voir où vous avez fait des erreurs. p>

for (int i = lengthof_x - 1; i >= 0; i--) {  // notice the "- 1"
    result[j] = x[i] * y;
    j++;
}

Résultat:

for (int i = lengthof_x; i >= 0; i--) {
    result[j] = x[i] * y;
    j++;
}

Notez cependant que ce n'est pas une solution optimale ...


0 commentaires