Como escrever um programa C para multiplicação sem usar os operadores * e +?


68

É possível escrever um programa C que multiplique dois números sem usar os operadores de multiplicação e adição?

Encontrei isso no Stack Overflow . Por favor, ajude este pobre programador com seu problema. E por favor, não dê respostas como c = a/(1/((float)b)), que é exatamente o mesmo que c = a*b. (E já é dado como resposta.)

A resposta com mais votos em 19 de janeiro de 2014 vence.

Nota: Esta é uma pergunta de . Por favor, não leve a sério a pergunta e / ou respostas. Mais informações estão em trolling de código .


2
@PaulR usar sua fantasia
John Dvorak

26
O Code-golf.SE não deve ser um lugar para você zombar das perguntas que você viu no StackOverflow.
Gareth

17
@Gareth, você tem certeza? A primeira linha disso sugere que isso pode ser bastante apropriado.
Darren Pedra

5
Estou à espera de alguém escrever um algoritmo baseado no sono
kb_sou

21
Esta pergunta não é tão ridícula quanto parece. O hardware real do computador (transistores) não possui operações de multiplicação e adição - elas possuem operações lógicas básicas como NOT, AND, OR, XOR. Descobrir como responder a essa pergunta pode fornecer uma excelente visão de como um computador realmente funciona no nível das portas lógicas.
Gabe

Respostas:


147

Sempre use recursão

Recusão é o caminho certo!

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
Eu daria +3 se pudesse: um para a recursão final, um para ??::sem parênteses, um para resolver o problema sem tentar ajustar as regras;)
yo '

10
Se Picasso era um programador ...
R Hughes

4
@SargeBorsch Mas onde está a diversão nisso ?
Oberon

3
@HC_ A incfunção testa seu argumento para ver se o bit mais baixo é 1; nesse caso, ele se chama nos bits superiores restantes do argumento e retorna o resultado com o mesmo bit baixo que foi verificado 0, enquanto que, se não (ou seja, o bit mais baixo 0), substitui-o 0por a 1e retorna o resultado . O processo é muito semelhante ao que você faria se estivesse adicionando os valores manualmente, dígito binário por dígito binário.
JAB

2
A função de incremento não entra em um loop infinito para -1? A ideona (0xFFFF) mostra (-1 >> 1) == -1.
Destrictor

87

Você precisará compilar o programa toda vez, mas ele multiplicará qualquer número inteiro positivo exatamente em qualquer versão do C ou C ++.

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

 typedef char buffer[A][B];

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

4
Coloque-o dentro de uma estrutura e você não precisa de memória.
Ben Jackson

4
Hahahah ótimo !!
Almo 12/01

11
use "%zu"formato string.
Grijesh Chauhan

5
apenas sizeof(char[A][B])irá funcionar (a menos que A <= 0 ou B <= 0 ou A * B overflows, caso em que você deve obter uma espécie 'tipo mau' de erro)
Greggo

3
@DavidRicherby - Eu poderia simplificar o código para main(){return sizeof(char[A][B]);}e você compilar usandocc -DA=6 -DB=7 a.c; ./a.out; echo $?
Mark Lakata

47

Se você estiver bem com alguma imprecisão, poderá usar o método 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));
}

Exemplo:

$ ./mul 300 250
300 * 250 = 74954

Eu acho que isso pode ser bom o suficiente;)


3
Você tem meu voto. Ouvi dizer que Monte Carlo é o que a NASA usa para sua aritmética. Mas eu gostaria de ver isso sem as duas instâncias do ++operador.
Darren Stone

11
@DarrenStone-= -1
Timtech

20
@Timtech |= 1(irá funcionar em 50% dos números, 100% do tempo)
Darren Pedra

2
+1, mas notando que poderia ser muito lento, e você pode adicionar suporte multi-thread, fechando cuidadosamente a 'contagem ++' :-)
Greggo

11
Sempre há printfincremento: printf("%*cc%n\n", count, &count, 'c');(Imprime 'c' contar vezes, depois outro 'c' e armazena o número de caracteres escritos novamente count.
MSalters

45

Como você não especificou qual tamanho de número, presumo que você queira dizer dois números de um bit.

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

Se você deseja uma implementação maximamente eficiente, use a pequena implementação a seguir:

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

Observe que ele ainda aceita apenas bits, mesmo que os tipos sejam ints implícitos - requer menos código e, portanto, é mais eficiente. (E sim, ele compila.)


8
Agradável. A má interpretação deliberada da :-) questão
John Dvorak

