จะเขียนโปรแกรม C สำหรับการคูณโดยไม่ต้องใช้ตัวดำเนินการ * และ + อย่างไร


68

เป็นไปได้ไหมที่จะเขียนโปรแกรม C ที่คูณสองตัวเลขโดยไม่ต้องใช้ตัวคูณและตัวดำเนินการเพิ่มเติม?

ฉันพบนี้ในกองมากเกิน โปรดช่วยโปรแกรมเมอร์ผู้น่าสงสารคนนี้ด้วยปัญหาของเขา และโปรดอย่าให้คำตอบเหมือนซึ่งเป็นเหมือนกับc = a/(1/((float)b)) c = a*b(และได้รับคำตอบแล้ว)

คำตอบที่มีผู้โหวตสูงสุดในวันที่ 19 มกราคม 2014 เป็นผู้ชนะ

หมายเหตุ: นี่เป็นคำถามการกรุณาอย่าใช้คำถามและ / หรือคำตอบอย่างจริงจัง ข้อมูลเพิ่มเติมในรหัสหลอก


2
@ Paul ใช้จินตนาการของคุณ
John Dvorak

26
Code-golf.SE ไม่ควรเป็นสถานที่สำหรับให้คุณจำลองคำถามที่คุณเห็นใน StackOverflow
Gareth

17
@ กาเร็ ธ คุณแน่ใจเหรอ? บรรทัดแรกของนี้บ่งชี้ว่านี่อาจจะเหมาะสมค่อนข้าง
Darren Stone

5
ฉันกำลังรอให้ใครบางคนเขียนอัลกอริทึมโดยนอน
kb_sou

21
คำถามนี้ไม่ได้ไร้สาระอย่างที่เห็น ฮาร์ดแวร์คอมพิวเตอร์จริง (ทรานซิสเตอร์) ไม่มีการคูณและเพิ่มการดำเนินการ - พวกมันมีการดำเนินการทางตรรกะขั้นพื้นฐานเช่น NOT, AND, OR, XOR การหาวิธีตอบคำถามนี้จะช่วยให้คุณเข้าใจอย่างถ่องแท้ว่าคอมพิวเตอร์ทำงานอย่างไรในระดับของประตูตรรกะ
Gabe

คำตอบ:


147

ใช้การสอบถามซ้ำทุกครั้ง

Recusion เป็นวิธีที่ถูกต้อง!

int inc(int x) {
    return x&1?inc(x>>1)<<1:x|1;
}

int dec(int x) {
    return x&1?x^1:dec(x>>1)<<1|1;
}

int add(int x, int y) {
    return x?add(dec(x),inc(y)):y;
}

int mul(int x, int y) {
    return x?x^1?add(y,mul(dec(x),y)):y:0;
}

int main() {
    int a, b;
    scanf("%i\n%i", &a, &b);
    printf("%i", mul(a,b));
}

8
ฉันจะให้ 3 ถ้าผม: หนึ่งสำหรับการเรียกซ้ำที่ดีที่สุดหนึ่ง??::โดยไม่ต้องวงเล็บหนึ่งในการแก้ปัญหาที่เกิดขึ้นโดยไม่ต้องพยายามที่จะปรับแต่งกฎ;)
โย'

10
หากปิกัสโซเป็นโปรแกรมเมอร์ ...
R ฮิวจ์

4
@SargeBorsch แต่ไหนสนุกจะอยู่ในที่ ?
Oberon

3
@HC_ incฟังก์ชั่นทดสอบอาร์กิวเมนต์เพื่อดูว่าบิตต่ำสุดคือ1; ถ้าเป็นเช่นนั้นก็เรียกตัวเองในบิตบนที่เหลือของการโต้แย้งและผลตอบแทนที่มีบิตต่ำเช่นเดียวกับที่ได้รับการตรวจสอบถูกตั้งค่าให้0ในขณะที่ถ้าไม่ได้ (เช่นบิตต่ำสุด0) จะแทนที่ที่0มี1และผลตอบแทน . กระบวนการนี้คล้ายกับสิ่งที่คุณทำถ้าคุณเพิ่มค่าด้วยมือเป็นเลขฐานสองเป็นเลขฐานสอง
JAB

