Comment écrire un programme C pour la multiplication sans utiliser les opérateurs * et +?

68

Est-il possible d'écrire un programme en langage C qui multiplie deux nombres sans utiliser les opérateurs de multiplication et d'addition?

J'ai trouvé ceci sur Stack Overflow . S'il vous plaît aider ce pauvre programmeur avec son problème. Et s'il vous plaît ne donnez pas de réponses comme c = a/(1/((float)b)), ce qui est exactement la même chose que c = a*b. (Et est déjà donné comme réponse.)

La réponse avec le plus de votes positifs le 19 janvier 2014 est la victoire.

Remarque: il s'agit d'une question à la . S'il vous plaît ne prenez pas la question et / ou les réponses au sérieux. Plus d'informations sont en code-trolling .

s3lph
la source
2
@PaulR utilisez votre fantaisie
John Dvorak
26
Code-golf.SE ne devrait pas être un endroit pour vous moquer des questions que vous avez vues sur StackOverflow.
Gareth
17
@Gareth, tu es sûr? La première ligne suggère que cela peut être tout à fait approprié.
Darren Stone
5
Je suis en attente pour quelqu'un d' écrire un algorithme basé sur le sommeil
kb_sou
21
Cette question n'est pas aussi ridicule que cela puisse paraître. Le matériel informatique réel (transistors) ne comporte pas d'opérations de multiplication ni d'ajout. Ils disposent d'opérations logiques de base comme NOT, AND, OR, XOR. Déterminer comment répondre à cette question peut vous donner une excellente idée du fonctionnement réel d’un ordinateur au niveau des portes logiques.
Gabe

Réponses:

147

Toujours utiliser la récursivité

La recusion est la bonne façon!

int inc(int x) {
    return x&1?inc(x>>1)<<1:x|1;
}

int dec(int x) {
    return x&1?x^1:dec(x>>1)<<1|1;
}

int add(int x, int y) {
    return x?add(dec(x),inc(y)):y;
}

int mul(int x, int y) {
    return x?x^1?add(y,mul(dec(x),y)):y:0;
}

int main() {
    int a, b;
    scanf("%i\n%i", &a, &b);
    printf("%i", mul(a,b));
}
Oberon
la source
8
Je donnerais +3 si je pouvais: un pour la récursion ultime, un pour ??::sans parenthèses, un pour résoudre le problème sans essayer d'affiner les règles;)
yo
10
Si Picasso était programmeur ...
R Hughes
4
@SargeBorsch Mais où as le plaisir dans ce que ?
Oberon
3
@HC_ La incfonction teste son argument pour voir si le bit le plus bas est 1; si tel est le cas, il s’appelle lui-même sur les bits supérieurs restants de l’argument et renvoie le résultat avec le même bit faible vérifié 0, alors que si ce n’est pas le cas (c’est-à-dire que le bit le plus bas est 0), il le remplace 0par un 1et renvoie le résultat. . Le processus est très similaire à ce que vous feriez si vous ajoutiez les valeurs à la main, chiffre binaire par chiffre binaire.
JAB
2
La fonction d'incrémentation ne va-t-elle pas dans une boucle infinie pour -1? (0xFFFF) l'idéone montre (-1 >> 1) == -1.
Destrictor
87

Vous devrez compiler le programme à chaque fois, mais il multiplie tous les entiers positifs exactement dans toutes les versions de C ou C ++.

 #define A 45  // first number
 #define B 315 // second number

 typedef char buffer[A][B];

 main() {
    printf("%d\n",sizeof(buffer));
 }
Mark Lakata
la source
4
Placez-le dans une structure et vous n’avez pas besoin de mémoire.
Ben Jackson
4
Hahahah génial !!
Almo
1
utilisez la "%zu"chaîne de format.
Grijesh Chauhan le
5
Cela sizeof(char[A][B])fonctionnera (sauf si A <= 0 ou B <= 0 ou A * B déborde, auquel cas vous devriez obtenir une erreur de type "mauvais type")
greggo
3
@DavidRicherby - Je pourrais simplifier le code main(){return sizeof(char[A][B]);}et compiler aveccc -DA=6 -DB=7 a.c; ./a.out; echo $?
Mark Lakata le
47

Si vous êtes OK avec un peu d'imprécision, vous pouvez utiliser la méthode de Monte Carlo :

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

unsigned int mul(unsigned short a, unsigned short b) {
  const int totalBits = 24;
  const int total = (1 << totalBits);
  const int maxNumBits = 10;
  const int mask = (1 << maxNumBits) - 1;
  int count = 0, i;
  unsigned short x, y;
  for(i = 0; i < total; i++) {
    x = random() & mask;
    y = random() & mask;
    if ((x < a) && (y < b))
      count++;
  }
  return ((long)count) >> (totalBits - (maxNumBits << 1));
}

