Jak napisać program do mnożenia bez użycia operatorów * i +?


68

Czy można napisać program w C, który zwielokrotnia dwie liczby bez użycia operatorów mnożenia i dodawania?

Znalazłem to na stosie przepełnienia . Pomóż temu biednemu programiście rozwiązać jego problem. I proszę, nie udzielaj odpowiedzi typu c = a/(1/((float)b)), który jest dokładnie taki sam jak c = a*b. (I jest już podany jako odpowiedź).

Odpowiedź z największą popularnością 19 stycznia 2014 r. Wygrywa.

Uwaga: jest to pytanie . Proszę nie brać poważnie pytania i / lub odpowiedzi. Więcej informacji znajduje się w trollingu kodu .


2
@PaulR użyj swojej fantazji
John Dvorak

26
Code-golf.SE nie powinien być miejscem, w którym możesz kpić z pytań, które widziałeś na StackOverflow.
Gareth,

17
@Gareth, jesteś pewien? Pierwsza linia to sugeruje to może być całkiem właściwe.
Darren Stone

5
Czekam, aż ktoś napisze algorytm oparty na śnie
kb_sou

21
To pytanie nie jest tak śmieszne, jak się wydaje. Rzeczywisty sprzęt komputerowy (tranzystory) nie ma operacji mnożenia i dodawania - ma podstawowe operacje logiczne, takie jak NOT, AND, OR, XOR. Zastanawianie się, jak odpowiedzieć na to pytanie, może dać doskonały wgląd w to, jak komputer naprawdę działa na poziomie bramek logicznych.
Gabe,

Odpowiedzi:


147

Zawsze używaj rekurencji

Recusion to właściwa droga!

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
Dałbym +3, gdybym mógł: jeden za ostateczną rekurencję, jeden za ??::bez nawiasów, jeden za rozwiązanie problemu bez próby zmiany zasad;)
yo

10
Gdyby Picasso był programistą ...
R Hughes,

4
@SargeBorsch Ale skąd zabawy być w to ?
Oberon

3
@HC_ incFunkcja sprawdza swój argument, aby sprawdzić, czy najniższym bitem jest 1; jeśli tak, wywołuje się na pozostałych górnych bitach argumentu i zwraca wynik z tym samym bitem niskim, który został sprawdzony 0, a jeśli nie (tj. bitem najniższym 0), zamienia to na 0a 1i zwraca wynik . Proces jest bardzo podobny do tego, co zrobiłbyś, gdybyś dodawał wartości ręcznie, cyfra binarna po cyfrze binarnej.
JAB

2
Czy funkcja inkrementacji nie przechodzi w nieskończoną pętlę dla -1? (0xFFFF) ideone pokazuje (-1 >> 1) == -1.
Destrictor,

87

Będziesz musiał skompilować program za każdym razem, ale robi to mnożenie dodatnich liczb całkowitych dokładnie w dowolnej wersji C lub C ++.

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

 typedef char buffer[A][B];

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

4
Umieść go w strukturze i nie potrzebujesz pamięci.
Ben Jackson

4
Hahahah świetnie !!
Almo

1
użyj "%zu"ciągu formatu.
Grijesh Chauhan

5
po prostu sizeof(char[A][B])zadziała (chyba że A <= 0 lub B <= 0 lub A * B przepełnia się, w takim przypadku powinieneś otrzymać błąd typu „zły typ”)
greggo

3
@DavidRicherby - Mogę uprościć kod, main(){return sizeof(char[A][B]);}a ty skompilujesz używająccc -DA=6 -DB=7 a.c; ./a.out; echo $?
Mark Lakata

47

Jeśli masz trochę niedokładności, możesz użyć metody 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));
}

Przykład:

$ ./mul 300 250
300 * 250 = 74954

Myślę, że to może być wystarczająco dobre;)


3
Masz mój głos. Słyszałem, że Monte Carlo używa NASA do obliczeń. Ale chciałbym to zobaczyć bez dwóch wystąpień ++operatora.
Darren Stone