2
ฟังก์ชันการเพิ่มขึ้นไม่เข้ากับการวนซ้ำไม่สิ้นสุดสำหรับ -1 หรือไม่ (0xFFFF) ideone แสดง (-1 >> 1) == -1
Destrictor

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"สตริงรูปแบบ
Grijesh Chauhan

5
เพียงแค่sizeof(char[A][B])จะทำงาน (เว้นแต่ <= 0 หรือ B <= 0 หรือ A * B ล้นซึ่งในกรณีที่คุณควรจะได้รับการจัดเรียง 'ชนิดเลว' ของข้อผิดพลาด)
greggo

3
@DavidRicherby - ฉันสามารถทำให้โค้ดง่ายขึ้นmain(){return sizeof(char[A][B]);}และคุณคอมไพล์โดยใช้cc -DA=6 -DB=7 a.c; ./a.out; echo $?
Mark Lakata

47

หากคุณไม่เป็นที่พอใจเล็กน้อยคุณสามารถใช้วิธี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));
}

ตัวอย่าง:

$ ./mul 300 250
300 * 250 = 74954

ฉันเดาว่านี่อาจจะดีพอ)


3
คุณมีคะแนนของฉัน ฉันได้ยินมาว่ามอนติคาร์โลเป็นสิ่งที่นาซ่าใช้สำหรับเลขคณิตของมัน แต่ฉันต้องการเห็นสิ่งนี้โดยไม่มีตัวดำเนินการสอง++ตัว
Darren Stone

1
@DarrenStone-= -1
Timtech

20
@Timtech |= 1(จะทำงานกับ 50% ของตัวเลข 100% ของเวลา)
Darren Stone

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

โปรดทราบว่ามันยังคงยอมรับบิตเพียงอย่างเดียวแม้ว่าชนิดนั้นจะเป็น ints โดยนัย - ใช้รหัสน้อยลงและดังนั้นจึงมีประสิทธิภาพมากขึ้น (และใช่มันรวบรวม)


8
ดี การตีความคำถามโดยไม่เจตนา :-)
John Dvorak

6
return a && b;คุณสามารถเพิ่มประสิทธิภาพนี้ มันสั้นกว่าดังนั้นเร็วกว่า
Ry-

1
@minitech: ฉันตัดสินใจว่าเพื่อให้รหัสแย่ลงเล็กน้อย return a&b;ถ้าผมต้องการที่จะไปต่อกับที่ฉันต้องการให้มัน
Cel Skeggs

1
C มี#include<stdbool.h>การกำหนดและtrue false
leewz

1
ใช่#include<stdbool.h>ดูเหมือนว่าจะเป็นเพียงสาม#defineของที่คุณสามารถทำเอง ( true, false, boolและธงในการทำเครื่องหมายที่ถูกเปิดใช้งาน) นอกจากนี้คุณยังสามารถใช้เคล็ดลับจากหนึ่งในคำตอบอื่น ๆ และใช้โดยนัยintสำหรับรุ่น "สั้น"
leewz

31

นี่คือเชลล์สคริปต์แบบง่ายๆที่จะทำ:

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

UPDATE: แน่นอนที่จะทำมันใน C exec("bash", "-c", ...)เพียงห่อไว้ใน (ขอบคุณ AmeliaBR)


41
ฉันไม่สามารถตัดสินใจได้ว่าอันใดอันยิ่งใหญ่กว่า ว่าคุณกำลังจ้างการคำนวณของคุณไปยังเครื่องมือค้นหาหรือว่าเครื่องมือค้นหาที่คุณเลือกคือ Bing น่าเสียดายที่ฉันไม่คิดว่าสิ่งนี้จะใช้ได้กับ OP ที่ไร้ความสุขของเราซึ่งต้องการบางสิ่งบางอย่างใน C
AmeliaBR

5
ขอขอบคุณที่ติดตามการกำกับดูแล ปีงบประมาณฉันใช้ Bing เพราะ Google ทำให้ซับซ้อนในการออกคำขอเช่นนี้คุณต้องเพิ่มส่วนหัวเพื่อโน้มน้าวให้ Google คำขอของคุณมาจากเบราว์เซอร์
Vroo