void main(int argc, char *argv[]) {
  unsigned short a = atoi(argv[1]);
  unsigned short b = atoi(argv[2]);
  printf("%hd * %hd = %d\n", a, b, mul(a, b));
}

Exemple:

$ ./mul 300 250
300 * 250 = 74954

Je suppose que cela pourrait suffire;)

Petr Pudlák
la source
3
Vous avez mon vote up. J'ai entendu dire que Monte-Carlo est ce que la NASA utilise pour son calcul. Mais j'aimerais voir cela sans les deux instances de l' ++opérateur.
Darren Stone
1
@DarrenStone-= -1
Timtech
20
@Timtech |= 1(travaillera sur 50% des chiffres, 100% du temps)
Darren Stone
2
+1, mais en notant que cela pourrait être trop lent et que vous pourriez ajouter un support multi-thread en verrouillant soigneusement le 'count ++' :-)
greggo le
1
Il y a toujours des printfincréments: printf("%*cc%n\n", count, &count, 'c');(Prints 'c' compter le nombre de fois, puis un autre 'c', et stocker le nombre de caractères écrits count.
MSalters
45

Puisque vous n'avez pas précisé la taille du nombre, je suppose que vous voulez dire deux nombres d'un bit.

#include <stdbool.h>
bool mul(bool a, bool b) {
    if (a && b) {
        return true;
    } else {
        return false;
    }
}

Si vous souhaitez une implémentation extrêmement efficace, utilisez la minuscule implémentation suivante:

m(a,b){return a&b;}

Notez qu'il n'accepte toujours que les bits, même si les types sont des enttes implicites - il prend moins de code et est donc plus efficace. (Et oui, ça compile.)

Cel Skeggs
la source
8
Agréable. Une mauvaise interprétation délibérée de la question :-)
John Dvorak
6
Vous pouvez optimiser cela pour return a && b;. C'est plus court, donc c'est plus rapide.
Ry-
1
@minitech: J'ai décidé de ne pas utiliser cela afin d'aggraver légèrement le code. Si je voulais aller plus loin avec ça, j'y arriverais return a&b;.
Cel Skeggs
1
C doit #include<stdbool.h>définir trueet false.
leewz
1
Oui, #include<stdbool.h>semble être juste trois #defines que vous pouvez faire vous - même ( true, false, boolet un drapeau pour marquer qu'il a été activé). Vous pouvez également utiliser une astuce d’une autre réponse et utiliser implicitement intla version "courte".
leewz
31

Voici un script shell simple pour le faire:

curl "http://www.bing.com/search?q=$1%2A$2&go=&qs=n&form=QBLH&pq=$1%2A$2" -s \
| sed -e "s/[<>]/\n/g" \
| grep "^[0-9 *]*=[0-9 ]*$"

UPDATE: Bien sûr, pour le faire en C, emballez-le simplement exec("bash", "-c", ...). (Merci, AmeliaBR)

Vroo
la source
41
Je ne peux pas décider lequel est le plus horrible. Que vous externalisiez vos calculs vers un moteur de recherche ou que le moteur de recherche que vous avez choisi soit Bing. Malheureusement, je ne pense pas que cela fonctionnera pour notre OP sans joie, qui avait besoin de quelque chose en C.
AmeliaBR
5
Merci d'avoir attrapé cet oubli. Pour votre information, j'utilise Bing parce que Google complique la tâche consistant à émettre des requêtes de ce type. Vous devez ajouter des en-têtes pour convaincre Google que votre demande provient réellement d'un navigateur.
Vroo
2
@abarnert hmm ... Bing comprend-il "les temps"? Wolfram Alpha pourrait, cependant.
John Dvorak
2
@ JanDvorak: Ouais, Wolfram fonctionne. (Notez le %20pour éviter d’utiliser des +signes.) Mais vous devez quand même analyser la sortie (en C) pour en extraire la valeur. Ce qui sera particulièrement délicat, car la sortie semble être une image, pas un texte. L'analyse HTML plus l'OCR pourrait en faire la meilleure réponse possible à ce problème.
Abarnert
3
@ JanDvorak: Ce n'est pas amusant. J'attendais avec impatience que quelqu'un
écrive
27

Pourquoi, effectuons une recherche récursive en divisant par deux entre INT64_MIN et INT64_MAX!

#include <stdio.h>
#include <stdint.h>

int64_t mul_finder(int32_t a, int32_t b, int64_t low, int64_t high)
{
    int64_t result = (low - (0 - high)) / 2;
    if (result / a == b && result % a == 0)
        return result;
    else
        return result / a < b ?
            mul_finder(a, b, result, high) :
            mul_finder(a, b, low, result);
}

int64_t mul(int32_t a, int32_t b)
{
    return a == 0 ? 0 : mul_finder(a, b, INT64_MIN, INT64_MAX);
}

void main(int argc, char* argv[])
{
    int32_t a, b;
    sscanf(argv[1], "%d", &a);
    sscanf(argv[2], "%d", &b);
    printf("%d * %d = %ld\n", a, b, mul(a, b));
}

