Làm thế nào để viết chương trình C để nhân mà không cần sử dụng toán tử * và +?


68

Có thể viết chương trình C nhân hai số mà không cần sử dụng toán tử nhân và toán tử cộng không?

Tôi tìm thấy điều này trên Stack Overflow . Xin hãy giúp lập trình viên nghèo này với vấn đề của mình. Và xin vui lòng không đưa ra câu trả lời như thế c = a/(1/((float)b)), giống hệt như c = a*b. (Và đã được đưa ra như một câu trả lời.)

Câu trả lời với nhiều sự ủng hộ nhất vào ngày 19 tháng 1 năm 2014 chiến thắng.

Lưu ý: Đây là một câu hỏi . Xin đừng coi trọng câu hỏi và / hoặc câu trả lời. Thêm thông tin là trong trolling mã .


2
@PaulR sử dụng trí tưởng tượng của bạn
John Dvorak

26
Code-golf.SE không nên là nơi để bạn chế giễu các câu hỏi bạn đã thấy trên StackOverflow.
Gareth

17
@Gareth, bạn có chắc không? Dòng đầu tiên cho thấy điều này có thể khá thích hợp.
Darren Stone

5
Tôi đang chờ đợi ai đó viết một thuật toán dựa trên giấc ngủ
kb_sou

21
Câu hỏi này không vô lý như nó nghe. Phần cứng máy tính thực tế (bóng bán dẫn) không có các hoạt động nhân và thêm - chúng có các hoạt động logic cơ bản như KHÔNG, VÀ, HOẶC, XOR. Tìm hiểu làm thế nào để trả lời câu hỏi này có thể cung cấp cho bạn cái nhìn sâu sắc tuyệt vời về cách một máy tính thực sự hoạt động ở mức độ của các cổng logic.
Gabe

Câu trả lời:


147

Luôn luôn sử dụng đệ quy

Recusion là cách đúng đắn!

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
Tôi sẽ cho +3 nếu tôi có thể: một cho đệ quy cuối cùng, một cho ??::không có dấu ngoặc đơn, một cho giải quyết vấn đề mà không cố gắng điều chỉnh các quy tắc;)
yo '

10
Nếu Picasso là một lập trình viên ...
R Hughes

4
@SargeBorsch Nhưng niềm vui sẽ ở đâu trong đó ?
Oberon

3
@HC_ incHàm kiểm tra đối số của nó để xem bit thấp nhất là 1; nếu vậy, nó tự gọi nó trên các bit trên còn lại của đối số và trả về kết quả với cùng một bit thấp đã được kiểm tra được đặt thành 0, trong khi nếu không (tức là bit thấp nhất 0), nó thay thế 0bằng a 1và trả về kết quả . Quá trình này rất giống với những gì bạn sẽ làm nếu bạn thêm các giá trị bằng tay, chữ số nhị phân bằng chữ số nhị phân.
JAB

2
Không phải hàm tăng có đi vào một vòng lặp vô hạn cho -1 không? (0xFFFF) ideone hiển thị (-1 >> 1) == -1.
Kẻ hủy diệt

87

Bạn sẽ phải biên dịch chương trình mỗi lần, nhưng nó thực hiện phép nhân bất kỳ số nguyên dương nào chính xác trong bất kỳ phiên bản C hoặc C ++ nào.

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

 typedef char buffer[A][B];

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

4
Đặt nó trong một cấu trúc và bạn không cần bộ nhớ.
Ben Jackson

4
Hahahah tuyệt vời !!
Almo

1
sử dụng "%zu"chuỗi định dạng.
Grijesh Chauhan

5
sizeof(char[A][B])sẽ chỉ hoạt động (trừ khi A <= 0 hoặc B <= 0 hoặc A * B tràn ra, trong trường hợp đó bạn sẽ nhận được một loại lỗi 'loại xấu')
greggo

3
@DavidR Richby - Tôi có thể đơn giản hóa mã main(){return sizeof(char[A][B]);}và bạn biên dịch bằng cách sử dụngcc -DA=6 -DB=7 a.c; ./a.out; echo $?
Mark Lakata

47

Nếu bạn ổn với một chút thiếu chính xác, bạn có thể sử dụng phương pháp 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));
}

Thí dụ:

$ ./mul 300 250
300 * 250 = 74954

Tôi đoán điều này có thể đủ tốt;)