2
@abarnert อืม ... Bing เข้าใจ "เวลา" หรือไม่ แม้ว่า Wolfram Alpha อาจจะ
John Dvorak

2
@JanDvorak: ใช่Wolframทำงาน (โปรดสังเกต%20เพื่อหลีกเลี่ยงการใช้+สัญญาณใด ๆ) แต่คุณยังต้องแยกวิเคราะห์เอาต์พุต (ใน C) เพื่อให้ได้ค่าออกมา ซึ่งจะยุ่งยากโดยเฉพาะอย่างยิ่งเนื่องจากผลลัพธ์ที่ปรากฏเป็นภาพไม่ใช่ข้อความ การแยกวิเคราะห์ HTML และ OCR อาจทำให้คำตอบที่ดีที่สุดสำหรับปัญหานี้
abarnert

3
@JanDvorak: ไม่สนุกเลย ผมกำลังมองไปข้างหน้าเพื่อคนเขียนห้องสมุด OCR ที่เรียบง่ายด้วยไม่เพิ่มหรือคูณ ...
abarnert

27

ทำไมเราลองทำการค้นหาแบบแบ่งครึ่งระหว่าง INT64_MIN และ INT64_MAX!

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

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

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

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

ป.ล. มันจะมีความสุขกับ sigsegv ด้วยค่าบางอย่าง ;)


18

น่าเสียดายนี่ใช้ได้กับเลขจำนวนเต็มเท่านั้น

เนื่องจากไม่ได้รับอนุญาตนอกจากนี้ให้สร้างโอเปอเรเตอร์ที่เพิ่มขึ้นก่อน:

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

ต่อไปเราต้องจัดการกับสัญญาณ ก่อนอื่นให้หาเครื่องหมายบิต:

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

จากนั้นนำสัญลักษณ์และขนาดของแต่ละข้อโต้แย้ง ในการลบล้างจำนวนในส่วนเติมเต็มของสองให้กลับด้านบิตทั้งหมดและเพิ่ม

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

ในการคูณจำนวนเต็มบวกสองตัวเราสามารถใช้ความหมายทางเรขาคณิตของการคูณ:

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

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

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

โทรลล์:

  • ไม่ได้รับอนุญาตนอกจากนี้ แต่a++จริงๆแล้วนับรวมเป็นอะไรเพิ่มเติม ฉันพนันว่าครูตั้งใจจะอนุญาต
  • พึ่งพาส่วนประกอบสองอย่าง แต่นั่นเป็นพฤติกรรมที่กำหนดโดยการนำไปปฏิบัติและแพลตฟอร์มเป้าหมายไม่ได้ระบุไว้
  • ในทำนองเดียวกันสมมติว่าการลบและการหารจะไม่ได้รับอนุญาตเช่นกัน
  • << เป็นการคูณด้วยกำลังสองดังนั้นในทางเทคนิคควรไม่อนุญาต
  • แผนภาพที่ไม่จำเป็นเป็นสิ่งที่ไม่จำเป็น นอกจากนี้มันอาจถูกย้ายเพื่อบันทึกหนึ่งบรรทัด
  • การขยับซ้ำของ-1ไม่ใช่วิธีที่ดีที่สุดในการค้นหาบิต แม้ว่าจะไม่มีค่าคงที่ในตัวคุณก็สามารถทำการเลื่อนตรรกะทางขวาของ -1 จากนั้นสลับบิตทั้งหมด
  • XOR -1 ไม่ใช่วิธีที่ดีที่สุดในการสลับบิตทั้งหมด
  • ปริศนาทั้งหมดที่มีการแสดงสัญลักษณ์ขนาดไม่จำเป็น เพียงส่งไปยังเลขคณิตที่ไม่ได้ลงชื่อและโมดูลาร์แบบแยกส่วนจะทำส่วนที่เหลือ
  • เนื่องจากค่าสัมบูรณ์ของMIN_INT(AKA signBit) เป็นลบการแบ่งนี้สำหรับค่านั้น โชคดีที่มันยังใช้งานได้ในครึ่งกรณีเพราะMIN_INT * [even number] ควรเป็นศูนย์นอกจากนี้ยังplusOneแบ่งสำหรับ-1ทำให้ลูปไม่มีที่สิ้นสุดทุกเวลาล้นผล plusOneทำงานได้ดีสำหรับค่าใด ๆ ขอโทษสำหรับความสับสน.