Post-scriptum Il va sigsegv heureusement avec certaines valeurs. ;)

transport
la source
18

Malheureusement, cela ne fonctionne que pour les entiers.

Puisque l'addition est interdite, construisons d'abord un opérateur d'incrémentation:

int plusOne(int arg){
  int onMask = 1;
  int offMask = -1;
  while (arg & onMask){
    onMask <<= 1;
    offMask <<= 1;
  }
  return(arg & offMask | onMask);
}

Ensuite, nous devons gérer le signe. D'abord, trouvez le bit de signe:

int signBit = -1;
while(signBit << 1) signBit <<=1;

Ensuite, prenez le signe et la magnitude de chaque argument. Pour annuler un nombre dans un complément à deux, inversez tous les bits et ajoutez un.

int signA = a & signBit;
if(signA) a = plusOne(-1 ^ a);
int signB = b & signBit;
if(signB) b = plusOne(-1 ^ b);
int signRes = signA ^ signB;

Pour multiplier deux entiers positifs, nous pouvons utiliser la signification géométrique de multiplication:

// 3x4
//
// ooo
// ooo
// ooo
// ooo

int res = 0;
for(int i = 0; i < a; i = plusOne(i))
  for(int j = 1; j < b; j = plusOne(j))
    res = plusOne(res);

if(signRes) res = plusOne(-1 ^ res);

trolls:

  • L'addition est interdite, mais compte-t-elle a++vraiment comme une addition? Je parie que le professeur avait l'intention de le permettre.
  • S'appuie sur le complément à deux, mais il s'agit d'un comportement défini par la mise en œuvre et la plate-forme cible n'a pas été spécifiée.
  • De même, on suppose que la soustraction et la division ne sont pas autorisées non plus.
  • << est en fait la multiplication par une puissance de deux, il devrait donc être techniquement interdit.
  • diagramme non nécessaire est inutile. En outre, il aurait pu être transposé pour enregistrer une ligne.
  • le décalage répété de -1n'est pas la meilleure façon de trouver le bit de signe. Même s'il n'y avait pas de constante intégrée, vous pourriez effectuer un décalage logique à droite de -1, puis inverser tous les bits.
  • XOR -1 n'est pas le meilleur moyen d'inverser tous les bits.
  • Toute la charade avec une représentation de la grandeur du signe est inutile. Il suffit de choisir un calcul arithmétique modulaire et non signé pour le reste.
  • puisque la valeur absolue de MIN_INT(AKA signBit) est négative, cela se brise pour cette valeur. Heureusement, cela fonctionne toujours dans la moitié des cas, car MIN_INT * [even number] devrait être nul.En outre, plusOnerompt pour -1, provoquant des boucles infinies chaque fois que le résultat déborde. plusOnefonctionne très bien pour n'importe quelle valeur. Désolé pour la confusion.
John Dvorak
la source
+1 pour un troll de code réel: il semble que cela devrait fonctionner, mais il va très probablement exploser sur l'OP et il / elle n'aura aucune idée pourquoi.
Kevin - Réintégrer Monica le
1
Il est possible de faire l'addition sans TOUT opérateur d'addition en utilisant simplement shift, XOR et AND. Tous ces ++ me font mal à la tête - un seul bit AJOUTER avec retenue est (x ^ y) | ((x & y) << 1) (modulo toutes les erreurs causées en tapant dans cette petite zone de texte pourrie.)
Julie à Austin le
@JulieinAustin yep. L'algorithme est encore plus inefficace qu'il ne devrait l'être. Devrais-je modifier la liste des trolls? :-)
John Dvorak
1
@JulieinAustin (x ^ y) | ((x & y) << 1)ne fonctionne pas tout à fait, il ne se propagera pas lorsque x ou y et que carry sont tous deux vrais dans la même position :)
hobbs
@hobbs solution: au lieu de ORing, ajoutez-les récursivement si carry est différent de zéro.
John Dvorak
14

Fonctionne également pour les nombres à virgule flottante:

float mul(float a, float b){
  return std::exp(std::log(a) - std::log(1.0 / b));
}
nbubis
la source
11

Tout le monde sait que Python est plus facile à utiliser que C. Et Python a des fonctions correspondant à chaque opérateur, dans les cas où vous ne pouvez pas utiliser l'opérateur. Quelle est exactement notre définition du problème, non? Alors:

#include <Python.h>

void multiply(int a, int b) {
    PyObject *operator_name, *operator, *mul, *pa, *pb, *args, *result;
    int result;

    operator_name = PyString_FromString("operator");
    operator = PyImport_Import(operator_name);
    Py_DECREF(operator_name);
    mul = PyObject_GetAttrString(operator, "__mul__");
    pa = PyLong_FromLong((long)a);
    pb = PyLong_FromLong((long)b);
    args = PyTuple_New(2);
    PyTuple_SetItem(args, 0, pa);
    PyTuple_SetItem(args, 1, pb);
    presult = PyObject_CallObject(mul, args);
    Py_DECREF(args);
    Py_DECREF(mul);
    Py_DECREF(operator);
    result = (int)PyLong_AsLong(presult);
    Py_DECREF(presult);
    return result;
}

