Як написати програму C для множення без використання операторів * та +?


68

Чи можливо написати програму C, яка множить два числа, не використовуючи операторів множення та додавання?

Я знайшов це на стеці Overflow . Будь ласка, допоможіть цьому бідному програмісту з його проблемою. І будь ласка, не дайте відповідей на кшталт c = a/(1/((float)b)), що точно так само, як c = a*b. (І вже дається як відповідь.)

Відповідь з найбільшою кількістю результатів 19 січня 2014 року виграє.

Примітка. Це запитання щодо . Будь ласка, не сприймайте питання та / або відповіді серйозно. Більше інформації можна знайти в коді-тролінгу .


2
@PaulR використовуйте свою фантазію
Джон Дворак

26
Code-golf.SE не повинен бути місцем для знущання над питаннями, які ви бачили на StackOverflow.
Гарет

17
@Gareth, ти впевнений? Перша лінія це передбачає , що це може бути цілком доречно.
Даррен Стоун

5
Я чекаю, коли хтось напише алгоритм на основі сну
kb_sou

21
Це питання не таке смішне, як звучить. Власне комп'ютерне обладнання (транзистори) не мають операцій множення та додавання - вони мають основні логічні операції, такі як NOT, AND, OR, XOR. З'ясування того, як відповісти на це питання, може дати вам прекрасне уявлення про те, як комп’ютер дійсно працює на рівні логічних воріт.
Гейб

Відповіді:


147

Завжди використовуйте рекурсію

Репресія - це правильний шлях!

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
Я хотів би дати +3 , якби я міг: один для кінцевої рекурсії, один для ??::без дужок, один для вирішення цієї проблеми , не намагаючись підправити правила;)
йо »

10
Якщо Пікассо був програмістом ...
Р. Х'юз

4
@SargeBorsch Але звідки у забави бути в тому , що ?
Оберон

3
@HC_ incФункція тестує свій аргумент, щоб перевірити, чи є найнижчий біт 1; якщо так, він викликає себе на решті верхніх бітів аргументу і повертає результат тим самим низьким бітом, на який було встановлено встановлення 0, а якщо ні (тобто найнижчий біт є 0), він замінює це 0на a 1і повертає результат . Процес дуже схожий на те, що ви робили, якби додавали значення вручну, двійкові цифри за двійковою цифрою.
JAB

2
Чи не збільшується функція приросту в нескінченний цикл для -1? (0xFFFF) ідеон показує (-1 >> 1) == -1.
Обіцяч

87

Вам доведеться компілювати програму кожен раз, але вона дійсно множує будь-які додатні цілі числа саме в будь-якій версії C або C ++.

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

 typedef char buffer[A][B];

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

4
Помістіть його всередині структури, і вам не потрібна пам'ять.
Бен Джексон

4
Ха-ха-ха здорово !!
Альмо

1
використовувати "%zu"форматний рядок.
Гріеш Чаухан

5
просто sizeof(char[A][B])буде працювати (якщо тільки A <= 0 або B <= 0 або A * B переповнюється; в такому випадку ви повинні отримати помилку "поганого типу")
greggo

3
@DavidRicherby - Я міг би спростити код main(){return sizeof(char[A][B]);}і ви компілюєте, використовуючиcc -DA=6 -DB=7 a.c; ./a.out; echo $?
Марк Лаката

47

Якщо ви добре з невеликою точністю, можете скористатися методом Монте-Карло :

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

Приклад:

$ ./mul 300 250
300 * 250 = 74954

Я думаю, це може бути досить добре;)


3
Ви маєте моє голосування. Я чув, що Монте-Карло - це те, що NASA використовує для своєї арифметики. Але я хотів би бачити це без двох примірників ++оператора.
Даррен Стоун

1
@DarrenStone-= -1
Timtech

20
@Timtech |= 1(працюватиме над 50% номерів, 100% часу)
Даррен Стоун,

2
+1, але зауваживши, що це може бути занадто повільним, і ви можете додати підтримку з декількома потоками, обережно заблокувавши "count ++" :-)
greggo