6
Você pode otimizar isso para return a && b;. É mais curto, então é mais rápido.
precisa saber é

11
@ minitech: Eu decidi contra isso, a fim de tornar o código um pouco pior. Se eu quisesse ir mais longe, eu conseguiria return a&b;.
Cel Skeggs

11
C tem #include<stdbool.h>que definir truee false.
leewz

11
Sim, #include<stdbool.h>parece ser apenas três #defines que você pode fazer a si mesmo ( true, false, bool, e uma bandeira para marcar que ele tenha sido ativado). Você também pode usar um truque de uma das outras respostas e usar o implícito intpara a versão "curta".
leewz

31

Aqui está um script de shell simples para fazer isso:

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

UPDATE: É claro que, para fazê-lo em C, basta envolvê-lo exec("bash", "-c", ...). (Obrigado, AmeliaBR)


41
Não consigo decidir o que é mais horrível. Você está terceirizando seu cálculo para um mecanismo de pesquisa ou o mecanismo de pesquisa escolhido é o Bing. Infelizmente, eu não acho que isso vai funcionar para o nosso OP happless, que precisava de algo em C.
AmeliaBR

5
Obrigado por capturar essa supervisão. Para sua informação, estou usando o Bing porque o Google torna mais complicado emitir solicitações como esta - você precisa adicionar cabeçalhos para convencer o Google de que sua solicitação realmente é proveniente de um navegador.
Vroo 12/01

2
@abarnert hmm ... o Bing entende "tempos"? Wolfram Alpha pode, no entanto.
John Dvorak

2
@JanDvorak: Sim, o Wolfram funciona. (Observe %20para evitar o uso de quaisquer +sinais.) Mas você ainda precisa analisar a saída (em C) para obter o valor dela. O que será especialmente complicado, pois a saída parece ser uma imagem, não texto. A análise de HTML e o OCR podem ser a melhor resposta possível para esse problema.
abarnert

3
@JanDvorak: Isso não é divertido. Eu estava ansioso para alguém escrever uma biblioteca de OCR simples, sem adição ou multiplicação ...
abarnert

27

Por que, vamos fazer uma pesquisa pela metade recursiva entre INT64_MIN e INT64_MAX!

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

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

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

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

PS Será feliz sigsegv com alguns valores. ;)


18

Infelizmente, isso funciona apenas para números inteiros.

Como a adição é proibida, vamos criar um operador de incremento primeiro:

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

Em seguida, temos que lidar com o sinal. Primeiro, encontre o bit do sinal:

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

Então pegue o sinal e a magnitude de cada argumento. Para negar um número no complemento de dois, inverta todos os bits e adicione um.

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

Para multiplicar dois números inteiros positivos, podemos usar o significado geométrico da multiplicação:

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

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

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

trolls:

  • A adição não é permitida, mas a++realmente conta como adição? Aposto que o professor pretendia permitir.
  • Confia no complemento de dois, mas esse é um comportamento definido pela implementação e a plataforma de destino não foi especificada.
  • Da mesma forma, assume que subtração e divisão também não são permitidas.
  • << é, na verdade, multiplicação por uma potência de dois, portanto, tecnicamente, isso não deve ser permitido.
  • diagrama desnecessário é desnecessário. Além disso, poderia ter sido transposto para salvar uma linha.
  • A mudança repetida de -1não é a melhor maneira de encontrar o bit de sinal. Mesmo se não houvesse constante interna, você poderia fazer um deslocamento lógico para a direita de -1 e inverter todos os bits.
  • XOR -1 não é a melhor maneira de inverter todos os bits.
  • Toda a charada com representação de magnitude de sinal é desnecessária. Apenas converter para aritmética modular e não assinada fará o resto.
  • como o valor absoluto de MIN_INT(AKA signBit) é negativo, isso quebra para esse valor. Felizmente, ele ainda funciona na metade dos casos, porque MIN_INT * [even number] deve ser zero.Além disso, plusOneinterrompe -1, causando loops infinitos sempre que o resultado exceder o limite. plusOnefunciona muito bem para qualquer valor. Desculpe pela confusão.

+1 para um troll de código real: parece que deve funcionar, mas provavelmente explodirá no OP e ele / ela não terá idéia do porquê.
Kevin