int main(int argc, char *argv[]) {
    int c;
    Py_Initialize();
    c = multiply(2, 3);
    printf("2 * 3 = %d\n", c);
    Py_Finalize();
}
abarnert
la source
10

Aucune des autres réponses n'est théoriquement valable. Comme le dit le tout premier commentaire sur la question:

S'il vous plaît être plus précis sur les "numéros"

Nous devons définir la multiplication et les nombres avant même de pouvoir obtenir une réponse. Une fois que nous le faisons, le problème devient trivial.

Le moyen le plus populaire de faire cela en commençant la logique mathématique consiste à construire des ordinaux de von Neumann sur la théorie des ensembles ZF , puis à utiliser les axiomes de Peano .

Cela se traduit naturellement en C, en supposant que vous ayez un type de jeu pouvant contenir d'autres jeux. Il ne doit pas contenir quoi que ce soit , mais des ensembles, ce qui le rend trivial (aucun de cette coulée void*non - sens dans la plupart des bibliothèques set), donc je vais laisser la mise en œuvre comme un exercice pour le lecteur.

Alors, d'abord:

/* The empty set is 0. */
set_t zero() {
    return set_new();
}

/* The successor of n is n U {n}. */
set_t successor(set_t n) {
    set_t result = set_copy(n);
    set_t set_of_n = set_new();
    set_add(set_of_n, n);
    set_union(result, set_of_n);
    set_free(set_of_n);
    return result;
}

/* It is an error to call this on 0, which will be reported by
   running out of memory. */
set_t predecessor(set_t n) {
    set_t pred = zero();
    while (1) {
        set_t next = successor(pred);
        if (set_equal(next, n)) {
            set_free(next);
            return pred;
        }
        set_free(pred);
    }
}        

set_t add(set_t a, set_t b) {
    if (set_empty(b)) {
        /* a + 0 = a */
        return a;
    } else {
        /* a + successor(b) = successor(a+b) */
        set_t pred_b = predecessor(b)
        set_t pred_ab = add(a, pred_b);
        set_t result = successor(pred_ab);
        set_free(pred_b);
        set_free(pred_ab);
        return result;
    }
}

set_t multiply(set_t a, set_t b) {
    if (set_empty(b)) {
        /* a * 0 = 0 */
        return b;
    } else {
        /* a * successor(b) = a + (a * b) */
        set_t pred_b = predecessor(b)
        set_t pred_ab = mul(a, pred_b);
        set_t result = successor(pred_ab);
        set_free(pred_b);
        set_free(pred_ab);
        return result;
    }
}

Si vous souhaitez étendre cela aux entiers, aux rationnels, aux réels, aux surréels, etc., vous pouvez — avec une précision infinie (en supposant que vous avez une mémoire infinie et un processeur), de démarrer. Mais comme Kroenecker l'a dit, Dieu a créé les nombres naturels; tout le reste est le travail de l'homme, alors vraiment, pourquoi s'embêter?

abarnert
la source
1
Sensationnel. Tu es encore plus lent que moi.
John Dvorak
10
unsigned add( unsigned a, unsigned b )
{
    return (unsigned)&((char*)a)[b];  // ignore compiler warnings
       // (if pointers are bigger than unsigned). it still works.
}
unsigned umul( unsigned a, unsigned b )
{
    unsigned res = 0;
    while( a != 0 ){
        if( a & 1) res = add( res, b );
        b <<= 1;
        a >>= 1;
    }
    return res;
}

int mul( int a, int b ){
    return (int)umul( (unsigned)a, (unsigned)b );
}

Si vous considérez que le hack a [b] est une triche (puisqu'il s'agit vraiment d'un ajout), cela fonctionne à la place. Mais les recherches dans les tables impliquent également un ajout de pointeur.

Voir http://en.wikipedia.org/wiki/IBM_1620 - un ordinateur qui a effectivement ajouté des données à l'aide de tables de recherche ...

Quelque chose de satisfaisant à propos de l'utilisation d'un mécanisme de table pour «accélérer» une opération qui pourrait réellement être effectuée en une seule instruction.

static unsigned sumtab[17][16]= {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15},
{ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16},
{ 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17},
{ 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18},
{ 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19},
{ 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20},
{ 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21},
{ 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22},
{ 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23},
{ 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24},
{10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25},
{11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26},
{12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27},
{13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28},
{14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29},
{15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30},
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
};