1
Завжди printfзбільшується: printf("%*cc%n\n", count, &count, 'c');(Друкує "c" рахують рази, потім ще "c", і зберігає кількість символів, записаних ще раз count.
MSalters

45

Оскільки ви не вказали, який розмір числа, я вважаю, що ви маєте на увазі два однорозрядних числа.

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

Якщо ви хочете отримати максимально ефективну реалізацію, використовуйте наступну крихітну реалізацію:

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

Зауважте, що він все ще приймає лише біти, хоча типи є неявними входами - він займає менше коду, а тому є більш ефективним. (І так, це все компілюється.)


8
Приємно. Навмисне неправильне тлумачення питання :-)
Іван Дворак

6
Ви можете оптимізувати це до return a && b;. Це коротше, тим швидше.
Ри-

1
@minitech: Я вирішив проти цього, щоб зробити код трохи гіршим. Якби я хотів іти далі з цим, я би встиг return a&b;.
Сел Скеггс

1
C має #include<stdbool.h>визначати trueі false.
leewz

1
Так, #include<stdbool.h>здається, тільки три #defineз , що ви можете зробити самі ( true, false, boolі прапор , щоб відзначити , що він був активований). Ви також можете взяти підказку з однієї з інших відповідей і використовувати неявно intдля "короткої" версії.
leewz

31

Ось простий скрипт оболонки для цього:

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

ОНОВЛЕННЯ: Звичайно, щоб зробити це на C, просто загорніть його exec("bash", "-c", ...). (Спасибі, AmeliaBR)


41
Я не можу вирішити, що страшніше. Що ви аутсорсингу вашого розрахунку в пошуковій системі, або що ваша обрана пошукова система Bing На жаль, я не думаю, що це допоможе нашому нещасному ОП, якому щось потрібне було у C.
AmeliaBR

5
Дякуємо, що переконались у нагляді. FYI, я використовую Bing, оскільки Google ускладнює видачу таких запитів - вам потрібно додати заголовки, щоб переконати Google, що ваш запит дійсно надходить із браузера.
Vroo

2
@abarnert хм ... чи розуміє Бінг "часи"? Вольфрам Альфа, однак, може.
Джон Дворак

2
@JanDvorak: Так, Вольфрам працює. (Зверніть увагу на те, %20щоб уникнути використання будь-яких +знаків.) Але вам все ж потрібно проаналізувати вихід (у С), щоб отримати значення з нього. Що буде особливо хитро, оскільки на виході видається зображення, а не текст. Розбір HTML плюс OCR може стати найкращою можливою відповіддю на цю проблему.
abarnert

3
@JanDvorak: Це не весело. Я з нетерпінням чекав, коли хтось написав просту бібліотеку OCR без додавання чи множення…
abarnert

27

Чому, давайте зробимо рекурсивний пошук вдвічі між INT64_MIN та 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 Це буде радісно sigsegv з деякими значеннями. ;)


18

На жаль, це працює лише для цілих чисел.

Оскільки додавання заборонено, спочатку побудуємо оператор приросту:

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

Далі ми маємо поводитися зі знаком. Спочатку знайдіть біт знака:

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

Потім візьміть знак і величину кожного аргументу. Щоб відмовити число в додатку два, переверніть всі біти та додайте один.

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

Для множення двох натуральних чисел ми можемо використовувати геометричне значення множення:

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

тролі:

  • Додавання заборонено, але чи a++дійсно вважається доповненням? Сумніваюся, що вчитель мав намір це допустити.
  • Покладається на доповнення двох, але це поведінка, визначена реалізацією, і цільова платформа не була вказана.
  • Так само припускається, що віднімання і ділення також заборонені.
  • << насправді множення на потужність дві, тому технічно його слід заборонити.
  • нецікаві діаграми є нецікавими. Крім того, його можна було перенести, щоб зберегти один рядок.
  • повторне зміщення -1не є найприємнішим способом знаходження знаку біта. Навіть якщо вбудованої константи не було, ви можете зробити логічний зсув справа на -1, а потім перевернути всі біти.
  • XOR -1 - не найкращий спосіб інвертувати всі біти.
  • Вся шарада зі знаковою величиною подання є не обов'язковою. Тільки, що передається неподписаним та модульним арифметичним, зробить все інше.
  • оскільки абсолютне значення MIN_INT(AKA signBit) від’ємне, це значення розбивається на це значення. На щастя, вона все ще працює в половині випадків, тому що MIN_INT * [even number] повинна бути нульовою.Крім того, plusOneперерви для -1, спричиняючи нескінченні петлі в будь-який час, результат переповнює. plusOneпрацює чудово за будь-яку цінність. Вибачте за непорозуміння.