3
Bạn có phiếu bầu của tôi. Tôi nghe nói Monte Carlo là những gì NASA sử dụng cho số học của nó. Nhưng tôi muốn thấy điều này mà không có hai trường hợp của ++toán tử.
Darren Stone

1
@DarrenStone-= -1
TimTech

20
@Timtech |= 1(sẽ hoạt động với 50% số, 100% thời gian)
Darren Stone

2
+1, nhưng lưu ý rằng nó có thể quá chậm và bạn có thể thêm hỗ trợ đa luồng, cẩn thận khóa 'Count ++' :-)
greggo

1
Luôn luôn printftăng: printf("%*cc%n\n", count, &count, 'c');(In 'c' đếm số lần, rồi 'c' khác và lưu trữ số lượng ký tự được viết lại count.
MSalters

45

Vì bạn không chỉ định kích thước của số, tôi giả sử rằng bạn có nghĩa là hai số một bit.

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

Nếu bạn muốn triển khai hiệu quả tối đa, hãy sử dụng triển khai nhỏ sau đây:

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

Lưu ý rằng nó vẫn chỉ chấp nhận các bit mặc dù các kiểu là ints ẩn - nó tốn ít mã hơn và do đó hiệu quả hơn. (Và vâng, nó biên dịch.)


8
Đẹp. Một sự cố ý giải thích sai câu hỏi :-)
John Dvorak

6
Bạn có thể tối ưu hóa điều này để return a && b;. Nó ngắn hơn, vì vậy nó nhanh hơn.
Ry-

1
@minitech: Tôi quyết định chống lại điều đó để làm cho mã hơi tệ hơn. Nếu tôi muốn đi xa hơn với điều đó, tôi sẽ làm nó return a&b;.
Cel Skeggs

1
C #include<stdbool.h>phải xác định truefalse.
leewz

1
Yeah, #include<stdbool.h>dường như chỉ là ba #defines mà bạn có thể làm cho mình ( true, false, bool, và một lá cờ để đánh dấu rằng nó được kích hoạt). Bạn cũng có thể thực hiện một mẹo từ một trong những câu trả lời khác và sử dụng ẩn intcho phiên bản "ngắn".
leewz

31

Đây là một kịch bản shell đơn giản để làm điều đó:

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ẬP NHẬT: Tất nhiên, để làm điều đó trong C, chỉ cần bọc nó vào exec("bash", "-c", ...). (Cảm ơn, AmeliaBR)


41
Tôi không thể quyết định cái nào tệ hơn. Rằng bạn đang thuê ngoài tính toán của bạn cho công cụ tìm kiếm hoặc công cụ tìm kiếm bạn chọn là Bing. Thật không may, tôi không nghĩ rằng điều này sẽ làm việc cho OP không may mắn của chúng tôi, người cần một cái gì đó trong C.
AmeliaBR

5
Cảm ơn vì đã bắt được sự giám sát đó. FYI, tôi đang sử dụng Bing vì Google khiến việc đưa ra các yêu cầu như thế này trở nên phức tạp hơn - bạn phải thêm các tiêu đề để thuyết phục Google yêu cầu của bạn thực sự đến từ trình duyệt.
Vroo

2
@abarnert hmm ... Bing có hiểu "thời gian" không? Wolfram Alpha có thể, mặc dù.
John Dvorak

2
@JanDvorak: Vâng, Wolfram hoạt động. (Lưu ý %20để tránh sử dụng bất kỳ +dấu hiệu nào .) Nhưng bạn vẫn cần phân tích đầu ra (tính bằng C) để lấy giá trị ra khỏi nó. Điều này sẽ đặc biệt khó khăn, vì đầu ra dường như là một hình ảnh, không phải văn bản. Phân tích cú pháp HTML cộng với OCR có thể làm cho câu trả lời tốt nhất có thể cho vấn đề này.
abarnert

3
@JanDvorak: Điều đó không vui chút nào. Tôi đã mong chờ ai đó viết một thư viện OCR đơn giản mà không cần thêm hoặc nhân
abarnert

27

Tại sao, hãy thực hiện tìm kiếm chia đôi đệ quy giữa INT64_MIN và 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 Nó sẽ vui vẻ sigsegv với một số giá trị. ;)


18

Thật không may, điều này chỉ hoạt động cho số nguyên.

Vì bổ sung không được phép, trước tiên, hãy xây dựng một toán tử gia tăng:

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

Tiếp theo, chúng ta phải xử lý các dấu hiệu. Đầu tiên, tìm bit dấu:

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