unsigned add( unsigned a, unsigned b )
{
   static const int add4hack[8] =  {4,8,12,16,20,24,28,32};
   unsigned carry = 0;
   unsigned (*sumtab0)[16] = &sumtab[0];
   unsigned (*sumtab1)[16] = &sumtab[1];
   unsigned result = 0;
   int nshift = 0;
   while( (a|b) != 0 ){
      unsigned psum = (carry?sumtab1:sumtab0)[ a & 0xF ][ b & 0xF ];
      result = result | ((psum & 0xF)<<nshift);
      carry = psum >> 4;
      a = a >> 4
      b = b >> 4;
      nshift= add4hack[nshift>>2];  // add 4 to nshift.
   }
   return result;
}
Gregg
la source
Oups, il y a un personnage *(bien que ce ne soit pas une multiplication)
Sarge Borsch le
Euh, la recherche dans la table utilise l'addition - (a [i]) est identique à (* (a + i)).
Julie à Austin le
@JulieinAustin je l'ai mentionné. La consultation de table peut se faire sans ajouts, en fusionnant des champs (comme illustré dans IBM 1620, voir lien), mais il est compliqué de la configurer en C: vous devez, pour une chose, aligner la table sur une adresse correcte pour que les index juste ou suis entré.
greggo
8

Je ne suis pas sûr de ce qui constitue une "triche" dans ces publications "code troll", mais cela multiplie 2 entiers arbitraires, au moment de l'exécution, sans opérateur *ou +utilisant des bibliothèques standard (C99).

#include <math.h>
main()
{
  int a = 6;
  int b = 7;
  return fma(a,b,0);
}
Mark Lakata
la source
8

Ma solution troll pour unsigned int:

#include<stdio.h>

unsigned int add(unsigned int x, unsigned int y)
{
  /* An addition of one bit corresponds to the both following logical operations
     for bit result and carry:
        r     = x xor y xor c_in
        c_out = (x and y) or (x and c_in) or (y and c_in)
     However, since we dealing not with bits but words, we have to loop till
     the carry word is stable
  */
  unsigned int t,c=0;
  do {
    t = c;
    c = (x & y) | (x & c) | (y & c);
    c <<= 1;
  } while (c!=t);
  return x^y^c;
}

unsigned int mult(unsigned int x,unsigned int y)
{
  /* Paper and pencil method for binary positional notation:
     multiply a factor by one (=copy) or zero
     depending on others factor actual digit at position, and  shift 
     through each position; adding up */
  unsigned int r=0;
  while (y != 0) {
    if (y & 1) r = add(r,x);
    y>>=1;
    x<<=1;
  }
  return r;
}

int main(int c, char** param)
{
  unsigned int x,y;
  if (c!=3) {
     printf("Fuck!\n");
     return 1;
  }
  sscanf(param[1],"%ud",&x);
  sscanf(param[2],"%ud",&y);
  printf("%d\n", mult(x,y));
  return 0;
}
Matthias
la source
1
+1 Nice implémentation de l’évaluation du carry J'aime votre code :)
Yo '12
@ BЈовић: De ma faute, je pensais que traîner ne consiste pas à comprendre. Noms modifiés et commentaires ajoutés.
Matthias
Pardon. J'ai mal compris en quoi consiste cette étiquette et en quoi consiste réellement le Q. Vous devriez l'
inverser
@ Matthias, dans ce cas, il est utile de comprendre comment cela fonctionne pour que nous puissions comprendre à quel point cette opération de transfert convergent est déformée. Dans une situation réelle de code-troll, les commentaires pourraient être expurgés :-)
greggo
Je voudrais souligner que si vous voulez ajouter des nombres inversés (avec high to lo carry prop) et que vous n'avez pas d'instruction 'bitrev', c'est probablement une approche parfaitement raisonnable (après avoir changé en c> > = 1 bien sûr)
greggo
7

Il y a beaucoup de bonnes réponses ici, mais il ne semble pas que beaucoup d'entre elles tirent parti du fait que les ordinateurs modernes sont vraiment puissants. Il existe plusieurs unités de traitement dans la plupart des processeurs, alors pourquoi en utiliser une seule? Nous pouvons exploiter cela pour obtenir d'excellents résultats.

#include <stdio.h>
#include <limits.h>
#include "omp.h"

int mult(int a, int b);

void main(){
        int first;
        int second;
        scanf("%i %i", &first, &second);
        printf("%i x %i = %i\n", first, second, mult(first,second));
}

int mult(int second, int first){
        int answer = INT_MAX;
        omp_set_num_threads(second);
        #pragma omp parallel
        for(second = first; second > 0; second--) answer--;
        return INT_MAX - answer;
}

Voici un exemple de son utilisation:

$ ./multiply
5 6
5 x 6 = 30

La #pragma omp paralleldirective oblige OpenMP à diviser chaque partie de la boucle for en une unité d'exécution différente, nous nous multiplions donc en parallèle!

Notez que vous devez utiliser l' -fopenmpindicateur pour indiquer au compilateur d'utiliser OpenMP.