+1 สำหรับการหมุนรอบรหัสที่แท้จริง: ดูเหมือนว่าจะทำงานได้ดี แต่มีโอกาสมากที่จะระเบิดใน OP และ s / เขาจะไม่ทราบว่าทำไม
Kevin

1
เป็นไปได้ที่จะเพิ่มโดยไม่มีตัวดำเนินการเพิ่มเติมใด ๆ เพียงแค่ใช้ shift, XOR และ AND ++ ทั้งหมดนี้ทำให้ฉันปวดหัว - บิตเดียวที่พกติดตัวคือ (x ^ y) | ((x & y) << 1) (modulo ข้อผิดพลาดใด ๆ ที่เกิดจากการพิมพ์ในกล่องข้อความเล็ก ๆ เส็งเคร็งนี้)
Julie ใน Austin

@JulieinAustin อ๋อ อัลกอรึทึมนั้นไม่มีประสิทธิภาพมากกว่าที่ควรจะเป็น ฉันควรแก้ไขรายการโทรลล์หรือไม่ :-)
John Dvorak

1
@JulieinAustin (x ^ y) | ((x & y) << 1)ไม่ทำงานไม่มากก็จะไม่เผยแพร่การดำเนินการเมื่อ X หรือ Y และดำเนินการมีทั้งจริงในตำแหน่งเดียวกัน :)
ฮอบส์

@ วิธีแก้ปัญหา hobbs: แทนที่จะใช้ ORing ให้เพิ่มพวกมันซ้ำ ๆ ถ้าการพกพาไม่ใช่ศูนย์
John Dvorak

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

ไม่มีคำตอบอื่นใดที่เป็นไปได้ในทางทฤษฎี ในฐานะที่เป็นความคิดเห็นแรกในคำถามพูดว่า:

โปรดเจาะจงมากขึ้นเกี่ยวกับ "หมายเลข"

เราจำเป็นต้องนิยามการคูณและตัวเลขก่อนที่จะตอบได้ เมื่อเราทำแล้วปัญหาจะกลายเป็นเรื่องเล็กน้อย

วิธีที่นิยมมากที่สุดในการทำเช่นนี้ในการเริ่มต้นตรรกะทางคณิตศาสตร์คือการสร้างเลข von Neumann ด้านบนของทฤษฎีเซต 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;
    }
}

หากคุณต้องการขยายจำนวนเต็ม rationals reals surreals และอื่น ๆ คุณสามารถ - ด้วยความแม่นยำที่ไม่มีที่สิ้นสุด (สมมติว่าคุณมีหน่วยความจำและ CPU ที่ไม่มีที่สิ้นสุด) แต่ดังที่ Kroenecker พูดอย่างมีชื่อเสียงพระเจ้าทรงสร้างตัวเลขตามธรรมชาติ ทั้งหมดเป็นงานของมนุษย์จริง ๆ แล้วทำไมต้องรำคาญ?


1
ว้าว. คุณช้ากว่าฉัน
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 );
}

หากคุณคิดว่าการแฮ็ค [b] นั้นเป็นการโกง (เนื่องจากเป็นการเพิ่ม) การทำงานนี้แทน แต่การค้นหาตารางเกี่ยวข้องกับตัวชี้เพิ่มเช่นกัน

ดูhttp://en.wikipedia.org/wiki/IBM_1620 - conputer ที่เพิ่มตารางการค้นหาได้จริง ...

มีบางอย่างที่น่าพอใจเกี่ยวกับการใช้กลไกตารางเพื่อ 'เร่งความเร็ว' การดำเนินการที่สามารถทำได้จริงในคำสั่งเดียว

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))
Julie ใน Austin