Sau đó lấy dấu và độ lớn của từng đối số. Để phủ định một số trong phần bù hai, đảo ngược tất cả các bit và thêm một số.

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

Để nhân hai số nguyên dương, chúng ta có thể sử dụng ý nghĩa hình học của phép nhân:

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

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

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

troll

  • Bổ sung là không được phép, nhưng a++thực sự được tính là bổ sung? Tôi đặt cược giáo viên dự định cho phép nó.
  • Dựa vào phần bù của hai, nhưng đó là hành vi được xác định theo thực hiện và nền tảng đích không được chỉ định.
  • Tương tự, giả sử phép trừ và chia cũng không được phép.
  • << thực sự là nhân với một sức mạnh của hai, vì vậy về mặt kỹ thuật nó không được phép.
  • sơ đồ không cần thiết là không cần thiết. Ngoài ra, nó có thể đã được chuyển để lưu một dòng.
  • dịch chuyển lặp đi lặp lại -1không phải là cách tốt nhất để tìm bit dấu. Ngay cả khi không có hằng số tích hợp, bạn có thể thực hiện dịch chuyển logic bên phải -1, sau đó đảo ngược tất cả các bit.
  • XOR -1 không phải là cách tốt nhất để đảo ngược tất cả các bit.
  • Toàn bộ trò chơi đố với biểu diễn cường độ ký hiệu là không cần thiết. Chỉ cần chuyển sang số học không dấu và mô-đun sẽ làm phần còn lại.
  • vì giá trị tuyệt đối của MIN_INT(AKA signBit) là âm, điều này phá vỡ giá trị đó. May mắn thay, nó vẫn hoạt động trong một nửa các trường hợp, bởi vì MIN_INT * [even number] nên bằng không.Ngoài ra, plusOnenghỉ cho -1, gây ra các vòng lặp vô hạn bất cứ khi nào kết quả tràn ra. plusOnechỉ hoạt động tốt cho bất kỳ giá trị. Xin lỗi vì sự nhầm lẫn.

+1 cho một troll mã thực tế: Có vẻ như nó sẽ hoạt động, nhưng rất có thể nó sẽ nổ tung trên OP và anh ta sẽ không biết tại sao.
Kevin

1
Có thể thực hiện phép cộng mà không cần BẤT K Toán toán tử bổ sung nào bằng cách chỉ sử dụng shift, XOR và AND. Tất cả những điều này của ++ đang làm tôi đau đầu - một bit THÊM với carry là (x ^ y) | ((x & y) << 1) (sửa đổi bất kỳ lỗi nào do gõ vào hộp văn bản nhỏ nhảm nhí này.)
Julie ở Austin

@JulieinAustin vâng. Thuật toán thậm chí còn kém hiệu quả hơn mức cần thiết. Tôi có nên sửa đổi danh sách troll? :-)
John Dvorak

1
@JulieinAustin (x ^ y) | ((x & y) << 1)không hoạt động tốt, nó sẽ không truyền bá khi x hoặc y và carry đều đúng ở cùng một vị trí :)
hobbs

Giải pháp @hobbs: thay vì ORing, hãy thêm chúng theo cách đệ quy nếu carry không khác không.
John Dvorak

14

Hoạt động cho số dấu phẩy động là tốt:

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

11

Mọi người đều biết rằng Python dễ sử dụng hơn C. Và Python có các hàm tương ứng với mọi toán tử, trong trường hợp bạn không thể sử dụng toán tử. Đó chính xác là định nghĩa vấn đề của chúng tôi, phải không? Vì thế:

#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

Không có câu trả lời nào khác về mặt lý thuyết. Như bình luận đầu tiên về câu hỏi nói:

Vui lòng cụ thể hơn về "số"

Chúng ta cần xác định phép nhân và số, trước khi có câu trả lời. Một khi chúng ta làm, vấn đề trở nên tầm thường.

Cách phổ biến nhất để thực hiện điều này khi bắt đầu logic toán học là xây dựng các mệnh lệnh von Neumann trên lý thuyết tập hợp ZF , và sau đó sử dụng các tiên đề Peano .

Điều này dịch tự nhiên sang C, giả sử bạn có một loại bộ có thể chứa các bộ khác. Nó không phải chứa bất cứ thứ gì ngoại trừ các tập hợp, điều này làm cho nó trở nên tầm thường (không có nội dung nào void*vô nghĩa trong hầu hết các thư viện được thiết lập), vì vậy tôi sẽ để việc thực hiện như một bài tập cho người đọc.