Troll parties:

  1. Tromper sur l'utilisation de la programmation parallèle.
  2. Ne fonctionne pas sur les nombres négatifs (ou grands).
  3. Ne divise pas réellement les parties de la forboucle - chaque thread exécute la boucle.
  4. Noms de variables ennuyeux et réutilisation de variables.
  5. Il y a une condition de course subtile answer--; la plupart du temps, il n'apparaîtra pas, mais occasionnellement, il provoquera des résultats inexacts.
Millinon
la source
2
Pourquoi ne pas combiner cela avec la réponse SIMD de Paul R, de sorte que vous pouvez exécuter 32x aussi vite que 8x? Bien que vraiment, vous voulez impliquer le GPU ainsi que les cœurs; alors ça flamberait vraiment . :)
abarnert
2
Vous pouvez également utiliser OpenMPI pour l’exécuter sur quelques machines en parallèle.
Millinon
6

Malheureusement, la multiplication est un problème très difficile en informatique. La meilleure solution consiste à utiliser la division à la place:

#include <stdio.h>
#include <limits.h>
int multiply(int x, int y) {
    int a;
    for (a=INT_MAX; a>1; a--) {
        if (a/x == y) {
            return a;
        }
    }
    for (a=-1; a>INT_MIN; a--) {
        if (a/x == y) {
            return a;
        }
    }
    return 0;
}
main (int argc, char **argv) {
    int a, b;
    if (argc > 1) a = atoi(argv[1]);
    else a = 42;
    if (argc > 2) b = atoi(argv[2]);
    else b = 13;
    printf("%d * %d is %d\n", a, b, multiply(a,b));
}
utilisateur3175123
la source
6

Dans la vraie vie, je réponds habituellement à la traîne par la connaissance, alors voici une réponse qui ne traine pas du tout. Cela fonctionne pour toutes les intvaleurs dans la mesure où je peux voir.

int multiply (int a, int b) {
  int r = 0;
  if (a < 0) { a = -a; b = -b }

  while (a) {
    if (a&1) {
      int x = b;
      do { int y = x&r; r ^= x; x = y<<1 } while (x);
    }
    a>>=1; b<<=1;
  }
  return r;
}

À ma connaissance, cela ressemble beaucoup à la façon dont un processeur peut en fait multiplier des nombres entiers. Premièrement, nous nous assurons qu'au moins un des arguments ( a) soit positif en retournant le signe si aest négatif (et non, je refuse de compter la négation comme une sorte d'addition ou de multiplication). Ensuite, la while (a)boucle ajoute une copie décalée de bau résultat pour chaque bit défini dans a. La doboucle implémente l' r += xutilisation de et, xor et transforme ce qui est clairement un ensemble de demi-additionneurs, avec les bits de report renvoyés xjusqu'à ce qu'il n'y en ait plus (un processeur réel utiliserait des additionneurs complets, ce qui est plus efficace, mais C t ont les opérateurs dont nous avons besoin pour cela, à moins que vous ne les comptiez +).

Hobbs
la source
4
Le demandeur n'a pas troll. Vous êtes censé troll.
John Dvorak
2
C'est un troll furtif! L'échec secret est sur un == INT_MIN.
Jander
1
@ Jander hmm. Ouais, c'est un bon. Je suppose (sur les systèmes du complément à deux ordinaires) que le résultat de la négation de a est toujours négatif et que la while(a)boucle ne se termine jamais.
Hobbs
@ Hobbs Oui, cela me semble juste. Sinon une très jolie réponse.
Jander
6
 int bogomul(int A, int B)
{
    int C = 0;
    while(C/A != B)
    {

        print("Answer isn't: %d", C);
        C = rand();

    }
    return C;
}
Chengarda
la source
1
Cela échouera horriblement si le résultat déborde. Agréable! Je suppose que vous ne devriez pas imprimer, cependant.
John Dvorak
2
échoue pour a = 2, b = 2, c = 5
BЈовић
@ BЈовић: while(C/A != B || C%A)?
Abarnert
2
Notez que c'est vraiment une tentative de faire la même chose que le successeur de Deep Thought, mais pour tous les univers possibles , au lieu de simplement celui où la réponse est 42. Ce qui serait très impressionnant s'il n'y avait pas ce bug. Et l'absence de traitement des erreurs dans le cas des Vogons.
Abarnert
1
A besoin de plusieurs threads. Vous savez, pour le rendre efficace.
greggo
6

Jeter ceci dans le mélange:

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

int mul(int a, int b)
{
        asm ("mul %2"
            : "=a" (a)
            : "%0" (a), "r" (b) : "cc"
        );
        return a;
}

int main(int argc, char *argv[])
{
        int a, b;

        a = (argc > 1) ? atoi(argv[1]) : 0;
        b = (argc > 2) ? atoi(argv[2]) : 0;

        return printf("%d x %d = %d\n", a, b, mul(a, b)) < 1;
}

De la page d'information .