@JulieinAustin ฉันพูดถึงเรื่องนั้น การค้นหาตารางสามารถทำได้โดยไม่ต้องเพิ่มโดยการรวมฟิลด์ (ดังที่แสดงใน IBM 1620 ดูลิงค์) แต่มันยุ่งเหยิงที่จะตั้งค่าใน C - สำหรับสิ่งหนึ่งที่คุณจำเป็นต้องจัดตารางไปยังที่อยู่ที่เหมาะสมเพื่อให้ดัชนีสามารถ หรือเพียงแค่เข้ามา
greggo

8

ฉันไม่แน่ใจว่าสิ่งใดที่ถือเป็นการ "โกง" ในโพสต์ "code troll" เหล่านี้ แต่นี่จะคูณจำนวนเต็ม 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 นำการประเมินผลไปได้ดี ฉันชอบรหัสของคุณ :)
yo '12

@ BЈовић: ความผิดของฉันฉันคิดว่าการหมุนรอบไม่ได้เกี่ยวกับการทำความเข้าใจ เปลี่ยนชื่อและเพิ่มความคิดเห็นแล้ว
Matthias

ขอโทษ ฉันเข้าใจผิดว่าแท็กนั้นคืออะไรและอะไรที่เกี่ยวกับคำถามจริง ๆ คุณควรเปลี่ยนกลับ
Bћовић

@ Matias ในกรณีนี้มันมีประโยชน์ที่จะเข้าใจว่ามันทำงานอย่างไรเพื่อที่เราจะได้ชื่นชมว่าการดำเนินการแบบมาบรรจบกันแบบบิดเบี้ยวเป็นอย่างไร ในสถานการณ์รหัสโทรลล์ที่เกิดขึ้นจริงความคิดเห็นที่อาจจะ redacted :-)
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 ของ Paul R ดังนั้นคุณสามารถรัน 32x เร็วแทน 8x ได้ แม้ว่าจริง ๆ แล้วคุณต้องการให้ GPU มีส่วนร่วมเช่นเดียวกับแกนกลาง จากนั้นมันก็จะลุกโชนจริงๆ :)
abarnert

2
อาจใช้ OpenMPI เพื่อรันบนเครื่องสองสามตัวพร้อมกัน
millinon

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

นี่คือความเข้าใจที่ดีที่สุดของฉันเหมือนกับว่า CPU อาจทำการคูณจำนวนเต็มจริง ๆ อันดับแรกเราตรวจสอบให้แน่ใจว่าอย่างน้อยหนึ่งข้อโต้แย้ง ( a) เป็นค่าบวกโดยการพลิกเครื่องหมายทั้งคู่ถ้าaเป็นลบ (และไม่ฉันปฏิเสธที่จะนับการปฏิเสธว่าเป็นการบวกหรือการคูณ) จากนั้นwhile (a)วงเพิ่มสำเนาขยับตัวของผลการสำหรับทุกบิตตั้งอยู่ในb aการdoวนซ้ำนั้นr += xใช้และ, xor, และขยับในสิ่งที่เห็นได้ชัดว่าเป็นชุดของโปรแกรมเสริมครึ่งหนึ่งโดยมีบิตพกพาที่ป้อนกลับเข้าไปxจนกระทั่งไม่มีอีกต่อไป (CPU จริงจะใช้โปรแกรมเสริมเต็มซึ่งมีประสิทธิภาพมากขึ้น ไม่มีโอเปอเรเตอร์ที่เราต้องการสำหรับสิ่งนี้เว้นแต่คุณจะนับ+โอเปอเรเตอร์)


4
ผู้ถามไม่ได้หมุนรอบ คุณควรจะหมุนรอบ
John Dvorak

2
มันเป็นโทรลล์ที่มองไม่เห็น! ความล้มเหลวที่เป็นความลับอยู่บน == INT_MIN
Jander

1
@ Jander อืม ใช่นั่นเป็นสิ่งที่ดี ฉันคาดเดา (ในระบบประกอบสองธรรมดา) ผลลัพธ์ของการปฏิเสธ a นั้นยังคงเป็นค่าลบและwhile(a)ลูปจะไม่สิ้นสุด
ฮอบส์