1
@DarrenStone-= -1
Timtech

20
@Timtech |= 1(będzie działać na 50% liczb, w 100% przypadków)
Darren Stone

2
+1, ale zauważając, że może to być zbyt wolne, i możesz dodać obsługę wielu wątków, ostrożnie blokując 'count ++' :-)
greggo 13.01.14

1
Zawsze jest printfprzyrost: printf("%*cc%n\n", count, &count, 'c');(Drukuje „c” zliczanie razy, potem kolejne „c” i zapisuje liczbę znaków count
wpisanych

45

Ponieważ nie podałeś wielkości, zakładam, że masz na myśli dwie jednobitowe liczby.

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

Jeśli chcesz maksymalnie wydajnej implementacji, użyj następującej niewielkiej implementacji:

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

Zauważ, że nadal akceptuje tylko bity, mimo że typy są niejawnymi liczbami całkowitymi - zajmuje mniej kodu i dlatego jest bardziej wydajny. (I tak, to się kompiluje.)


8
Miły. Celowa błędna interpretacja pytania :-)
John Dvorak

6
Możesz to zoptymalizować do return a && b;. Jest krótszy, więc jest szybszy.
Ry-

1
@minitech: Zdecydowałem się tego nie robić, aby nieco pogorszyć kod. Gdybym chciał pójść z tym dalej, zrobiłbym to return a&b;.
Cel Skeggs,

1
C musi #include<stdbool.h>zdefiniować truei false.
leewz

1
Tak, #include<stdbool.h>wydaje się być tylko trzy #defines, które można zrobić samemu ( true, false, bool, i flagi z okazji, że został aktywowany). Możesz także wziąć lewę z jednej z pozostałych odpowiedzi i użyć niejawnie intdla „krótkiej” wersji.
leewz

31

Oto prosty skrypt powłoki, aby to zrobić:

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

AKTUALIZACJA: Oczywiście, aby to zrobić w C, po prostu ją zawiń exec("bash", "-c", ...). (Dzięki, AmeliaBR)


41
Nie mogę zdecydować, co jest bardziej okropne. Że zlecasz swoje obliczenia wyszukiwarce lub że wybraną przez Ciebie wyszukiwarką jest Bing. Niestety nie sądzę, że to zadziała dla naszego nieszczęsnego OP, który potrzebował czegoś w C.
AmeliaBR

5
Dzięki za uchwycenie tego przeoczenia. Do Twojej wiadomości używam Bing, ponieważ Google komplikuje wysyłanie takich wniosków - musisz dodać nagłówki, aby przekonać Google, że Twoje zapytanie pochodzi naprawdę z przeglądarki.
Vroo

2
@abarnert hmm ... czy Bing rozumie „czasy”? Wolfram Alpha może jednak.
John Dvorak

2
@JanDvorak: Tak, Wolfram działa. (Zwróć uwagę, %20aby uniknąć używania jakichkolwiek +znaków.) Ale nadal musisz przeanalizować dane wyjściowe (w C), aby uzyskać z nich wartość. Co będzie szczególnie trudne, ponieważ wyjście wydaje się być obrazem, a nie tekstem. Analiza składni HTML plus OCR może sprawić, że będzie to najlepsza możliwa odpowiedź na ten problem.
abarnert

3
@JanDvorak: To nie jest zabawne. Nie mogłem się doczekać, aby ktoś napisał prostą bibliotekę OCR bez dodawania lub mnożenia…
abarnert

27

Zróbmy rekurencyjne wyszukiwanie o połowę między INT64_MIN a 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 Z radością sigsegv z pewnymi wartościami. ;)


18

Niestety działa to tylko w przypadku liczb całkowitych.

Ponieważ dodawanie jest niedozwolone, najpierw stwórzmy operator inkrementacji:

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

Następnie musimy obsłużyć znak. Najpierw znajdź bit znaku:

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

Następnie weź znak i wielkość każdego argumentu. Aby zanegować liczbę w uzupełnieniu do dwóch, odwróć wszystkie bity i dodaj jeden.

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