- Introduire dans le code quelque chose d'extrêmement inacceptable ou déraisonnable qui ne peut pas être supprimé sans tout jeter, ce qui rend la réponse totalement inutile pour le PO.

- […] L'intention est de faire les devoirs dans une langue que le PO paresseux pourrait juger acceptable, mais qui le frustre encore.

Runium
la source
2
"sans utiliser les opérateurs de multiplication et d'addition". Bien plier les règles - cela sera absolument inutile pour le demandeur :-)
John Dvorak
2
Ce n'est pas vraiment une solution en C De plus, il ne parvient pas à compiler sur mon ARM9.
Abarnert
1
@abarnert: Impossible de reconnaître votre déclaration en tant qu'argument pertinent.
Runium
@Sukminder: La question est "Est-il possible d'écrire un programme C…?" L'assemblage en ligne n'est pas un C. Le fait que certains compilateurs C puissent également l'assemblage en ligne ne change rien à cela, pas plus que le fait que certains compilateurs C peuvent également effectuer du C ++ ou ObjC fait que C ++ ou ObjC sont comptés en tant que code C.
Abarnert
2
@abarnert: C'est un code incorporé largement utilisé dans les programmes C. Même si elle est une race croisée on peut affirmer qu'il est un programme C . Il est même plausible que l'OP le reconnaisse en tant que code C. Ce n'est clairement pas Python, ou?
Runium
5
#include <stdio.h>
#include <stdlib.h>

int mult (int n1, int n2);
int add (int n1, int n2 );
int main (int argc, char** argv)
{
        int a,b;
        a = atoi(argv[1]);
        b = atoi(argv[2]);

        printf ("\n%i times %i is %i\n",a,b,mult(a,b));
        return 0;
}

int add (int n1, int n2 )
{
        return n1 - -n2;
}

int mult (int n1, int n2)
{
        int sum = 0;
        char s1='p', s2='p';
        if ( n1 == 0 || n2 == 0 ) return 0;
        if( n1 < 0 )
        {
                s1 = 'n';
                n1 = -n1;
        }
        if( n2 < 0 )
        {
                s2 = 'n';
                n2 = -n2;
        }
        for (int i = 1; i <= n2; i = add( i, 1 ))
        {
                sum = add(sum,  n1);
        }
        if ( s1 != s2 ) sum = -sum;
        return sum;
}
jaybers
la source
5

Est-il possible d'écrire un programme en langage C qui multiplie deux nombres sans utiliser les opérateurs de multiplication et d'addition?

Sûr:

void multiply() {
    printf("6 times 7 is 42\n");
}

Mais bien sûr, c'est de la triche. il veut évidemment pouvoir _soumettre) deux nombres, non?

void multiply(int a, int b) {
    int answer = 42;
    if (answer / b != a || answer % b) {
        printf("The answer is 42, so that's the wrong question.\n");
    } else {
        printf("The answer is 42, but that's probably not the right question anyway.\n");
    }
}
abarnert
la source
Pourquoi, ce n'était pas évident du tout pour moi!
leewz
4

Il n'y a pas d'arithmétique comme l'arithmétique de pointeur:

int f(int a, int b) {
        char x[1][b];
        return x[a] - x[0];
}

int
main(int ac, char **av) {
        printf("%d\n", f(atoi(av[1]),atoi(av[2])));
        return 0;
}

La fonction fimplémente la multiplication. mainl'appelle simplement avec deux arguments.
Fonctionne aussi pour les nombres négatifs.

Ugoren
la source
Négatif a, oui, négatif, bje ne le pense pas. Mais c'est réparable de nombreuses manières créatives. Le plus simple serait sign_a ^ = sign_b, sign_b = 0.
MSalters
@MSalters, testé et fonctionne pour toutes les combinaisons de signes (avec Linux / gcc).
Ugoren
3

C #

Je pense que la soustraction et la négation ne sont pas autorisées ... Quoi qu'il en soit:

int mul(int a, int b)
{
    int t = 0;
    for (int i = b; i >= 1; i--) t -= -a;
    return t;
}
thepirat000
la source
1
C’est exactement la solution à laquelle je pensais ... mais en arrivant à la soirée en retard, je savais qu’il suffisait de faire défiler la liste jusqu’à ce que j’aperçois que quelqu'un l’a déjà écrite. Pourtant, vous obtenez un - (- 1) de moi.
Floris
3

C avec SSE intrinsics (car tout va mieux avec SIMD):

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

static float mul(float a, float b)
{
    float c;

    __m128 va = _mm_set1_ps(a);
    __m128 vb = _mm_set1_ps(b);
    __m128 vc = _mm_mul_ps(va, vb);
    _mm_store_ss(&c, vc);
    return c;
}

int main(int argc, char *argv[])
{
    if (argc > 2)
    {
        float a = atof(argv[1]);
        float b = atof(argv[2]);
        float c = mul(a, b);
        printf("%g * %g = %g\n", a, b, c);
    }
    return 0;
}

