Come scrivere un programma C per la moltiplicazione senza usare gli operatori * e +?


68

È possibile scrivere un programma C che moltiplica due numeri senza usare gli operatori di moltiplicazione e addizione?

L'ho trovato su Stack Overflow . Aiuta questo povero programmatore a risolvere il suo problema. E per favore non dare risposte come c = a/(1/((float)b)), che è esattamente lo stesso di c = a*b. (Ed è già stato dato come risposta.)

Vince la risposta con il maggior numero di voti il ​​19 gennaio 2014.

Nota: questa è una domanda di . Si prega di non prendere sul serio la domanda e / o le risposte. Ulteriori informazioni sono in code-trolling .


2
@PaulR usa la tua fantasia
John Dvorak,

26
Code-golf.SE non dovrebbe essere un posto per deridere le domande che hai visto su StackOverflow.
Gareth,

17
@Gareth, sei sicuro? La prima riga di ciò suggerisce che questo può essere abbastanza appropriato.
Darren Stone,

5
Sto aspettando che qualcuno scriva un algoritmo basato sul sonno
kb_sou,

21
Questa domanda non è così ridicola come sembra. L'hardware effettivo del computer (transistor) non ha operazioni di moltiplicazione e aggiunta, ma ha operazioni logiche di base come NOT, AND, OR, XOR. Capire come rispondere a questa domanda può darti un'idea eccellente di come un computer funziona davvero a livello di porte logiche.
Gabe,

Risposte:


147

Usa sempre la ricorsione

La recusione è la strada giusta!

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));
}

8
Darei +3 se potessi: uno per la ricorsione finale, uno per ??::senza parentesi, uno per risolvere il problema senza cercare di modificare le regole;)
yo

10
Se Picasso fosse un programmatore ...
R Hughes, il

4
@SargeBorsch Ma dove sarebbe il divertimento in quello ?
Oberon,

3
@HC_ La incfunzione verifica il suo argomento per vedere se il bit più basso è 1; in tal caso, si richiama sui rimanenti bit superiori dell'argomento e restituisce il risultato con lo stesso bit basso su cui era stato impostato il check 0, mentre in caso contrario (ovvero il bit più basso è 0), lo sostituisce 0con a 1e restituisce il risultato . Il processo è molto simile a quello che faresti se aggiungessi i valori a mano, cifra binaria per cifra binaria.
JAB

2
La funzione di incremento non va in un ciclo infinito per -1? (0xFFFF) ideone mostra (-1 >> 1) == -1.
Destrictor

87

Dovrai compilare il programma ogni volta, ma moltiplica qualsiasi numero intero positivo esattamente in qualsiasi versione di C o C ++.

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

 typedef char buffer[A][B];

 main() {
    printf("%d\n",sizeof(buffer));
 }

4
Inseriscilo in una struttura e non hai bisogno di memoria.
Ben Jackson,

4
Hahahah fantastico !!
Almo,

1
usa la "%zu"stringa di formato.
Grijesh Chauhan,

5
solo sizeof(char[A][B])funzionerà (a meno che A <= 0 o B <= 0 o A * B overflow, nel qual caso si dovrebbe ottenere un 'tipo cattivo' sorta di errore)
Greggo

3
@DavidRicherby - Potrei semplificare il codice main(){return sizeof(char[A][B]);}e compilarlo usandocc -DA=6 -DB=7 a.c; ./a.out; echo $?
Mark Lakata

47

Se stai bene con una piccola imprecisione, puoi usare il metodo 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));
}

Esempio:

$ ./mul 300 250
300 * 250 = 74954

Immagino che questo potrebbe essere abbastanza buono;)


3
Hai il mio voto positivo. Ho sentito che Monte Carlo è ciò che la NASA usa per la sua aritmetica. Ma mi piacerebbe vederlo senza le due istanze ++dell'operatore.
Darren Stone,

1
@DarrenStone-= -1
Timtech,

20
@Timtech |= 1(funzionerà sul 50% dei numeri, il 100% delle volte)
Darren Stone

2
+1, ma notando che potrebbe essere troppo lento e potresti aggiungere il supporto multi-thread, bloccando attentamente il "count ++" :-)
greggo

1
C'è sempre printfincremento: printf("%*cc%n\n", count, &count, 'c');(Stampa i tempi di conteggio 'c', quindi un altro 'c' e memorizza il numero di caratteri count
riscritti

45

Dato che non hai specificato quale dimensione del numero, suppongo che intendi due numeri da un bit.

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

Se si desidera un'implementazione massimamente efficiente, utilizzare la seguente piccola implementazione:

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