11
É possível fazer acréscimos sem QUALQUER operador de acréscimo usando apenas shift, XOR e AND. Todos esses ++ estão fazendo minha cabeça doer - um bit ADD com carry é (x ^ y) | ((x & y) << 1) (modulo quaisquer erros causados ​​pela digitação nesta caixinha de texto ruim).
Julie in Austin

@JulieinAustin yep. O algoritmo é ainda mais ineficiente do que precisa ser. Devo alterar a lista de trolls? :-)
John Dvorak

11
@JulieinAustin (x ^ y) | ((x & y) << 1)não funciona muito bem, não vai propagar carry quando x ou y e realizar são verdadeiras na mesma posição :)
hobbs

@ hobbs solution: em vez de ORing, adicione-os recursivamente se carry for diferente de zero.
John Dvorak

14

Também funciona para números de ponto flutuante:

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

11

Todo mundo sabe que o Python é mais fácil de usar que o C. E o Python tem funções correspondentes a todos os operadores, para casos em que você não pode usá-lo. Qual é exatamente a nossa definição de problema, certo? Assim:

#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

Nenhuma das outras respostas é teoricamente correta. Como o primeiro comentário sobre a questão diz:

Por favor, seja mais específico sobre "números"

Precisamos definir multiplicação e números antes que uma resposta seja possível. Quando o fazemos, o problema se torna trivial.

A maneira mais popular de fazer isso no início da lógica matemática é construir ordinais de von Neumann sobre a teoria dos conjuntos ZF e, em seguida, usar os axiomas Peano .

Isso se traduz naturalmente em C, assumindo que você tenha um tipo de conjunto que pode conter outros conjuntos. Ele não precisa conter nada além de conjuntos, o que o torna trivial (nada disso faz void*disparates na maioria das bibliotecas de conjuntos); portanto, deixarei a implementação como um exercício para o leitor.

Então, primeiro:

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

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

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

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

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

Se você quiser expandir isso para números inteiros, racionais, reais, surreals, etc., poderá - com precisão infinita (supondo que você tenha memória e CPU infinitas), para inicializar. Mas, como disse Kroenecker, Deus fez os números naturais; todo o resto é obra do homem, então realmente, por que se preocupar?


11
Uau. Você é ainda mais lento que eu.
John Dvorak

10
unsigned add( unsigned a, unsigned b )
{
    return (unsigned)&((char*)a)[b];  // ignore compiler warnings
       // (if pointers are bigger than unsigned). it still works.
}
unsigned umul( unsigned a, unsigned b )
{
    unsigned res = 0;
    while( a != 0 ){
        if( a & 1) res = add( res, b );
        b <<= 1;
        a >>= 1;
    }
    return res;
}

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

Se você considera o truque a [b] uma trapaça (já que é realmente um complemento), isso funciona. Mas as pesquisas de tabela também envolvem acréscimos de ponteiro.

Veja http://en.wikipedia.org/wiki/IBM_1620 - um computador que realmente fez adição usando tabelas de pesquisa ...

Algo satisfatório sobre o uso de um mecanismo de tabela para 'acelerar' uma operação que poderia realmente ser realizada em uma instrução.

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

Opa, não é *char (embora não seja a multiplicação)
Sarge Borsch

A pesquisa de tabela usa adição - (a [i]) é igual a (* (a + i)).
Julie em Austin

@JulieinAustin eu mencionei isso. A pesquisa de tabela pode ser feita sem acréscimos, mesclando campos (conforme ilustrado no IBM 1620, consulte o link), mas é confuso configurá-lo em C - por um lado, é necessário alinhar a tabela com um endereço adequado para que os índices possam ser just or'd in.
greggo 02/03

8

Não sei ao certo o que constitui "trapaça" nessas postagens de "troll de código", mas isso multiplica 2 números inteiros arbitrários, em tempo de execução, sem operador *ou +usando bibliotecas padrão (C99).

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

8

Minha solução troll para 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;
}

11
+1 Boa implementação de avaliação de transporte. Eu gosto do seu código :)
yo '

@ BЈовић: A culpa é minha, pensei que trollar não era sobre entender. Nomes alterados e comentários adicionados.
Matthias

desculpa. Entendi mal o que é essa tag e o que realmente é o Q. Você deve reverter isso
BЈовић