Aby pomnożyć dwie dodatnie liczby całkowite, możemy użyć geometrycznego znaczenia mnożenia:

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

trolle:

  • Dodawanie jest niedozwolone, ale czy a++tak naprawdę liczy się jako dodawanie? Założę się, że nauczyciel zamierzał na to pozwolić.
  • Opiera się na uzupełnieniu do dwóch, ale jest to zachowanie zdefiniowane w implementacji, a platforma docelowa nie została określona.
  • Podobnie zakłada się, że odejmowanie i dzielenie są również niedozwolone.
  • << jest w rzeczywistości pomnożeniem przez potęgę dwóch, więc technicznie należy go zabronić.
  • niepotrzebny schemat jest niepotrzebny. Ponadto można było transponować, aby zapisać jedną linię.
  • powtarzane przesunięcie -1nie jest najlepszym sposobem na znalezienie bitu znaku. Nawet jeśli nie ma wbudowanej stałej, można wykonać logiczne przesunięcie w prawo o -1, a następnie odwrócić wszystkie bity.
  • XOR -1 nie jest najlepszym sposobem na odwrócenie wszystkich bitów.
  • Cała szarada z reprezentacją wielkości znaku jest niepotrzebna. Wystarczy rzucić na niepodpisaną i modułową arytmetykę zrobi resztę.
  • ponieważ wartość bezwzględna MIN_INT(AKA signBit) jest ujemna, ta wartość jest dzielona na tę wartość. Na szczęście nadal działa w połowie przypadków, ponieważ MIN_INT * [even number] powinno wynosić zero.Ponadto plusOneprzerywa -1, powodując nieskończone pętle za każdym razem, gdy wynik przepełnia się. plusOnedziała dobrze dla dowolnej wartości. Przepraszam za zamieszanie.

+1 za prawdziwego trolla kodowego: Wygląda na to, że powinien działać, ale najprawdopodobniej wysadzi OP i nie będzie miał pojęcia, dlaczego.
Kevin

1
Można dodawać bez ŻADNYCH operatorów dodawania, używając tylko Shift, XOR i AND. Wszystkie te ++ powodują ból głowy - pojedynczy bit ADD z przeniesieniem to (x ^ y) | ((x i y) << 1) (modulo wszelkie błędy spowodowane pisaniem w tym gównianym małym polu tekstowym.)
Julie w Austin

@JulieinAustin tak. Algorytm jest jeszcze bardziej nieefektywny, niż powinien. Czy powinienem zmienić listę trolli? :-)
John Dvorak

1
@JulieinAustin (x ^ y) | ((x & y) << 1)nie działa, nie propaguje przenoszenia, gdy x lub y i przeniesienie są prawdziwe w tej samej pozycji :)
hobbs

Rozwiązanie @hobbs: zamiast ORing, dodaj je rekurencyjnie, jeśli carry jest niezerowy.
John Dvorak,

14

Działa również dla liczb zmiennoprzecinkowych:

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

11

Wszyscy wiedzą, że Python jest łatwiejszy w użyciu niż C. A Python ma funkcje odpowiadające każdemu operatorowi, w przypadkach, gdy nie można go używać. Jaka jest dokładnie nasza definicja problemu, prawda? Więc:

#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

Żadna z pozostałych odpowiedzi nie jest teoretycznie poprawna. Jak mówi pierwszy komentarz do pytania:

Dokładniej określ „liczby”

Musimy zdefiniować mnożenie i liczby, zanim odpowiedź będzie w ogóle możliwa. Gdy to zrobimy, problem stanie się banalny.

Najpopularniejszym sposobem osiągnięcia tego na początku logiki matematycznej jest zbudowanie porządków von Neumanna na podstawie teorii zbiorów ZF , a następnie użycie aksjomatów Peano .

To oczywiście przekłada się na C, zakładając, że masz typ zestawu, który może zawierać inne zestawy. Nie musi zawierać niczego poza zestawami, co czyni go trywialnym (żaden z tych rzutujących void*nonsensów w większości bibliotek zestawów), więc zostawię implementację jako ćwiczenie dla czytelnika.