Vì vậy, đầu tiên:

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

Nếu bạn muốn mở rộng điều này thành số nguyên, số hữu tỷ, số thực, siêu thực, v.v., bạn có thể với độ chính xác vô hạn (giả sử bạn có bộ nhớ và CPU vô hạn), để khởi động. Nhưng như Kroenecker nổi tiếng đã nói, Chúa đã tạo ra những con số tự nhiên; tất cả những thứ khác là công việc của con người, vậy thực sự, tại sao phải bận tâm?


1
Ồ Bạn thậm chí còn chậm hơn tôi.
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 );
}

Nếu bạn coi hack [b] là gian lận (vì nó thực sự là một add) thì điều này sẽ hoạt động thay thế. Nhưng tra cứu bảng liên quan đến con trỏ thêm quá.

Xem http://en.wikipedia.org/wiki/IBM_1620 - một conputer thực sự đã bổ sung bằng cách sử dụng bảng tra cứu ...

Một cái gì đó thỏa mãn về việc sử dụng cơ chế bảng để 'tăng tốc' một thao tác thực sự có thể được thực hiện trong một lệnh.

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

Rất tiếc, có *char (mặc dù nó không nhân)
SUND Borsch

Uh, tra cứu bảng sử dụng phép cộng - (a [i]) giống với (* (a + i)).
Julie ở Austin

@JulieinAustin Tôi đã đề cập đến điều đó. Việc tra cứu bảng có thể được thực hiện mà không cần thêm, bằng cách hợp nhất các trường (như được minh họa trong IBM 1620, xem liên kết) nhưng thật khó để thiết lập điều đó trong C - vì một điều bạn cần phải căn chỉnh bảng thành một địa chỉ thích hợp để các chỉ mục có thể chỉ cần tham gia.
greggo

8

Tôi không chắc chắn điều gì cấu thành "gian lận" trong các bài đăng "troll mã" này, nhưng điều này nhân 2 số nguyên tùy ý, trong thời gian chạy, không có *hoặc có +toán tử sử dụng thư viện chuẩn (C99).

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

8

Giải pháp troll của tôi cho 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 Thực hiện tốt việc đánh giá mang. Tôi thích mã của bạn :)
yo '

@ BЈовић: Lỗi của tôi, tôi nghĩ trolling không phải là về sự hiểu biết. Thay đổi tên và thêm ý kiến.
Matthias

lấy làm tiếc. Tôi đã hiểu nhầm thẻ đó là gì và Q thực sự là về cái gì. Bạn nên hoàn nguyên nó
Bовић

@Matthias trong trường hợp này thật hữu ích để hiểu cách thức hoạt động của nó để chúng tôi có thể đánh giá cao mức độ xoắn của hoạt động mang theo hội tụ này. Trong một tình huống mã troll thực tế, các ý kiến ​​có thể được điều chỉnh lại :-)
greggo

Tôi muốn chỉ ra rằng nếu bạn muốn thêm các số đảo ngược bit (với mức cao để lo mang theo) và bạn không có hướng dẫn 'bitrev', đây có lẽ là một cách tiếp cận hoàn toàn hợp lý (sau khi thay đổi thành c> > = 1 tất nhiên)
greggo

7

Có rất nhiều câu trả lời hay ở đây, nhưng có vẻ như nhiều người trong số họ lợi dụng thực tế là máy tính hiện đại thực sự mạnh mẽ. Có nhiều đơn vị xử lý trong hầu hết các CPU, vậy tại sao chỉ sử dụng một? Chúng ta có thể khai thác điều này để có được kết quả hiệu suất tuyệt vời.

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

Đây là một ví dụ về việc sử dụng nó:

$ ./multiply
5 6
5 x 6 = 30

Lệnh #pragma omp parallelnày làm cho OpenMP chia từng phần của vòng lặp for cho một đơn vị thực thi khác nhau, vì vậy chúng tôi sẽ nhân song song!

Lưu ý rằng bạn phải sử dụng -fopenmpcờ để báo cho trình biên dịch sử dụng OpenMP.