@ Matthias, neste caso, é útil entender como funciona para que possamos apreciar o quão distorcida é essa operação de transporte de convergência. Em uma situação de código troll real os comentários poderiam ser redigido :-)
Greggo

Eu gostaria de ressaltar que, se você deseja adicionar números de bits reversos (com alto a baixo para carregar prop) e não possui uma instrução 'bitrev', essa provavelmente é uma abordagem perfeitamente razoável (depois de alterar para c> > = 1 é claro)
greggo 13/01

7

Existem muitas respostas boas aqui, mas não parece que muitas delas tirem vantagem do fato de que os computadores modernos são realmente poderosos. Existem várias unidades de processamento na maioria das CPUs, então por que usar apenas uma? Podemos explorar isso para obter ótimos resultados de desempenho.

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

Aqui está um exemplo de seu uso:

$ ./multiply
5 6
5 x 6 = 30

A #pragma omp paralleldiretiva faz com que o OpenMP divida cada parte do loop for em uma unidade de execução diferente, por isso estamos multiplicando em paralelo!

Observe que você precisa usar o -fopenmpsinalizador para dizer ao compilador para usar o OpenMP.


Peças de pesca à corrica:

  1. Enganador sobre o uso de programação paralela.
  2. Não funciona em números negativos (ou grandes).
  3. Na verdade, não divide as partes do forloop - cada thread executa o loop.
  4. Nomes de variáveis ​​irritantes e reutilização de variáveis.
  5. Há uma condição sutil de corrida answer--; na maioria das vezes, ele não aparece, mas ocasionalmente causa resultados imprecisos.

2
Por que não combinar isso com a resposta SIMD de Paul R, para que você possa executar 32x mais rápido do que 8x? Embora realmente, você queira envolver a GPU e os núcleos; então realmente iria brilhar. :)
abarnert

2
É melhor usar o OpenMPI para executá-lo em algumas máquinas em paralelo.
millinon

6

Infelizmente, a multiplicação é um problema muito difícil na ciência da computação. A melhor solução é usar a divisão:

#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

Na vida real, eu geralmente respondo a trollar com conhecimento, então aqui está uma resposta que nem trolla. Funciona para todos os intvalores, tanto quanto eu posso ver.

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

Isto é, até onde eu entendi, muito parecido com o modo como uma CPU pode realmente fazer a multiplicação de números inteiros. Primeiro, garantimos que pelo menos um dos argumentos ( a) seja positivo, lançando o sinal em ambos se afor negativo (e não, eu me recuso a contar a negação como um tipo de adição ou multiplicação). Em seguida, o while (a)loop adiciona uma cópia deslocada bdo resultado para cada bit definido a. O doloop implementa o r += xuso e, xor e a mudança no que é claramente um conjunto de semi-somadores, com os bits de transporte alimentados xaté que não haja mais (uma CPU real usaria somadores completos, o que é mais eficiente, mas C não ' não temos os operadores necessários para isso, a menos que você conte o +operador).


4
O autor da pergunta não correu atrás. Você deveria trollar.
John Dvorak

2
É um troll furtivo! A falha secreta está em um == INT_MIN.
Jander

11
@Jander hmm. Sim, essa é boa. Eu estou supondo (nos sistemas de complemento comuns de dois) o resultado de negar a ainda é negativo, e o while(a)loop nunca termina.
Hobbs

@ Hobbs Sim, isso parece certo para mim. Caso contrário, uma resposta muito bonita.
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;
}

11
Isso falhará terrivelmente se o resultado exceder o limite. Agradável! Eu acho que você não deve imprimir, no entanto.
John Dvorak

2
falha para a = 2, b = 2, c = 5
BЈовић

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

2
Note que esta é realmente uma tentativa de fazer a mesma coisa que o sucessor do Deep Thought, mas para todos os universos possivelmente , em vez de apenas aquele em que a resposta é 42. O que seria muito impressionante se não fosse o bug. E a falta de tratamento de erros no caso dos Vogons.
abarnert

11
Precisa de vários threads. Você sabe, para torná-lo eficiente.
Greggo

6

Jogando isso na mistura:

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

Na página de informações .

- Introduzir algo extremamente inaceitável ou irracional no código que não pode ser removido sem jogar tudo fora, tornando a resposta totalmente inútil para o OP.

- [...] A intenção é fazer a lição de casa em um idioma que o OP preguiçoso possa achar aceitável, mas ainda o frustre.


