nsi:langages:c:solutions:karatsuba
Solution C pour l'algorithme de Karatsuba
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // pour strlen
#define N 1000
typedef unsigned char* Kdigits;
typedef struct Knumber {
char signe;
Kdigits pdigits;
unsigned long size;
} Knumber;
/* déclaration entêtes */
Knumber* creer_kempty();
void append(Knumber* k, unsigned char c);
Knumber* from_str(char* strvalue);
void free_knumber(Knumber* k);
void transfert_in(Knumber* source, Knumber* cible);
Knumber* copy(Knumber* k);
void change_sign(Knumber* k);
char isgreater(Knumber* k1, Knumber* k2);
void upsize(Knumber* k, unsigned long newsize);
void egalize_size(Knumber* k1, Knumber* k2);
void sub_digits(Kdigits pd1, Kdigits pd2, unsigned long size);
unsigned char add_digits(Kdigits pd1, Kdigits pd2, unsigned long size);
void add(Knumber* k1, Knumber* k2);
void sub(Knumber* k1, Knumber* k2);
void lshift(Knumber* k, unsigned long s);
void rshift(Knumber* k, unsigned long s);
Knumber* MSB(Knumber* k, unsigned long m);
Knumber* LSB(Knumber* k, unsigned long m);
Knumber* mult_rec(Knumber* k1, Knumber* k2);
Knumber* mult(Knumber* k1, Knumber* k2);
void kdisplay(Knumber* k);
/* déclaration des contenues des fonctions */
Knumber* creer_kempty()
{
/* crée un pointeur sur Knumber non initialisé */
Kdigits pdigits = malloc(N*sizeof(*pdigits));
if (pdigits == NULL)
{
printf("creer_empty: erreur création Kdigits.\n");
exit(1);
}
Knumber* k = malloc(sizeof(Knumber));
if (k == NULL)
{
printf("creer_empty: erreur création Knumber.\n");
exit(1);
}
k->signe = 0;
k->pdigits = pdigits;
k->size = 0;
return k;
}
void append(Knumber* k, unsigned char c)
{
/* ajoute c au bout de k.digits, augmentant k.size de 1*/
if (k->size >= N)
{
printf("append: dépassement de capacité.\n");
exit(1);
}
k->pdigits[k->size] = c;
k->size = k->size + 1;
}
Knumber* from_str(char* strvalue)
{
/* transforme la strvalue en Knumber */
unsigned long n = strlen(strvalue);
Knumber* k = creer_kempty();
for (unsigned long i=n; i>0; i--){
unsigned char d = strvalue[i-1] - '0';
append(k, d);
}
return k;
}
void free_knumber(Knumber* k)
{
/* libère la mémoire occupée par le Knumber k */
if (k->pdigits != NULL)
{
free(k->pdigits);
k->pdigits = NULL;
}
}
void transfert_in(Knumber* source, Knumber* cible)
{
/* déplace le contenu de source vers cible et libère source */
free(cible->pdigits);
cible->pdigits = source->pdigits;
cible->signe = source->signe;
cible->size = source->size;
source->pdigits = NULL;
free_knumber(source);
}
Knumber* copy(Knumber* k)
{
/* crée un Knumber copie de k*/
Knumber* h = creer_kempty(k->size);
h->size = k->size;
h->signe = k->signe;
for (unsigned long i =0; i<k->size; i++){
h->pdigits[i] = k->pdigits[i];
}
return h;
}
void change_sign(Knumber* k)
{
/* change le signe */
k->signe = 1-k->signe;
}
char isgreater(Knumber* k1, Knumber* k2)
{
/* renvoie 1 si |k2| >= |k1|, 0 sinon */
if (k1->size > k2->size)
{
for (unsigned long i = k1->size-1; i>=k2->size; i--)
{
if (k1->pdigits[i] > 0)
{
return 1;
}
}
} else if (k1->size < k2->size)
{
for (unsigned long i = k2->size-1; i>=k1->size; i--)
{
if (k2->pdigits[i] > 0)
{
return 0;
}
}
}
for (unsigned long i = k1->size; i>0; i--)
{
char d1 = k1->pdigits[i-1];
char d2 = k2->pdigits[i-1];
if (d1 > d2)
{
return 1;
}
if (d2 > d1)
{
return 0;
}
}
/* cas d'égalité */
return 1;
}
void upsize(Knumber* k, unsigned long newsize)
{
/* augmente la taille de k en remplissant par des 0 */
if (newsize < k->size)
{
printf("upsize : newsize doit être >= k.size.\n");
exit(1);
}
for (unsigned long i =k->size; i<newsize; i++){
k->pdigits[i] = 0;
}
k->size = newsize;
}
void egalize_size(Knumber* k1, Knumber* k2)
{
/* aligne la taille de la plus petite sur la plus grande */
if (k1->size < k2->size)
{
upsize(k1, k2->size);
}
else if (k1->size > k2->size)
{
upsize(k2, k1->size);
}
}
void sub_digits(Kdigits pd1, Kdigits pd2, unsigned long size)
{
/* modifie pd1 pour qu'il reçoive la différence *pd1 - *pd2 */
unsigned char retenue = 0;
for (unsigned long i=0; i<size; i++)
{
retenue = retenue + pd2[i];
if (pd1[i] < retenue)
{
pd1[i] = 10 + pd1[i] - retenue;
retenue = 1;
}
else
{
pd1[i] = pd1[i] - retenue;
retenue = 0;
}
}
}
unsigned char add_digits(Kdigits pd1, Kdigits pd2, unsigned long size)
{
/* modifie pd1 pour qu'il reçoive la somme *pd1 + *pd2*/
unsigned char retenue = 0;
for (unsigned long i=0; i<size; i++)
{
retenue = pd1[i] + pd2[i] + retenue;
pd1[i] = retenue % 10;
retenue = retenue / 10;
}
return retenue;
}
void add(Knumber* k1, Knumber* k2)
{
/* Modifie k1 pour qu'il contienne k1 + k2 */
if (k1->size != k2->size){
egalize_size(k1, k2);
}
if (k1->signe != k2->signe)
{
if (isgreater(k1, k2) == 1)
{
sub_digits(k1->pdigits, k2->pdigits, k1->size);
}
else
{
Knumber* h = copy(k2);
sub_digits(h->pdigits, k1->pdigits, h->size);
transfert_in(h, k1);
}
} else {
unsigned char r = add_digits(k1->pdigits, k2->pdigits, k1->size);
if (r > 0)
{
append(k1, r);
}
}
}
void sub(Knumber* k1, Knumber* k2)
{
/* modifie k1 qui contiendra k1 - k2 */
if (k1->size != k2->size){
egalize_size(k1, k2);
}
if (k1->signe == k2->signe)
{
if (isgreater(k1, k2) == 1)
{
sub_digits(k1->pdigits, k2->pdigits, k1->size);
}
else
{
Knumber* h = copy(k2);
sub_digits(h->pdigits, k1->pdigits, h->size);
transfert_in(h, k1);
change_sign(k1);
}
} else {
unsigned char r = add_digits(k1->pdigits, k2->pdigits, k1->size);
append(k1, r);
}
}
void lshift(Knumber* k, unsigned long s)
{
/* correspond à multiplier par 10^s */
if (s == 0)
{
return;
}
unsigned long newsize = k->size + s;
if (newsize > N)
{
printf("lshift: dépassement de capacité.\n");
exit(1);
}
for (unsigned long i=newsize - 1; i >= s; i--){
k->pdigits[i] = k->pdigits[i-s];
}
for (unsigned long i=0; i < s; i++){
k->pdigits[i] = 0;
}
k->size = newsize;
}
void rshift(Knumber* k, unsigned long s)
{
/* correspond à diviser par 10^s */
if (s > k-> size)
{
k->size = 0;
return;
}
unsigned long newsize = k->size - s;
for (unsigned long i=0; i<newsize; i++){
unsigned long ips = i + s;
k->pdigits[i] = k->pdigits[ips];
}
k->size = newsize;
}
Knumber* MSB(Knumber* k, unsigned long m)
{
/* crée une copie en tronquant les m digits LSB */
if (m > k->size)
{
printf("MSB : m doit être <= à k.size.\n");
exit(1);
}
Knumber* a = creer_kempty();
a->size = k->size - m;
for (unsigned long i=0; i<a->size; i++){
a->pdigits[i] = k->pdigits[i+m];
}
return a;
}
Knumber* LSB(Knumber* k, unsigned long m)
{
/* crée une copie ne gardant que les m digits LSB */
if (m > k->size)
{
printf("LSB : m doit être <= à k.size.\n");
exit(1);
}
Knumber* a = creer_kempty();
for (unsigned long i=0; i<m; i++)
{
a->pdigits[i] = k->pdigits[i];
}
a->size = m;
return a;
}
Knumber* mult_rec(Knumber* k1, Knumber* k2)
{
char signe;
if (k1->signe == k2->signe)
{
signe = 0;
}
else
{
signe = 1;
}
if (k1->size == 1){
Knumber* h = creer_kempty();
h->signe = signe;
unsigned char p = k1->pdigits[0] * k2->pdigits[0];
h->pdigits[0] = p % 10;
h->pdigits[1] = p / 10;
h->size = 2;
return h;
}
unsigned long m = k1->size / 2;
Knumber* a = MSB(k1, m);
Knumber* b = LSB(k1, m);
Knumber* c = MSB(k2, m);
Knumber* d = LSB(k2, m);
Knumber* ac = mult_rec(a, c);
Knumber* bd = mult_rec(b, d);
sub(a, b); // a contient a - b
sub(d, c); // d contient d - c
free_knumber(b);
free_knumber(c);
Knumber* crossed = mult_rec(a, d);
free_knumber(a);
free_knumber(d);
add(crossed, ac);
add(crossed, bd);
lshift(crossed, m);
lshift(ac, 2*m);
add(crossed, ac);
free_knumber(ac);
add(crossed, bd);
free_knumber(bd);
if (signe == 1)
{
change_sign(crossed);
}
return crossed;
}
Knumber* mult(Knumber* k1, Knumber* k2)
{
/* produit le Knumber k1*k2 par l'algo de Karastuba */
egalize_size(k1, k2);
return mult_rec(k1, k2);
}
void kdisplay(Knumber* k)
{
if (k->size == 0)
{
printf("0\n");
return;
}
if (k->signe == 1)
{
printf("-");
}
unsigned char started = 0;
for (unsigned long i=k->size-1; i>=1; i--)
{
if (k->pdigits[i] > 0)
{
started = 1;
}
if (started > 0)
{
printf("%d", k->pdigits[i]);
}
}
printf("%d\n", k->pdigits[0]);
}
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("Vous devez préciser deux entiers.nExemple : %s 42536452 97524242\n", argv[0]);
exit(1);
}
Knumber* a = from_str(argv[1]);
Knumber* b = from_str(argv[2]);
Knumber* p = mult(a, b);
kdisplay(p);
free_knumber(a);
free_knumber(b);
free_knumber(p);
exit(0);
}
nsi/langages/c/solutions/karatsuba.txt · Dernière modification : de goupillwiki