Po pierwsze:

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

Jeśli chcesz rozszerzyć to na liczby całkowite, racjonalne, rzeczywiste, surrealne itp., Możesz - z nieskończoną precyzją (zakładając, że masz nieskończoną pamięć i procesor), uruchomić. Ale jak to słynie Kroenecker, Bóg stworzył liczby naturalne; wszystko inne jest dziełem człowieka, więc naprawdę po co zawracać sobie głowę?


1
Łał. Jesteś jeszcze wolniejszy ode mnie.
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 );
}

Jeśli uważasz, że hack a [b] jest oszustwem (ponieważ jest to naprawdę dodatek), to działa zamiast tego. Ale wyszukiwanie tabel obejmuje również dodawanie wskaźnika.

Zobacz http://en.wikipedia.org/wiki/IBM_1620 - konwerter, który faktycznie dodawał za pomocą tabel odnośników ...

Coś satysfakcjonującego w używaniu mechanizmu tabeli w celu „przyspieszenia” operacji, którą można by faktycznie wykonać za pomocą jednej instrukcji.

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

Ups, jest *char (chociaż nie jest to mnożenie)
Sarge Borsch

Wyszukiwanie tabeli używa dodawania - (a [i]) to to samo co (* (a + i)).
Julie w Austin,

@JulieinAustin Wspomniałem o tym. Wyszukiwanie tabeli można wykonać bez dodawania, łącząc pola (jak pokazano w IBM 1620, patrz link), ale konfiguracja w C jest nieuporządkowana - po pierwsze, należy wyrównać tabelę do odpowiedniego adresu, aby indeksy mogły być just or'd in.
greggo

8

Nie jestem pewien, co stanowi „oszustwo” w tych „kodowych trollach”, ale to zwielokrotnia 2 dowolne liczby całkowite w czasie wykonywania, bez operatora *lub +przy użyciu standardowych bibliotek (C99).

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

8

Moje rozwiązanie trollowe dla 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 Dobra implementacja oceny carry. Podoba mi się twój kod :)
jo

@ BЈовић: Moja wina, myślałem, że trolling nie polega na zrozumieniu. Zmieniono nazwy i dodano komentarze.
Matthias

Przepraszam. Źle zrozumiałem, co to za tag i na czym tak naprawdę polega Q. Powinieneś to cofnąć
BЈовић

@Matthias w tym przypadku warto zrozumieć, jak to działa, abyśmy mogli docenić, jak pokręcona jest ta operacja zbierania-przenoszenia. W rzeczywistej sytuacji troll-code komentarze mogą zostać
zredagowane

Chciałbym zauważyć, że jeśli chcesz dodać liczby z odwróconą liczbą bitów (z propozycją od high do lo carry) i nie masz instrukcji „bitrev”, jest to prawdopodobnie całkowicie rozsądne podejście (po zmianie na c> > = 1 oczywiście)
greggo

7

Jest tu wiele dobrych odpowiedzi, ale nie wygląda na to, aby wiele z nich skorzystało z faktu, że nowoczesne komputery są naprawdę potężne. W większości procesorów jest wiele jednostek przetwarzania, więc po co używać tylko jednego? Możemy to wykorzystać, aby uzyskać doskonałe wyniki wydajności.

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

Oto przykład jego użycia:

$ ./multiply
5 6
5 x 6 = 30

#pragma omp parallelDyrektywa sprawia OpenMP dzielić każdą część pętli for do innej jednostki wykonawczej, więc jesteśmy pomnożenie równolegle!

Pamiętaj, że musisz użyć -fopenmpflagi, aby poinformować kompilator o użyciu OpenMP.


Części trolli:

  1. Wprowadzające w błąd stosowanie równoległego programowania.
  2. Nie działa na liczbach ujemnych (lub dużych).
  3. W rzeczywistości nie dzieli części forpętli - każdy wątek uruchamia pętlę.
  4. Irytujące nazwy zmiennych i ich ponowne użycie.
  5. Jest subtelny warunek wyścigu answer--; przez większość czasu nie pojawia się, ale czasami powoduje niedokładne wyniki.