Le gros avantage de cette implémentation est qu’elle peut être facilement adaptée pour effectuer 4 multiplications parallèles sans *ou +si nécessaire.

Paul R
la source
Je ne pense pas que ce soit à la traîne ...
John Dvorak
Vraiment ? Je pensais que l'utilisation inutile, gratuite et spécifique à l'architecture de SIMD le qualifierait pour le trolling de code?
Paul R
hmm ... vrai. Je ne savais pas que c'était spécifique à l'architecture.
John Dvorak
3
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define INF 1000000

char cg[INF];

int main()
{
    int a, b;

    char bg[INF];
    memset(bg, '*', INF);

    scanf("%d%d", &a, &b);

    bg[b] = 0;

    while(a--)  
        strcat(cg, bg);

    int result;
    printf("%s%n",cg,&result);
    printf("%d\n", result);

    return 0;
}
  • ne fonctionne que pour un résultat de multiplication <1 000 000
  • Jusqu'à présent, ne peut pas se débarrasser de - opérateur, éventuellement améliorer ici
  • utiliser le spécificateur de format% n dans printf pour compter le nombre de caractères imprimés (je le poste principalement pour rappeler l'existence de% n en C, au lieu de% n pourrait bien sûr être strlen, etc.)
  • Imprime les caractères a * b de '*'
Marol
la source
Nous attendons maintenant la solution "d'émulation de la machine".
Gregory
1
while strlen(cg) != aest une méthode très efficace pour éliminer le --(le rend O (N * N)).
MSalters
3

Probablement trop vite :-(

   unsigned int add(unsigned int a, unsigned int b)
    {
        unsigned int carry;

        for (; b != 0; b = carry << 1) {
            carry = a & b;
            a ^= b;
        }
        return a;
    }

    unsigned int mul(unsigned int a, unsigned int b)
    {
        unsigned int prod = 0;

        for (; b != 0;  a <<= 1, b >>= 1) {
            if (b & 1)
                prod = add(prod, a);
        }
        return prod;
    }
David Laight
la source
1
Ungh. Ce n'est pas à la traîne. C'est un moyen tout à fait raisonnable de le faire.
John Dvorak le
1
C'est vraiment parce que c'est trop rapide :-)
Timtech le
3

Cette version de Haskell ne fonctionne qu'avec des entiers non négatifs, mais elle multiplie de la manière dont les enfants l’apprennent pour la première fois. Par exemple, 3x4 est 3 groupes de 4 choses. Dans ce cas, les "choses" prises en compte sont des entailles ('|') sur un bâton.

mult n m = length . concat . replicate n . replicate m $ '|'
Mhwombat
la source
3
int multiply(int a, int b) {
    return sizeof(char[a][b]);
}

Cela peut fonctionner en C99, si le temps le permet, et si votre compilateur prend en charge les absurdités non définies.

Konrad Borowski
la source
3

Puisque le PO n'a pas demandé le C , en voici un en SQL (Oracle)!

WITH
   aa AS (
      SELECT LEVEL AS lvl 
      FROM dual
      CONNECT BY LEVEL <= &a
   ),
   bb AS (
      SELECT LEVEL AS lvl
      FROM dual
      CONNECT BY LEVEL <= &b
   )
SELECT COUNT(*) AS addition
FROM (SELECT * FROM aa UNION ALL SELECT * FROM bb);

WITH
   aa AS (
      SELECT LEVEL AS lvl 
      FROM dual
      CONNECT BY LEVEL <= &a
   ),
   bb AS (
      SELECT LEVEL AS lvl
      FROM dual
      CONNECT BY LEVEL <= &b
   )
SELECT COUNT(*) AS multiplication
FROM aa CROSS JOIN bb;
SQB
la source
1
Mon Dieu, c'est plein de *s!
Paul R
1
@PaulR :) mais ce ne sont pas des opérateurs .
SQB
2
int add(int a, int b) {
    return 0 - ((0 - a) - b);
}

int mul(int a, int b) {
    int m = 0;
    for (int count = b; count > 0; m = add(m, a), count = add(count, 0 - 1)) { }
    return m;
}

Peut contenir des traces d'UD.

Joker_vD
la source
2
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
  int x = atoi(argv[1]);
  int y = atoi(argv[2]);
  FILE *f = fopen("m","wb");
  char *b = calloc(x, y);
  if (!f || !b || fwrite(b, x, y, f) != y) {
    puts("503 multiplication service is down for maintenance");
    return EXIT_FAILURE;
  }
  printf("%ld\n", ftell(f));
  fclose(f);
  remove("m");
  return 0;
}

Essai:

$ ./a.out 1 0
0
$ ./a.out 1 1
1
$ ./a.out 2 2
4
$ ./a.out 3 2
6
$ ./a.out 12 12
144
$ ./a.out 1234 1234
1522756
Kaz
la source