+1 для фактичного кодового троля: Схоже, це має спрацювати, але це, швидше за все, підірветься на ОП і він / він не матиме поняття, чому.
Кевін

1
Додавання можна зробити без будь-яких операторів додавання, просто використовуючи shift, XOR та AND. Усі ці ++ роблять мені голову боляче - один біт ADD with carry is (x ^ y) | ((x & y) << 1) (модульовані будь-які помилки, спричинені введенням цього маленького текстового поля.)
Джулі в Остіні,

@JulieinAustin так. Алгоритм навіть більш неефективний, ніж це має бути. Чи варто внести зміни до списку тролів? :-)
Джон Дворак

1
@JulieinAustin (x ^ y) | ((x & y) << 1)не дуже працює, він не розповсюджуватиме перенесення, коли x або y і носять обоє вірні в одній позиції :)
hobbs

@hobbs рішення: замість ORing додайте їх рекурсивно, якщо переносне значення не дорівнює нулю.
Іван Дворак

14

Працює і для чисел з плаваючою комою:

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

11

Всім відомо, що Python простіший у використанні, ніж C. І Python має функції, відповідні кожному оператору, у випадках, коли ви не можете використовувати оператора. Яке саме визначення нашої проблеми, правда? Тому:

#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

Жодна з інших відповідей теоретично обгрунтована. Як вказує найперший коментар до цього питання:

Будьте точнішими щодо "цифр"

Нам потрібно визначити множення та числа, перш ніж відповідь навіть стане можливою. Як тільки ми це зробимо, проблема стає тривіальною.

Найпопулярніший спосіб зробити це на початку математичної логіки - це побудувати ординари фон Неймана на основі теорії множин ZF , а потім використовувати аксіоми Пеано .

Це природно перекладається на C, якщо припустити, що у вас є тип набору, який може містити інші набори. Він не повинен містити нічого, крім наборів, що робить його тривіальним (жодна з тих, що кидає void*дурниці в більшості встановлених бібліотек), тому я залишу реалізацію як вправу для читача.

Отже, спочатку:

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

Якщо ви хочете розширити це на цілі, раціональні, реальні, сюрреалістичні дані тощо, ви можете - з нескінченною точністю (якщо припустити, що у вас є нескінченна пам'ять і процесор), завантажитися. Але, як чудово сказав Кронекер, Бог створив натуральні числа; все інше - робота людини, так що насправді, навіщо турбуватися?


1
Ого. Ти навіть повільніше, ніж я.
Іван Дворак

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

Якщо ви вважаєте, що хак [b] є обманом (оскільки це дійсно додаток), то це працює замість цього. Але пошук таблиць, що включають покажчики, також додає.

Дивіться http://en.wikipedia.org/wiki/IBM_1620 - комп'ютер, який фактично робив доповнення за допомогою таблиць пошуку ...

Щось задовольняє використання механізму таблиці для «прискорення» операції, що насправді може бути виконано за допомогою однієї інструкції.

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

На жаль, є *char (хоча це не множення)
Sarge Borsch,

Так, для пошуку таблиці використовується додавання - (a [i]) те саме, що (* (a + i)).
Джулі в Остіні

@JulieinAustin Я це згадав. Пошук таблиць можна здійснити без додавання шляхом злиття полів (як показано в IBM 1620, див. Посилання), але це безладно встановити це в C - для однієї речі вам потрібно вирівняти таблицю за належною адресою, щоб показники могли бути просто або
ввійшов

8

Я не впевнений , що представляє собою «шахрайство» в цих «код тролів» пости, але це примножує 2 довільні цілі числа, під час виконання, без *або +оператора з використанням стандартних бібліотек (C99).

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

8

Моє рішення тролів для 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 Приємна реалізація оцінювання. Мені подобається ваш код :)
йо »

@ Бйович: Я винен, я вважав, що тролінг - це не розуміння. Змінили імена та додали коментарі.
Маттіас