@ ฮอบส์ใช่นั่นฟังดูเหมาะสมกับฉัน มิฉะนั้นคำตอบที่สวยมาก
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
สิ่งนี้จะล้มเหลวอย่างน่ากลัวหากผลลัพธ์ล้นออกมา ดี! ฉันคิดว่าคุณไม่ควรพิมพ์ด้วยซ้ำ
John Dvorak

2
ล้มเหลวสำหรับ a = 2, b = 2, c = 5
BЈовић

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

2
โปรดทราบว่านี่เป็นความพยายามที่จะทำสิ่งเดียวกันกับผู้สืบทอดของ Deep Thought แต่สำหรับจักรวาลที่เป็นไปได้ทั้งหมดแทนที่จะเป็นเพียงคำตอบที่ 42 ซึ่งน่าประทับใจมากหากไม่ใช่ข้อผิดพลาด และการขาดการจัดการข้อผิดพลาดในกรณีของ Vogons
abarnert

1
ต้องการหลายเธรด คุณรู้เพื่อให้มีประสิทธิภาพ
greggo

6

โยนสิ่งนี้ลงในส่วนผสม:

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

int mul(int a, int b)
{
        asm ("mul %2"
            : "=a" (a)
            : "%0" (a), "r" (b) : "cc"
        );
        return a;
}

int main(int argc, char *argv[])
{
        int a, b;

        a = (argc > 1) ? atoi(argv[1]) : 0;
        b = (argc > 2) ? atoi(argv[2]) : 0;

        return printf("%d x %d = %d\n", a, b, mul(a, b)) < 1;
}

จากหน้าข้อมูล

- แนะนำสิ่งที่ไม่สามารถยอมรับได้หรือไม่มีเหตุผลอย่างมากในรหัสที่ไม่สามารถลบออกได้โดยไม่ทิ้งทุกอย่างออกไป

- […] ความตั้งใจคือทำการบ้านในภาษาที่ OP ขี้เกียจอาจคิดว่ายอมรับได้ แต่ก็ยังทำให้เขาหงุดหงิด


2
msgstr "โดยไม่ต้องใช้ตัวเพิ่มการคูณและตัวเสริม". การโค้งงอของกฎดี - สิ่งนี้จะไร้ประโยชน์อย่างแน่นอนต่อผู้ถาม :-)
John Dvorak

2
นี่ไม่ใช่โซลูชัน C จริงๆ นอกจากนี้มันไม่สามารถคอมไพล์บน ARM9 ของฉัน
abarnert

1
@abarnert: ไม่สามารถจดจำคำสั่งของคุณเป็นอาร์กิวเมนต์ที่เกี่ยวข้องได้
Runium

@Sukminder: คำถามคือ "เป็นไปได้ไหมที่จะเขียนโปรแกรม C ... ?" ชุดประกอบแบบ Inline ไม่ใช่ C ความจริงที่ว่าคอมไพเลอร์ C บางตัวสามารถทำชุดประกอบแบบอินไลน์ได้ไม่เปลี่ยนแปลงนอกจากความจริงที่ว่าคอมไพเลอร์ C บางตัวสามารถทำ C ++ หรือ ObjC ทำให้ C ++ หรือ ObjC นับเป็นรหัส C
abarnert

2
@abarnert: มันเป็นรหัสที่ฝังตัวที่ใช้กันอย่างแพร่หลายในโปรแกรม C แม้ว่ามันจะเป็นข้ามสายพันธุ์หนึ่งที่สามารถยืนยันว่ามันเป็นโปรแกรม C มันเป็นไปได้ที่ 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");
}

แต่แน่นอนว่าเป็นการโกง เห็นได้ชัดว่าเขาต้องการที่จะสามารถ _supply) ตัวเลขสองตัวใช่มั้ย

void multiply(int a, int b) {
    int answer = 42;
    if (answer / b != a || answer % b) {
        printf("The answer is 42, so that's the wrong question.\n");
    } else {
        printf("The answer is 42, but that's probably not the right question anyway.\n");
    }
}