Nota che accetta solo bit anche se i tipi sono ints impliciti - richiede meno codice ed è quindi più efficiente. (E sì, si compila.)


8
Bello. Una deliberata interpretazione errata della domanda :-)
John Dvorak,

6
È possibile ottimizzare questo a return a && b;. È più corto, quindi è più veloce.
Ry,

1
@minitech: ho deciso di non farlo per peggiorare leggermente il codice. Se volessi andare oltre, lo farei return a&b;.
Cel Skeggs,

1
C deve #include<stdbool.h>definire truee false.
Leewz,

1
Sì, #include<stdbool.h>sembra essere solo tre #defines che si possono fare da soli ( true, false, bool, e una bandiera per marcare che è stato attivato). Puoi anche prendere uno scherzo da una delle altre risposte e usare implicitamente intla versione "breve".
leewz,

31

Ecco un semplice script di shell per farlo:

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 ]*$"

AGGIORNAMENTO: Naturalmente, per farlo in C, basta avvolgerlo exec("bash", "-c", ...). (Grazie, AmeliaBR)


41
Non riesco a decidere quale sia più orribile. Che stai esternalizzando i tuoi calcoli a un motore di ricerca o che il tuo motore di ricerca scelto è Bing. Sfortunatamente, non penso che funzionerà per il nostro OP senza felicità, che aveva bisogno di qualcosa in C.
AmeliaBR,

5
Grazie per aver colto quella svista. Cordiali saluti, sto usando Bing perché Google rende più complicato inviare richieste come questa: devi aggiungere intestazioni per convincere Google che la tua richiesta proviene davvero da un browser.
Vroo,

2
@abarnert hmm ... Bing capisce i "tempi"? Wolfram Alpha potrebbe, però.
John Dvorak,

2
@JanDvorak: Sì, Wolfram funziona. (Nota %20per evitare di usare +segni.) Ma devi ancora analizzare l'output (in C) per ottenere il valore da esso. Il che sarà particolarmente complicato, poiché l'output sembra essere un'immagine, non testo. L'analisi HTML e l'OCR potrebbero rendere la migliore risposta possibile a questo problema.
Abarnert,

3
@JanDvorak: non è divertente. Non vedevo l'ora che qualcuno scrivesse una semplice libreria OCR senza aggiunta o moltiplicazione ...
abarnert,

27

Facciamo una ricerca dimezzante ricorsiva tra INT64_MIN e 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));
}

PS Sarà felicemente sigsegv con alcuni valori. ;)


18

Sfortunatamente, questo funziona solo per numeri interi.

Poiché l'addizione non è consentita, costruiamo prima un operatore di incremento:

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

Successivamente, dobbiamo gestire il segno. Innanzitutto, trova il bit di segno:

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

Quindi prendi il segno e la grandezza di ogni argomento. Per annullare un numero nel complemento a due, invertire tutti i bit e aggiungerne uno.

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

Per moltiplicare due numeri interi positivi, possiamo usare il significato geometrico della moltiplicazione:

// 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);

troll:

  • L'aggiunta non è consentita, ma a++conta davvero come aggiunta? Scommetto che l'insegnante intendeva permetterlo.
  • Si basa sul complemento a due, ma si tratta di un comportamento definito dall'implementazione e la piattaforma di destinazione non è stata specificata.
  • Allo stesso modo, si presume che anche la sottrazione e la divisione siano vietate.
  • << è in realtà una moltiplicazione per una potenza di due, quindi tecnicamente dovrebbe essere vietato.
  • diagramma non obbligatorio è inutile. Inoltre, avrebbe potuto essere trasposto per salvare una riga.
  • lo spostamento ripetuto di -1non è il modo migliore per trovare il bit di segno. Anche se non esistesse una costante incorporata, è possibile eseguire uno spostamento logico a destra di -1, quindi invertire tutti i bit.
  • XOR -1 non è il modo migliore per invertire tutti i bit.
  • L'intera sciarada con la rappresentazione della grandezza del segno è inutile. Basta lanciare l'aritmetica senza segno e modulare farà il resto.
  • poiché il valore assoluto di MIN_INT(AKA signBit) è negativo, questo si interrompe per quel valore. Fortunatamente, funziona ancora nella metà dei casi, perché MIN_INT * [even number] dovrebbe essere zero.Inoltre, si plusOneinterrompe -1, causando infiniti loop ogni volta che il risultato trabocca. plusOnefunziona perfettamente per qualsiasi valore. Dispiace per la confusione.