вибачте. Я неправильно зрозумів, що таке тег і про що насправді йде Q. Ви повинні повернути його
BЈович

@Matthias в цьому випадку корисно зрозуміти, як це працює, щоб ми могли оцінити, наскільки вита операція конвергенції-перенесення. У реальній ситуації з кодовим тролем коментарі можуть бути відредаговані :-)
greggo

Я хотів би зазначити, що якщо ви хочете додати біт-обернені номери (з високим рівнем переносу опори) і у вас немає інструкції "bitrev", це, ймовірно, цілком розумний підхід (після зміни на c> > = 1 звичайно)
greggo

7

Тут є багато хороших відповідей, але не схоже, що багато хто з них скористається тим, що сучасні комп’ютери справді потужні. У більшості процесорів є кілька одиниць обробки, тож навіщо використовувати лише один? Ми можемо використовувати це для отримання чудових результатів.

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

Ось приклад його використання:

$ ./multiply
5 6
5 x 6 = 30

#pragma omp parallelДиректива робить OpenMP розділити кожну частину для циклу до іншого блоку виконання, тому ми множачи паралельно!

Зауважте, що вам потрібно використовувати -fopenmpпрапор, щоб сказати компілятору використовувати OpenMP.


Тролі частини:

  1. Оману щодо використання паралельного програмування.
  2. Не працює на негативних (або великих) номерах.
  3. Дійсно не розділяє частини forциклу - кожна нитка запускає цикл.
  4. Дратівливі назви змінних та повторне використання.
  5. Там витончена умова гонки answer--; більшу частину часу вона не відображатиметься, але іноді це призведе до неточних результатів.

2
Чому б не поєднати це з SIMD-відповіддю Пола Р, тож ви можете бігти 32 рази так само швидко, а не 8 разів? Хоча насправді, ви хочете залучити GPU так само, як і сердечники; тоді воно б справді палало. :)
abarnert

2
Можна також використовувати OpenMPI для запуску на декількох машинах паралельно.
мільйон

6

На жаль, множення є дуже важкою проблемою в інформатиці. Найкращим рішенням є використання розділення натомість:

#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

У реальному житті я зазвичай реагую на тролінг знанням, тож ось відповідь, яка зовсім не тролить. intНаскільки я бачу, він працює для всіх значень.

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

Наскільки я розумію, це дуже схоже на те, як процесор насправді може робити ціле множення. По-перше, ми переконуємося, що принаймні один з аргументів ( a) є позитивним, перевернувши знак на обидва, якщо aнегативний (і ні, я відмовляюся вважати заперечення як різновид додавання чи множення). Потім while (a)цикл додає зміщену копію bдо результату для кожного встановленого біта a. doПетля реалізує з r += xвикористанням і, XOR, і зміщення в тому, що це явно набір половин сумматоров, з керрі битами подається назад в xдо тих пір, поки не більше (реальний процесор буде використовувати повні суматори, який є більш ефективним, але C Байдуже » не мати операторів, які нам потрібні для цього, якщо ви не рахуєте +оператора).


4
Аскер не тролював. Ви повинні троліти.
Джон Дворак

2
Це стелс-троль! Секретний збій знаходиться на == INT_MIN.
Яндер

1
@Jander хм. Так, це добре. Я здогадуюсь (у звичайних системах доповнення двох) результат відмови від а досі негативний, і while(a)цикл ніколи не припиняється.
варення

@hobbs Так, це звучить правильно для мене. Інакше дуже гарна відповідь.
Джандер

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
Це жахливо вийде з ладу, якщо результат переповниться. Приємно! Я думаю, ви не повинні друкувати.
Іван Дворак

2
не вдається для a = 2, b = 2, c = 5
BЈовић

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

2
Зауважте, що це справді спроба зробити те ж саме, що і наступник Deep Thought, але для всіх, можливо, всесвітів , а не лише той, де відповідь є 42. Що було б дуже вражає, якби не помилка. І відсутність обробки помилок у випадку Vogons.
abarnert

1
Потребує декількох ниток. Ви знаєте, щоб зробити це ефективним.
greggo

6

Кидаючи це в суміш:

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