ทำไมฉันไม่เห็นด้วยเลย!
leewz

4

ไม่มีเลขคณิตเหมือนตัวชี้เลขคณิต:

int f(int a, int b) {
        char x[1][b];
        return x[a] - x[0];
}

int
main(int ac, char **av) {
        printf("%d\n", f(atoi(av[1]),atoi(av[2])));
        return 0;
}

ฟังก์ชันfใช้การคูณ mainเพียงแค่เรียกมันว่ามีสองข้อโต้แย้ง
ทำงานสำหรับตัวเลขลบเช่นกัน


ติดลบaใช่ลบbฉันไม่คิดอย่างนั้น แต่นั่นสามารถแก้ไขได้ในหลายวิธี ที่ง่ายที่สุดคือ sign_a ^ = sign_b, sign_b = 0.
MSalters

@MSalters ทดสอบและใช้งานได้กับชุดค่าสัญญาณทั้งหมด (พร้อม Linux / gcc)
ugoren

3

C #

ฉันคิดว่าไม่อนุญาตให้ใช้การลบและการปฏิเสธ ... อย่างไรก็ตาม:

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

1
นี่เป็นวิธีการแก้ปัญหาที่ฉันคิด ... แต่มางานปาร์ตี้สายฉันรู้ว่ามันเป็นเรื่องของการเลื่อนลงจนกว่าฉันจะพบว่ามีคนเขียนไว้แล้ว ยังคุณจะได้รับ - (- 1) จากฉัน
Floris

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 ขนานโดยไม่ต้อง*หรือ+ถ้าจำเป็น


ฉันไม่คิดว่านี่เป็นการหมุนรอบ ...
John Dvorak

จริงเหรอ ฉันคิดว่าการใช้ SIMD ที่ไม่มีจุดหมายไม่มีค่าใช้จ่ายและเฉพาะสถาปัตยกรรมจะมีคุณสมบัติเพียงพอสำหรับการใช้รหัสหลอก?
Paul R

อืม ... จริง ไม่ทราบว่านี่เป็นสถาปัตยกรรมเฉพาะ
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;
}
  • ทำงานเพื่อผลคูณเท่านั้น <1 000 000
  • จนถึงตอนนี้ไม่สามารถกำจัดตัวดำเนินการ - อาจปรับปรุงที่นี่
  • ใช้ตัวระบุรูปแบบ% n ใน printf เพื่อนับจำนวนตัวอักษรที่พิมพ์ (ฉันโพสต์สิ่งนี้เพื่อเตือนว่ามีอยู่% n ใน C แทนที่จะเป็น% n อาจเป็น strlen เป็นต้น)
  • พิมพ์อักขระ * b ของ '*'

กำลังรอการแก้ปัญหา 'การจำลองเครื่องทัวริง'
Greggo

1
ในขณะที่strlen(cg) != aเป็นวิธีการหมุนรอบมากในการกำจัด--(ทำให้ O (N * N))
MSalters

3

อาจเร็วเกินไป :-(

   unsigned int add(unsigned int a, unsigned int b)
    {
        unsigned int carry;

        for (; b != 0; b = carry << 1) {
            carry = a & b;
            a ^= b;
        }
        return a;
    }

    unsigned int mul(unsigned int a, unsigned int b)
    {
        unsigned int prod = 0;

        for (; b != 0;  a <<= 1, b >>= 1) {
            if (b & 1)
                prod = add(prod, a);
        }
        return prod;
    }

1
Ungh นี่ไม่ใช่การหมุนรอบ นี่เป็นวิธีที่สมเหตุสมผลในการทำเช่นนี้
John Dvorak

1
มันเร่งรีบเพราะมันเร็วเกินไป :-)
Timtech

3

รุ่น Haskell นี้ใช้งานได้เฉพาะกับจำนวนเต็มไม่ใช่ค่าลบ แต่มันจะทำการคูณวิธีที่เด็กเรียนรู้ครั้งแรก คือ 3x4 คือ 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!
Paul R

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

อาจมีร่องรอยของ 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;
}

ทดสอบการทำงาน:

$ ./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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.