Phần Troll:

  1. Sai lệch về việc sử dụng lập trình song song.
  2. Không hoạt động trên số âm (hoặc lớn).
  3. Không thực sự phân chia các phần của forvòng lặp - mỗi luồng chạy vòng lặp.
  4. Làm phiền tên biến và tái sử dụng biến.
  5. Có một điều kiện chủng tộc tinh tế trên answer--; hầu hết thời gian, nó sẽ không xuất hiện, nhưng đôi khi nó sẽ gây ra kết quả không chính xác.

2
Tại sao không kết hợp câu trả lời này với câu trả lời SIMD của Paul R, để bạn có thể chạy nhanh gấp 32 lần thay vì 8 lần? Mặc dù thực sự, bạn muốn có GPU tham gia cũng như các lõi; sau đó nó sẽ thực sự rực sáng. :)
abarnert

2
Cũng có thể sử dụng OpenMPI để chạy song song trên một số máy.
millinon

6

Thật không may, phép nhân là một vấn đề rất khó khăn trong khoa học máy tính. Giải pháp tốt nhất là sử dụng phép chia thay thế:

#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

Trong cuộc sống thực, tôi thường phản ứng với việc trolling với kiến ​​thức, vì vậy đây là một câu trả lời hoàn toàn không phải là troll. Nó hoạt động cho tất cả các intgiá trị như tôi có thể thấy.

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

Theo sự hiểu biết tốt nhất của tôi, rất giống như cách CPU thực sự có thể nhân số nguyên. Đầu tiên, chúng tôi đảm bảo rằng ít nhất một trong các đối số ( a) là dương bằng cách lật dấu hiệu trên cả hai nếu alà âm (và không, tôi từ chối tính phủ định là một dạng cộng hoặc nhân). Sau đó, while (a)vòng lặp thêm một bản sao được dịch chuyển bvào kết quả cho mỗi bit được đặt a. Các dovòng lặp thực hiện r += xviệc sử dụng và, xor, và thay đổi trong những gì là rõ ràng một bộ nửa adders, với các bit carry ăn trở lại xcho đến khi không có nhiều (một CPU thực sẽ sử dụng adders đầy đủ, đó là hiệu quả hơn, nhưng C doesn' Chúng ta có các toán tử mà chúng ta cần cho việc này, trừ khi bạn đếm +toán tử).


4
Người hỏi không troll. Bạn có nghĩa vụ phải troll.
John Dvorak

2
Đó là một trò troll lén lút! Thất bại bí mật là trên a == INT_MIN.
Jander

1
@Jander hmm. Vâng, đó là một trong những tốt. Tôi đoán (trên các hệ thống bổ sung của hai thông thường) kết quả của việc phủ định a vẫn là âm và while(a)vòng lặp không bao giờ kết thúc.
hobbs

@hobbs Vâng, điều đó nghe đúng với tôi. Nếu không thì một câu trả lời rất đẹp.
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
Điều này sẽ thất bại khủng khiếp nếu kết quả tràn ra. Đẹp! Tôi đoán bạn không nên in, mặc dù.
John Dvorak

2
không thành công cho a = 2, b = 2, c = 5
BЈовић

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

2
Lưu ý rằng đây thực sự là một nỗ lực để làm điều tương tự như người kế nhiệm của Deep Think, nhưng đối với tất cả các vũ trụ có thể , thay vì chỉ có câu trả lời là 42. Sẽ rất ấn tượng nếu không có lỗi. Và việc thiếu xử lý lỗi trong trường hợp của Vogons.
abarnert

1
Cần nhiều chủ đề. Bạn biết đấy, để làm cho nó hiệu quả.
greggo

6

Ném cái này vào hỗn hợp:

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

Từ trang thông tin .

- Giới thiệu một cái gì đó cực kỳ không thể chấp nhận hoặc không hợp lý trong mã không thể xóa mà không vứt bỏ mọi thứ, đưa ra câu trả lời hoàn toàn vô dụng cho OP.

- [Mạnh] Ý định là làm bài tập về nhà bằng ngôn ngữ mà OP lười biếng có thể nghĩ là chấp nhận được, nhưng vẫn làm anh thất vọng.


2
"Không sử dụng các toán tử nhân và cộng". Sự uốn cong tuyệt vời của các quy tắc - điều này sẽ hoàn toàn vô dụng đối với người hỏi :-)
John Dvorak

2
Đây không thực sự là một giải pháp C. Thêm vào đó, nó không thể biên dịch trên ARM9 của tôi.
abarnert

1
@abarnert: Không nhận ra tuyên bố của bạn là một đối số có liên quan.
Runium