З інформаційної сторінки .

- Внесення до коду чогось вкрай неприйнятного або необгрунтованого, яке не можна вилучити, не викинувши все, зробивши відповідь абсолютно корисним для ОП.

- […] Намір полягає в тому, щоб зробити домашнє завдання мовою, яку лінивий ОП може вважати прийнятною, але все-таки засмучує його.


2
"без використання операторів множення та додавання". Приємне згинання правил - це запитувачеві буде абсолютно марно :-)
Джон Дворак

2
Це насправді не рішення C. Крім того, він не може компілювати на моєму ARM9.
abarnert

1
@abarnert: невпізнання вашої заяви як релевантного аргументу.
Руніум

@Sukminder: Питання "Чи можна написати програму C ...?" Вбудована збірка не є C. Факт, що деякі компілятори C також можуть робити вбудовану збірку, це не змінює, більше ніж те, що деякі компілятори C також можуть робити C ++ або ObjC робить C ++ або ObjC вважати C кодом.
abarnert

2
@abarnert: Це вбудований код, який широко використовується в програмах на С. Незважаючи на те, що це помісь можна стверджувати , що це програма C . Навіть правдоподібна ОП визнала б це кодом С. Це явно не пітон, чи?
Руніум

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

Чи можливо написати програму C, яка множить два числа, не використовуючи операторів множення та додавання?

Звичайно:

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

Але звичайно це обман; очевидно, він хоче мати можливість подати два числа, правда?

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

Чому, це мені зовсім не було очевидно!
leewz

4

Не існує арифметики на зразок арифметики вказівника:

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

Функція fреалізує множення. mainпросто називає це двома аргументами.
Працює і для від’ємних чисел.


Негативний a, так, негативний. bЯ не думаю, що так. Але це можна виправити у багатьох творчих способах. Найпростішим буде sign_a ^ = sign_b, sign_b = 0.
MSalters

@MSalters, протестований та працює для всіх комбінацій знаків (з Linux / gcc).
ugoren

3

C #

Я думаю, віднімання та заперечення заборонені ... У будь-якому випадку:

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

1
Це саме рішення, про яке я думав ... але пізно приїжджаючи на вечірку, я знав, що це питання прокрутки вниз, поки я не виявив, що хтось це вже написав. Тим не менш, ти отримуєш - (- 1) від мене.
Флоріс

3

C із вбудованими SSE (тому що все краще з 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;
}

Великою перевагою цієї реалізації є те, що вона може бути легко адаптована для виконання 4 паралельних множень без *або +за необхідності.


Я не думаю, що це тролінг ...
Джон Дворак

Дійсно? Я думав, що безглузде, безоплатно та архітектурне використання SIMD спричинить його за кодовий тролінг?
Пол Р.

хм ... правда. Не усвідомлював, що це стосується архітектури.
Іван Дворак

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;
}
  • робота лише для результату множення <1 000 000
  • Поки що не можна позбутися від оператора, можливо, посилюючись тут
  • використовуючи специфікатор формату% n у printf для підрахунку кількості друкованих символів (я розміщую це головним чином, щоб нагадати про існування% n в C, замість% n, звичайно, може бути strlen тощо)
  • Друкує a * b символів '*'

Тепер чекаємо на рішення "емуляції машини машин".
greggo

1
Хоча strlen(cg) != aце дуже тролінг метод усунення --(робить його O (N * N)).
MSalters

3

Можливо, занадто швидко :-(

   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
Невірно. Це не тролінг. Це цілком розумний спосіб зробити це.
Джон Дворак

1
Це trolly, тому що це занадто швидко :-)
Timtech

3

Ця версія Haskell працює лише з невід’ємними цілими числами, але вона множує так, як діти вперше її вивчають. Тобто 3х4 - це 3 групи з 4 речей. У цьому випадку "речі", що рахуються, є насічками ("|") на паличці.

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

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

Це може спрацювати в C99, якщо правильна погода, і ваш компілятор підтримує невизначені дурниці.


3

Оскільки ОП не просив C , ось один у (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
Боже мій, це повне *s!
Пол Р

1
@PaulR :), але вони не оператори .
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;
}

Може містити сліди УД.


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

Пробіг:

$ ./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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.