2
Dlaczego nie połączyć tego z odpowiedzią SIMD Paula R., abyś mógł uruchomić 32x tak szybko, a nie 8x? Chociaż tak naprawdę chcesz zaangażować procesor graficzny, a także rdzenie; wtedy naprawdę by się zapłonęło. :)
abarnert

2
Równie dobrze można użyć OpenMPI, aby uruchomić go na kilku komputerach równolegle.
millinon

6

Niestety mnożenie jest bardzo trudnym problemem w informatyce. Najlepszym rozwiązaniem jest użycie podziału:

#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

W prawdziwym życiu zwykle reaguję trollingiem na wiedzę, więc oto odpowiedź, która wcale nie trolluje. Działa dla wszystkich intwartości, o ile widzę.

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

Jest to, według mojego najlepszego zrozumienia, bardzo podobne do tego, w jaki sposób procesor faktycznie może zwielokrotniać liczby całkowite. Po pierwsze, upewniamy się, że co najmniej jeden z argumentów ( a) jest dodatni, poprzez włączenie znaku zarówno jeśli ajest ujemny (i nie, odmawiam liczenia negacji jako rodzaju dodawania lub mnożenia). Następnie while (a)pętla dodaje przesuniętą kopię bwyniku do każdego ustawionego bitu a. doPętla implementuje r += xużywania i XOR, a przesunięcie w co wyraźnie zestaw pół dodatkami, z bitami carry zawraca się xaż nie ma więcej (prawdziwy procesor będzie używać pełnych adders, który jest bardziej wydajny, ale C nie robi” t mieć operatorów, których potrzebujemy, chyba że policzysz +operatora).


4
Pytający nie trollował. Ci mają troll.
John Dvorak

2
To podstępny troll! Sekretna awaria jest na == INT_MIN.
Jander

1
@Jander hmm. Tak, to jest dobre. Zgaduję (na zwykłych dwóch systemach dopełniacza) wynik zanegowania a jest nadal ujemny, a while(a)pętla nigdy się nie kończy.
hobbs

@ Hobbs Tak, to mi pasuje. W przeciwnym razie bardzo ładna odpowiedź.
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
To zawiedzie okropnie, jeśli wynik się przepełni. Miły! Myślę jednak, że nie powinieneś drukować.
John Dvorak

2
zawodzi dla a = 2, b = 2, c = 5
--овић

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

2
Zauważ, że tak naprawdę jest to próba zrobienia tego samego, co następca Głębokiej Myśli, ale dla wszystkich możliwie wszechświatów , zamiast tylko tego, w którym odpowiedź wynosi 42. To byłoby bardzo imponujące, gdyby nie błąd. I brak obsługi błędów w przypadku Vogonów.
abarnert

1
Potrzebuje wielu wątków. Wiesz, aby uczynić go wydajnym.
greggo

6

Wrzucając to do miksu:

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

Ze strony informacyjnej .

- Wprowadzenie do kodu czegoś wyjątkowo niedopuszczalnego lub nierozsądnego, którego nie można usunąć bez wyrzucenia wszystkiego, co sprawia, że ​​odpowiedź jest całkowicie bezużyteczna dla OP.

- […] Chodzi o odrabianie pracy domowej w języku, który leniwy OP może uznać za akceptowalny, ale nadal go frustruje.


2
„bez użycia operatorów mnożenia i dodawania”. Niezłe naginanie zasad - to będzie całkowicie bezużyteczne dla pytającego :-)
John Dvorak

2
To nie jest tak naprawdę rozwiązanie C. Ponadto nie można go skompilować na moim ARM9.
abarnert

1
@abarnert: Nie rozpoznano twojego oświadczenia jako odpowiedniego argumentu.
Runium