@Sukminder: Câu hỏi là "Có thể viết chương trình C không?" Lắp ráp nội tuyến không phải C. Thực tế là một số trình biên dịch C cũng có thể thực hiện lắp ráp nội tuyến không thay đổi điều đó, bất kể thực tế là một số trình biên dịch C cũng có thể thực hiện C ++ hoặc ObjC làm cho C ++ hoặc ObjC được tính là mã C.
abarnert

2
@abarnert: Đó là mã nhúng được sử dụng rộng rãi trong các chương trình C. Mặc dù nó là một cross-giống người ta có thể tranh luận đó là một chương trình C . OP thậm chí còn hợp lý sẽ nhận ra đó là mã C. Nó rõ ràng không phải là trăn, hay?
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ó thể viết chương trình C nhân hai số mà không cần sử dụng toán tử nhân và toán tử cộng không?

Chắc chắn rồi:

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

Nhưng tất nhiên đó là gian lận; rõ ràng anh ta muốn có thể _supply) hai số, phải không?

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

Tại sao, nó không rõ ràng với tôi cả!
leewz

4

Không có số học như số học con trỏ:

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

Hàm fthực hiện phép nhân. mainchỉ đơn giản gọi nó với hai đối số.
Hoạt động cho số âm là tốt.


Tiêu cực a, vâng, tiêu cực bTôi không nghĩ như vậy. Nhưng điều đó có thể sửa chữa theo nhiều cách sáng tạo. Đơn giản nhất sẽ là Sign_a ^ = sign_b, sign_b = 0.
MSalters

@MSalters, đã thử nghiệm và hoạt động cho tất cả các kết hợp dấu (với Linux / gcc).
xấu xí

3

C #

Tôi nghĩ rằng phép trừ và phủ định không được phép ... Dù sao đi nữa:

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

1
Đây chính xác là giải pháp tôi nghĩ đến ... nhưng đến bữa tiệc muộn tôi biết đó là vấn đề cuộn xuống cho đến khi tôi thấy rằng ai đó đã viết nó. Tuy nhiên, bạn nhận được một - (- 1) từ tôi.
Floris

3

C với nội tại SSE (vì mọi thứ đều tốt hơn với 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;
}

Ưu điểm lớn của việc thực hiện này là nó có thể dễ dàng được điều chỉnh để thực hiện 4 phép nhân song song mà không *hoặc +nếu cần.


Tôi không nghĩ đây là trò troll ...
John Dvorak

Có thật không ? Tôi nghĩ rằng việc sử dụng SIMD vô nghĩa, vô cớ và kiến ​​trúc cụ thể sẽ đủ điều kiện để trolling mã?
Paul R

hmm ... đúng Không nhận ra đây là kiến ​​trúc cụ thể.
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;
}
  • chỉ làm việc cho kết quả nhân <1 000 000
  • Cho đến nay không thể thoát khỏi - nhà điều hành, có thể tăng cường ở đây
  • sử dụng công cụ xác định định dạng% n trong printf để đếm số lượng ký tự được in (tôi đăng bài này chủ yếu để nhắc về sự tồn tại% n trong C, thay vì% n tất nhiên có thể là strlen, v.v.)
  • In một ký tự * b của '*'

Bây giờ đang chờ giải pháp 'mô phỏng máy turing'.
greggo

1
trong khi đó strlen(cg) != alà một phương pháp rất trolling để loại bỏ --(làm cho nó O (N * N)).
MSalters

3

Có lẽ là quá nhanh :-(

   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
Ưm. Đây không phải là trolling. Đây là một cách hoàn toàn hợp lý để làm điều này.
John Dvorak

1
Thật là
rắc rối

3

Phiên bản Haskell này chỉ hoạt động với các số nguyên không âm, nhưng nó nhân với cách trẻ em học nó lần đầu tiên. Tức là 3x4 là 3 nhóm gồm 4 thứ. Trong trường hợp này, "những thứ" được tính là các rãnh ('|') trên một cây gậy.

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

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

Điều này có thể hoạt động trong C99, nếu thời tiết phù hợp và trình biên dịch của bạn hỗ trợ vô nghĩa không xác định.


3

OP không yêu cầu C , đây là một trong (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
Chúa ơi, nó đầy *s!
Paul R

1
@PaulR :) nhưng họ không phải là nhà khai thác .
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;
}

Có thể chứa dấu vết của 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;
}

Chạy thử nghiệm:

$ ./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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.