+1 per un vero troll di codice: sembra che dovrebbe funzionare, ma molto probabilmente esploderà sull'OP e non avrà idea del perché.
Kevin,

1
È possibile eseguire l'aggiunta senza QUALSIASI operatore di aggiunta semplicemente usando shift, XOR ed AND. Tutti questi ++ mi fanno male alla testa - il bit singolo ADD con carry è (x ^ y) | ((x & y) << 1) (modulo eventuali errori causati dalla digitazione in questa piccola casella di testo.)
Julie in Austin,

@JulieinAustin yep. L'algoritmo è ancora più inefficiente di quanto debba essere. Devo modificare l'elenco dei troll? :-)
John Dvorak,

1
@JulieinAustin (x ^ y) | ((x & y) << 1)non funziona del tutto, non propagherà carry quando x o y e carry sono entrambi veri nella stessa posizione :)
hobbs,

Soluzione @hobbs: anziché ORing, aggiungerli in modo ricorsivo se carry è diverso da zero.
John Dvorak,

14

Funziona anche con numeri in virgola mobile:

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

11

Tutti sanno che Python è più facile da usare rispetto a C. E Python ha funzioni corrispondenti a tutti gli operatori, per i casi in cui non è possibile utilizzare l'operatore. Qual è esattamente la nostra definizione del problema, giusto? Così:

#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();
}

10

Nessuna delle altre risposte è teoricamente valida. Come dice il primo commento sulla domanda:

Si prega di essere più specifici sui "numeri"

Dobbiamo definire la moltiplicazione e i numeri prima ancora che sia possibile una risposta. Una volta fatto, il problema diventa banale.

Il modo più popolare per farlo all'inizio della logica matematica è quello di costruire ordinali von Neumann in cima alla teoria degli insiemi ZF , e quindi usare gli assiomi di Peano .

Questo si traduce naturalmente in C, supponendo che tu abbia un tipo di set che può contenere altri set. Non deve contenere altro che set, il che lo rende banale (nessuna di quelle void*sciocchezze espresse nella maggior parte delle librerie di set), quindi lascerò l'implementazione come esercizio per il lettore.

Quindi, prima di tutto:

/* 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;
    }
}

Se vuoi espandere questo a numeri interi, razionali, reali, surreali, ecc., Puoi - con precisione infinita (supponendo che tu abbia memoria e CPU infinite), per avviarlo. Ma come disse Kroenecker, Dio ha creato i numeri naturali; tutto il resto è opera dell'uomo, quindi davvero, perché preoccuparsi?


1
Wow. Sei anche più lento di me.
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 );
}

Se consideri che l'hack a [b] sia un imbroglio (dato che si tratta davvero di un'aggiunta), allora funziona invece. Ma le ricerche delle tabelle implicano anche l'aggiunta di puntatori.

Vedi http://en.wikipedia.org/wiki/IBM_1620 - un computer che effettivamente ha fatto l'aggiunta utilizzando le tabelle di ricerca ...

Qualcosa di soddisfacente nell'uso di un meccanismo di tabella per "accelerare" un'operazione che potrebbe effettivamente essere eseguita in un'istruzione.

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;
}

Oops, c'è *char (anche se non è moltiplicazione)
Sarge Borsch,

Uh, la ricerca della tabella usa l'addizione - (a [i]) è uguale a (* (a + i)).
Julie ad Austin,

@JulieinAustin L'ho detto. La ricerca della tabella può essere eseguita senza aggiunte, unendo i campi (come illustrato nell'IBM 1620, consultare il link) ma è complicato configurarlo in C - per prima cosa è necessario allineare la tabella a un indirizzo corretto in modo che gli indici possano essere appena o dentro.
Greggo

8

Non sono sicuro di cosa significhi "barare" in questi post "code troll", ma questo moltiplica 2 numeri interi arbitrari, in fase di esecuzione, senza operatore *o +utilizzando librerie standard (C99).

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

8

La mia soluzione troll per 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;
}

1
+1 Buona implementazione della valutazione carry. Mi piace il tuo codice :)
yo'

@ BЈовић: colpa mia, ho pensato che la pesca a traina non riguardasse la comprensione. Nomi modificati e commenti aggiunti.
Matthias,

scusa. Ho frainteso che cos'è quel tag e di cosa parla veramente la Q. Dovresti ripristinarlo
BЈовић

@Matthias in questo caso è utile capire come funziona in modo da poter apprezzare quanto sia contorta tale operazione di trasporto convergente. In una situazione reale di troll di codice i commenti potrebbero essere redatti :-)
greggo

Vorrei sottolineare che se si desidera aggiungere numeri con inversione di bit (con il valore da alto a basso) e non si ha un'istruzione 'bitrev', questo è probabilmente un approccio perfettamente ragionevole (dopo essere passati a c> > = 1 ovviamente)
greggo

7

Ci sono molte buone risposte qui, ma non sembra che molti di loro traggano vantaggio dal fatto che i computer moderni sono davvero potenti. Esistono più unità di elaborazione nella maggior parte delle CPU, quindi perché utilizzarne una sola? Possiamo sfruttare questo per ottenere grandi risultati prestazionali.

#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;
}

Ecco un esempio del suo utilizzo:

$ ./multiply
5 6
5 x 6 = 30

La #pragma omp paralleldirettiva fa sì che OpenMP divida ciascuna parte del ciclo for in un'unità di esecuzione diversa, quindi stiamo moltiplicando in parallelo!

Nota che devi usare il -fopenmpflag per dire al compilatore di usare OpenMP.


Parti del troll:

  1. Ingannevole sull'uso della programmazione parallela.
  2. Non funziona su numeri negativi (o grandi).
  3. In realtà non divide le parti del forloop: ogni thread esegue il loop.
  4. Nomi delle variabili fastidiosi e riutilizzo delle variabili.
  5. C'è una sottile condizione di gara answer--; il più delle volte, non verrà visualizzato, ma a volte causerà risultati imprecisi.

2
Perché non combinare questo con la risposta SIMD di Paul R, in modo da poter eseguire 32 volte più veloce invece di 8 volte? Anche se davvero, vuoi coinvolgere la GPU e i core; allora sarebbe davvero ardente. :)
abarnert,

2
Potrebbe anche usare OpenMPI per eseguirlo su alcune macchine in parallelo.
millinon

6

Sfortunatamente, la moltiplicazione è un problema molto difficile nell'informatica. La soluzione migliore è utilizzare invece la divisione:

#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));
}

6

Nella vita reale di solito rispondo alla pesca a traina con conoscenza, quindi ecco una risposta che non trollerà affatto. Funziona per tutti i intvalori, per quanto posso vedere.

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;
}

Questo è, per quanto ne so, molto simile a come una CPU potrebbe effettivamente fare la moltiplicazione dei numeri interi. Innanzitutto, ci assicuriamo che almeno uno degli argomenti ( a) sia positivo capovolgendo il segno su entrambi se aè negativo (e no, mi rifiuto di considerare la negazione come una sorta di addizione o moltiplicazione). Quindi il while (a)loop aggiunge una copia spostata bal risultato per ogni bit impostato in a. Il doloop implementa r += xusando e, xor, e spostandosi in quello che è chiaramente un insieme di mezzo-additivi, con i bit di riporto dentro xfino a quando non ce ne sono più (una vera CPU userebbe additivi completi, il che è più efficiente, ma C non lo fa t abbiamo gli operatori di cui abbiamo bisogno per questo, a meno che tu non conti l' +operatore).


4
Il richiedente non troll. Dovresti trollare.
John Dvorak,

2
È un troll invisibile! L'errore segreto si trova in == INT_MIN.
Jander

1
@Jander hmm. Sì, è buono. Sto indovinando (sui normali sistemi a complemento a due) il risultato della negazione di a è ancora negativo e il while(a)ciclo non termina mai.
Hobbs

@hobbs Sì, mi sembra giusto. Altrimenti una risposta molto carina.
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;
}

1
Questo fallirà orribilmente se il risultato trabocca. Bello! Immagino che non dovresti stampare, comunque.
John Dvorak,

2
fallisce per a = 2, b = 2, c = 5
BЈовић

@ BЈовић: while(C/A != B || C%A)?
Abarnert,

2
Si noti che questo è davvero un tentativo di fare la stessa cosa del successore di Deep Thought, ma per tutti forse gli universi , anziché solo quello in cui la risposta è 42. Sarebbe davvero impressionante se non fosse per il bug. E la mancanza di gestione degli errori in caso di Vogons.
Abarnert,

1
Ha bisogno di più thread. Sai, per renderlo efficiente.
Greggo,

6

Lanciando questo nel mix:

#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;
}

Dalla pagina di informazioni .

- Introdurre qualcosa di estremamente inaccettabile o irragionevole nel codice che non può essere rimosso senza buttare via tutto, rendendo la risposta completamente inutile per l'OP.

- […] L'intenzione è di fare i compiti in una lingua che l'OP pigro potrebbe ritenere accettabile, ma lo frustra ancora.


2
"senza utilizzare gli operatori di moltiplicazione e addizione". Bella piegatura delle regole - questo sarà assolutamente inutile per chi chiede :-)
John Dvorak,

2
Questa non è davvero una soluzione C. Inoltre, non riesce a compilare sul mio ARM9.
Abarnert,

1
@abarnert: impossibile riconoscere la tua dichiarazione come argomento pertinente.
Runium,

@Sukminder: La domanda è "È possibile scrivere un programma C ...?" L'assemblaggio in linea non è C. Il fatto che alcuni compilatori C possano anche eseguire l'assemblaggio in linea non cambia ciò, non più del fatto che alcuni compilatori C possono anche fare C ++ o ObjC fa contare C ++ o ObjC come codice C.
Abarnert,

2
@abarnert: è un codice incorporato ampiamente utilizzato nei programmi C. Anche se si tratta di un incrocio si può sostenere che è un programma di C . È anche plausibile che OP lo riconoscerebbe come codice C. Chiaramente non è Python, o?
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;
}

5

È possibile scrivere un programma C che moltiplica due numeri senza usare gli operatori di moltiplicazione e addizione?

Sicuro:

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

Ma ovviamente questo è barare; ovviamente vuole essere in grado di fornire due numeri, giusto?

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");
    }
}

Perché, per me non era affatto ovvio!
leewz,

4

Non c'è aritmetica come l'aritmetica del puntatore:

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 funzione fimplementa la moltiplicazione. mainlo chiama semplicemente con due argomenti.
Funziona anche con numeri negativi.


Negativo a, sì, negativo bNon credo. Ma questo è risolvibile in molti modi creativi. Il più semplice sarebbe sign_a ^ = sign_b, sign_b = 0.
MSalters

@MSalters, testato e funziona per tutte le combinazioni di segni (con Linux / gcc).
Ugoren,

3

C #

Penso che la sottrazione e la negazione non siano consentite ... Comunque:

int mul(int a, int b)
{
    int t = 0;
    for (int i = b; i >= 1; i--) t -= -a;
    return t;
}

1
Questa è esattamente la soluzione a cui ho pensato ... ma venendo alla festa tardi sapevo che si trattava di scorrere verso il basso fino a quando non ho scoperto che qualcuno l'aveva già scritto. Tuttavia, ricevi un - (- 1) da me.
Floris,

3

C con intrinseci SSE (perché tutto è meglio con 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;
}

Il grande vantaggio di questa implementazione è che può essere facilmente adattato per eseguire 4 moltiplicazioni parallele senza *o +se necessario.


Non penso che questo sia il trolling ...
John Dvorak,

Veramente ? Ho pensato che l'uso inutile, gratuito e specifico dell'architettura di SIMD lo avrebbe qualificato per il trolling del codice?
Paul R,

hmm ... vero. Non avevo capito che questo era specifico dell'architettura.
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;
}
  • funziona solo per risultato di moltiplicazione <1 000 000
  • Finora non posso liberarmi di - operatore, forse migliorando qui
  • usando lo specificatore di formato% n in printf per contare il numero di caratteri stampati (invio questo principalmente per ricordare l'esistenza di% n in C, invece di% n potrebbe ovviamente essere strlen ecc.)
  • Stampa i caratteri a * b di '*'

Ora aspetto la soluzione di "emulazione della macchina turing".
Greggo,

1
while strlen(cg) != aè un metodo molto da traina per eliminare --(lo rende O (N * N)).
Salterio del

3

Probabilmente troppo veloce :-(

   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;
    }

1
Ungh. Questo non è traina. Questo è un modo del tutto ragionevole per farlo.
John Dvorak,

1
È vero perché è troppo veloce :-)
Timtech,

3

Questa versione di Haskell funziona solo con numeri interi non negativi, ma esegue la moltiplicazione nel modo in cui i bambini la apprendono per la prima volta. Vale a dire, 3x4 è 3 gruppi di 4 cose. In questo caso, le "cose" che vengono contate sono tacche ('|') su un bastone.

mult n m = length . concat . replicate n . replicate m $ '|'

3
int multiply(int a, int b) {
    return sizeof(char[a][b]);
}

Questo può funzionare in C99, se il tempo è giusto, e il tuo compilatore supporta assurdità non definite.


3

Dato che l'OP non ha richiesto C , eccone uno in (Oracle) SQL!

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;

1
Mio Dio, è pieno di *s!
Paul R,

1
@PaulR :) ma non sono operatori .
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;
}

Può contenere tracce di UD.


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;
}

Prova:

$ ./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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.