@Sukminder: Pytanie brzmi: „Czy można napisać program C…?” Asembler nie jest C. Fakt, że niektóre kompilatory C mogą również wykonywać inline, nie zmienia tego, podobnie jak fakt, że niektóre kompilatory C mogą także C ++ lub ObjC sprawiają, że C ++ lub ObjC są liczone jako kod C.
abarnert

2
@abarnert: Jest to kod osadzony szeroko stosowany w programach C. Nawet jeśli to jest cross-rasa można argumentować, że jest to program C . Jest nawet prawdopodobne, że OP rozpoznałby go jako kod C. Wyraźnie nie jest to python lub?
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

Czy można napisać program w C, który zwielokrotnia dwie liczby bez użycia operatorów mnożenia i dodawania?

Pewnie:

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

Ale to oczywiście oszustwo; najwyraźniej chce być w stanie podać dwie liczby, prawda?

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

To wcale nie było dla mnie oczywiste!
leewz

4

Nie ma arytmetyki takiej jak arytmetyka wskaźnikowa:

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

Funkcja fimplementuje mnożenie. mainpo prostu wywołuje to z dwoma argumentami.
Działa również dla liczb ujemnych.


Negatywne a, tak, negatywne bNie sądzę. Ale można to naprawić na wiele kreatywnych sposobów. Najprostszym byłoby znak_a ^ = znak_b, znak_b = 0.
MSalters

@MSalters, przetestowany i działa dla wszystkich kombinacji znaków (w systemie Linux / gcc).
ugoren

3

DO#

Myślę, że odejmowanie i negowanie są niedozwolone ... W każdym razie:

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

1
Właśnie o tym pomyślałem ... ale spóźniając się na przyjęcie wiedziałem, że to kwestia przewijania w dół, dopóki nie odkryłem, że ktoś już to napisał. Mimo to dostajesz ode mnie - (- 1).
Floris,

3

C z właściwościami SSE (ponieważ wszystko jest lepsze z 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;
}

Dużą zaletą tej implementacji jest to, że można ją łatwo dostosować do wykonywania 4 równoległych multiplikacji bez *lub w +razie potrzeby.


Nie sądzę, że to trolling ...
John Dvorak

Naprawdę Myślałem, że bezcelowe, nieuzasadnione i specyficzne dla architektury użycie SIMD kwalifikuje go do trollowania kodu?
Paul R

hmm ... prawda. Nie zdawałem sobie sprawy, że było to specyficzne dla architektury.
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;
}
  • działa tylko dla wyniku pomnożenia <1 000 000
  • Do tej pory nie można się go pozbyć - operatora, być może ulepszenia tutaj
  • użycie specyfikatora formatu% n w printf do zliczenia liczby wydrukowanych znaków (wysyłam to głównie w celu przypomnienia o istnieniu% n w C, zamiast% n można oczywiście strlen itp.)
  • Drukuje znaki * * z „*”

Teraz czeka na rozwiązanie „emulacji maszyny Turinga”.
greggo

1
while strlen(cg) != ajest bardzo trollingową metodą eliminacji --(czyni to O (N * N)).
MSalters

3

Prawdopodobnie za szybko :-(

   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
Nie. To nie jest trolling. Jest to całkowicie rozsądny sposób na zrobienie tego.
John Dvorak

1
Jest trolly, ponieważ jest za szybki :-)
Timtech

3

Ta wersja Haskell działa tylko z nieujemnymi liczbami całkowitymi, ale dokonuje mnożenia w sposób, w jaki dzieci uczą się tego po raz pierwszy. Tj. 3x4 to 3 grupy po 4 rzeczy. W tym przypadku liczone „rzeczy” to wycięcia („|”) na patyku.

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

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

Może to działać w C99, jeśli pogoda jest odpowiednia, a Twój kompilator obsługuje niezdefiniowane bzdury.


3

Ponieważ OP nie poprosił o C , oto jeden w (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
Mój Boże, jest pełen *s!
Paul R

1
@PaulR :), ale nie są operatorami .
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;
}

Może zawierać śladowe ilości 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;
}

Testowe uruchomienie:

$ ./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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.