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


68

Можно ли написать программу на C, которая умножает два числа без использования операторов умножения и сложения?

Я нашел это в переполнении стека . Пожалуйста, помогите этому бедному программисту с его проблемой. И, пожалуйста, не давайте ответы, как c = a/(1/((float)b)), что точно так же, как c = a*b. (И уже дан как ответ.)

Ответ с наибольшим количеством голосов 19 января 2014 года выигрывает.

Примечание: это вопрос . Пожалуйста, не воспринимайте вопрос и / или ответы всерьез. Больше информации в коде троллинга .


2
@PaulR используй свою фантазию
Джон Дворак

26
Code-golf.SE не должен быть местом, где вы можете высмеивать вопросы, которые вы видели в StackOverflow.
Гарет

17
@ Гарет, ты уверен? Первая линия это предполагает , что это может быть вполне уместно.
Даррен Стоун

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, если бы мог: один для окончательной рекурсии, один для ??::без скобок, один для решения проблемы без попыток подправить правила;)
yo '11

10
Если бы Пикассо был программистом ...
Р. Хьюз

4
@SargeBorsch Но откуда у забавы быть в том , что ?
Оберон

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

2
Разве функция приращения не входит в бесконечный цикл для -1? (0xFFFF) ideone показывает (-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
Хахахах здорово !!
Almo

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
У тебя есть мой голос. Я слышал, что Монте-Карло - это то, что НАСА использует для своей арифметики. Но я бы хотел увидеть это без двух экземпляров ++оператора.
Даррен Стоун

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;. Это короче, так быстрее.
Ry-

1
@minitech: Я решил против этого, чтобы сделать код немного хуже. Если бы я хотел пойти дальше с этим, я бы сделал это return a&b;.
Чел Скеггс

1
С должен #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", ...). (Спасибо, АмелияБР)


41
Я не могу решить, что является более ужасным. Что вы передаете свои расчеты поисковой системе, или что выбранная вами поисковая система - Bing. К сожалению, я не думаю, что это сработает для нашего счастливого ОП, которому что-то нужно в C.
AmeliaBR

5
Спасибо, что поймали этот недосмотр. К вашему сведению, я использую Bing, потому что Google усложняет выдачу таких запросов - вы должны добавить заголовки, чтобы убедить Google, что ваш запрос действительно поступает из браузера.
Vroo

2
@abarnert хм ... Бинг понимает "времена"? Вольфрам Альфа мог бы, хотя.
Джон Дворак

2
@JanDvorak: Да, Вольфрам работает. (Обратите внимание, %20чтобы избежать использования каких-либо +знаков.) Но вам все равно нужно проанализировать вывод (в C), чтобы получить значение из него. Что будет особенно сложно, поскольку на выходе получается изображение, а не текст. Синтаксический анализ HTML и распознавание текста могут сделать это наилучшим возможным решением этой проблемы.
abarnert

3
@JanDvorak: это не весело. Я с нетерпением ждал, когда кто-то
напишет

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(АКА signBit) является отрицательным, это разрыв для этого значения. К счастью, это все еще работает в половине случаев, потому что MIN_INT * [even number] должно быть ноль.Кроме того, plusOneразрывы для -1, вызывая бесконечные циклы каждый раз, когда результат переполняется. plusOneработает просто отлично для любой стоимости. Извините за путаницу.

+1 для реального тролля кода: похоже, он должен работать, но он, скорее всего, взорвется на OP, и он / она не будет знать, почему.
Кевин

1
Можно сделать сложение без ЛЮБЫХ операторов сложения, просто используя shift, XOR и AND. Все эти ++ делают мою голову болит - один бит ADD с переносом является (x ^ y) | ((x & y) << 1) (по модулю любые ошибки, вызванные вводом в это дерьмовое маленькое текстовое поле.)
Джули в Остине

@JulieinAustin да. Алгоритм еще более неэффективен, чем должен быть. Должен ли я изменить список троллей? :-)
Джон Дворак

1
@JulieinAustin (x ^ y) | ((x & y) << 1)не совсем работает, он не будет распространять информацию о переносе, когда x и y и
переносы

Решение @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;
}

Упс, есть *символ (хотя это не умножение)
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 Хорошая реализация оценки выполнения. Мне нравится ваш код :)
йо»

@ BЈовић: Моя вина, я думал, что троллинг не для понимания. Изменены имена и добавлены комментарии.
Матиас

извиняюсь. Я неправильно понял, что это за тег, и что такое 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-ответом Пола Р., чтобы вы могли бежать быстрее 32x вместо 8x? Хотя на самом деле вы хотите подключить как 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.
Jander

1
@ Джандер хм. Да, это хорошо. Я предполагаю (на обычных системах дополнения двух) результат отрицания a все еще отрицателен, и while(a)цикл никогда не заканчивается.
Хоббс

@hobbs Да, это звучит правильно для меня. В противном случае очень красивый ответ.
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
Это ужасно потерпит неудачу, если результат переполнится. Приятно! Я думаю, вы не должны печатать, хотя.
Джон Дворак

2
терпит неудачу для a = 2, b = 2, c = 5
BЈовић

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

2
Обратите внимание, что это действительно попытка сделать то же самое, что и преемник Deep Thought, но для всех возможных вселенных , а не только тот, где ответ 42. Что было бы очень впечатляющим, если бы не ошибка. И отсутствие обработки ошибок в случае Vogons.
abarnert

1
Требуется несколько потоков. Вы знаете, чтобы сделать это эффективным.
Грегго

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
msgstr "без использования операторов умножения и сложения". Приятное изменение правил - это будет абсолютно бесполезно для спрашивающего :-)
Джон Дворжак

2
Это не совсем решение C. Кроме того, он не компилируется на моем ARM9.
abarnert

1
@abarnert: не в состоянии признать ваше утверждение в качестве соответствующего аргумента.
Runium

@Sukminder: Вопрос в том, "Можно ли написать программу на C ...?" Встроенная сборка - это не C. Тот факт, что некоторые компиляторы C могут также выполнять встроенную сборку, не меняет этого, равно как и тот факт, что некоторые компиляторы C также могут выполнять C ++ или ObjC заставляет C ++ или ObjC считаться кодом C.
abarnert

2
@abarnert: Это встроенный код, широко используемый в программах на Си. Хотя это междоусобица, можно утверждать, что это программа на Си . Это даже правдоподобно, что OP распознал бы его как C-код. Это явно не питон, или?
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

Можно ли написать программу на 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).
Угорен

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 символы '*'

Теперь жду решения «эмуляция машины Тьюринга».
Грегго

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
Это троллейбус, потому что он слишком быстрый :-)
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

Поскольку OP не запрашивал 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.