2
"sem usar os operadores de multiplicação e adição". Agradável que dobra das regras - este será absolutamente inútil para o :-) consulente
John Dvorak

2
Esta não é realmente uma solução em C. Além disso, ele falha ao compilar no meu ARM9.
abarnert

11
@abarnert: falha ao reconhecer sua afirmação como um argumento relevante.
Runium

@Sukminder: A pergunta é "É possível escrever um programa C ...?" O assembly embutido não é C. O fato de alguns compiladores C também poderem fazer assemblagem embutido não muda isso, assim como o fato de alguns compiladores C também poderem fazer C ++ ou ObjC faz com que C ++ ou ObjC conte como código C.
abarnert

2
@ abarnert: É um código incorporado amplamente utilizado em programas em C. Mesmo que seja a-raça cruzada pode-se argumentar que é um programa C . É até plausível que o OP o reconheça como código C. Claramente não é python, ou?
Runium

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

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

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

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

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

5

É possível escrever um programa C que multiplique dois números sem usar os operadores de multiplicação e adição?

Certo:

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

Mas é claro que isso é trapaça; obviamente ele quer ser capaz de fornecer dois números, certo?

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

Ora, isso não era óbvio para mim!
leewz

4

Não há aritmética como a aritmética de ponteiros:

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

A função fimplementa multiplicação. mainsimplesmente chama isso com dois argumentos.
Funciona para números negativos também.


Negativo a, sim, negativo, bacho que não. Mas isso pode ser corrigido de várias maneiras criativas. Mais simples seria sign_a ^ = sign_b, sign_b = 0.
MSalters

@MSalters, testado e funciona para todas as combinações de sinais (com Linux / gcc).
ugoren

3

C #

Eu acho que subtração e negação não são permitidas ... Enfim:

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

11
Essa é exatamente a solução em que pensei ... mas, chegando atrasado à festa, sabia que era uma questão de rolar para baixo até descobrir que alguém já havia escrito. Ainda assim, você recebe um - (- 1) de mim.
Floris

3

C com intrínsecas SSE (porque tudo está melhor com o 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;
}

A grande vantagem dessa implementação é que ela pode ser facilmente adaptada para realizar 4 multiplicações paralelas sem *ou +se necessário.


Eu não acho que isso é corrico ...
John Dvorak

Realmente ? Eu pensei que o uso inútil, gratuito e específico da arquitetura do SIMD o qualificaria para trollagem de código?
Paul R

hmm ... verdade. Não sabia que isso era específico da arquitetura.
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;
}
  • trabalhar apenas para resultados de multiplicação <1 000 000
  • Até agora, não podemos nos livrar do operador - possivelmente melhorando aqui
  • usando o especificador de formato% n em printf para contar o número de caracteres impressos (eu o publico principalmente para lembrar a existência% n em C, em vez de% n poderia, é claro, ser strlen etc.)
  • Imprime a * b caracteres de '*'

Agora, aguardando a solução 'emulação de máquina de turing'.
Greggo

11
while strlen(cg) != aé um método muito difícil de eliminar o --(torna O (N * N)).
precisa saber é o seguinte

3

Provavelmente rápido demais :-(

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

11
Ungh. Isso não é trolling. Esta é uma maneira inteiramente razoável de fazer isso.
John Dvorak

11
É trolly porque é :-) muito rápido
Timtech

3

Essa versão do Haskell funciona apenas com números inteiros não negativos, mas faz a multiplicação da maneira que as crianças aprendem primeiro. Ou seja, 3x4 é 3 grupos de 4 coisas. Nesse caso, as "coisas" sendo contadas são entalhes ('|') em um pedaço de pau.

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

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

Isso pode funcionar em C99, se o tempo estiver bom, e seu compilador oferecer suporte a bobagens indefinidas.


3

Como o OP não pediu C , aqui está um no SQL (Oracle)!

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

WITH
   aa AS (
      SELECT LEVEL AS lvl 
      FROM dual
      CONNECT BY LEVEL <= &a
   ),
   bb AS (
      SELECT LEVEL AS lvl
      FROM dual
      CONNECT BY LEVEL <= &b
   )
SELECT COUNT(*) AS multiplication
FROM aa CROSS JOIN bb;

11
Meu Deus, está cheio de *s!
Paul R

11
@PaulR :) mas eles não são operadores .
precisa saber é

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

Pode conter vestígios de 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;
}

Execução de teste:

$ ./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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.