* 및 + 연산자를 사용하지 않고 곱셈을위한 C 프로그램을 작성하는 방법은 무엇입니까?


68

곱셈 및 덧셈 연산자를 사용하지 않고 두 숫자를 곱하는 C 프로그램을 작성할 수 있습니까?

Stack Overflow 에서 이것을 찾았습니다 . 이 가난한 프로그래머가 자신의 문제를 해결하도록 도와주십시오. 같은과 답변을 제공하지 마십시오 c = a/(1/((float)b))정확히 동일하다, c = a*b. (그리고 이미 답변으로 제공됩니다.)

2014 년 1 월 19 일에 가장 많이 찬성 한 답변이 승리합니다.

참고 : 이것은 질문입니다. 질문 및 / 또는 답변을 진지하게 받아들이지 마십시오. 자세한 정보는 코드 스크롤링에 있습니다.


2
@PaulR은 당신의 환상을 사용합니다
John Dvorak

26
Code-golf.SE는 StackOverflow에서 본 질문을 조롱 할 수있는 장소가되어서는 안됩니다.
Gareth

17
@Gareth, 확실합니까? 이것의 첫 번째 줄은 이것이 적절할 수 있음을 시사합니다.
Darren Stone

5
잠을 기반으로하는 알고리즘을 쓰는 사람을 기다리고 미안
kb_sou

21
이 질문은 들리는 것만 큼 우스운 것이 아닙니다. 실제 컴퓨터 하드웨어 (트랜지스터)에는 곱하기 및 추가 작업이 없습니다. NOT, AND, OR, XOR과 같은 기본 논리 작업이 있습니다. 이 질문에 대답하는 방법을 알아 내면 컴퓨터가 실제로 논리 게이트 수준에서 어떻게 작동하는지에 대한 훌륭한 통찰력을 얻을 수 있습니다.
Gabe

답변:


147

항상 재귀를 사용하십시오

회개가 올바른 방법입니다!

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

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

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

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

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

8
내가 할 수 있다면 나는 +3을 줄 것이다 : 궁극의 재귀를위한 것 ??::, 괄호가없는 것, 규칙을 조정하지 않고 문제를 해결하기위한 것;)
yo '

10
피카소가 프로그래머라면 ...
R Hughes

4
@SargeBorsch 그러나 그 재미 어디에 있었습니까?
Oberon

3
@HC_이 inc함수는 인수가 가장 낮은 비트인지 확인하기 위해 인수를 테스트합니다 1. 그렇다면 인수의 나머지 상위 비트에서 자체를 호출하고로 설정되어있는 동일한 하위 비트로 결과를 반환 0하지만 그렇지 않은 경우 (즉 가장 낮은 비트는 0)는 해당 비트 0를 a로 대체 1하고 결과를 반환합니다 . 이 과정은 손으로, 이진수를 이진수로 추가하는 경우 수행하는 작업과 매우 유사합니다.
JAB

2
증가 함수가 -1의 무한 루프로 들어 가지 않습니까? (0xFFFF) 이데온은 (-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])작동합니다 (A <= 0 또는 B <= 0 또는 A * B 오버플로가 아닌 경우 '나쁜 유형'오류가 발생합니다)
greggo

3
@DavidRicherby-코드를 단순화 main(){return sizeof(char[A][B]);}하고 다음을 사용하여 컴파일 할 수 있습니다.cc -DA=6 -DB=7 a.c; ./a.out; echo $?
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
내 투표권이 있습니다. 몬테 카를로는 NASA가 산술에 사용하는 것을 들었습니다. 그러나 ++연산자 의 두 인스턴스가 없으면 이것을보고 싶습니다 .
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

숫자의 크기를 지정하지 않았으므로 2 개의 1 비트 숫자를 의미한다고 가정합니다.

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

최대한 효율적인 구현을 원한다면 다음과 같은 작은 구현을 사용하십시오.

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

유형이 암시 적 정수이지만 여전히 비트 만 허용한다는 점에 유의하십시오. 코드는 덜 걸리므로 더 효율적입니다. (그렇습니다. 컴파일합니다.)


8
좋은. 의문의 잘못된 해석 :-)
John Dvorak

6
이를에 최적화 할 수 있습니다 return a && b;. 짧아서 빠릅니다.
Ry-

1
@ minitech : 코드를 약간 악화시키기 위해 그것에 반대하기로 결정했습니다. 더 나아가고 싶을 때 나는 그것을 만들 것이다 return a&b;.
Cel Skeggs

1
C는 및 #include<stdbool.h>을 정의해야합니다 . truefalse
leewz

1
그래, #include<stdbool.h>단 세 것 같다 #define(당신이 자신을 할 수의 true, false, bool, 및 플래그가 활성화되어 있다고 표시). 다른 답변 중 하나에서 트릭을 가져 와서 int"짧은"버전에 암시 적 으로 사용할 수도 있습니다 .
leewz

31

간단한 쉘 스크립트는 다음과 같습니다.

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

업데이트 : 물론 C에서 수행하려면에 래핑하십시오 exec("bash", "-c", ...). (감사합니다, 아멜리아 BR)


41
어느 것이 더 끔찍한 지 결정할 수 없습니다. 검색 엔진에 계산을 아웃소싱하거나 선택한 검색 엔진이 Bing이라는 것을 나타냅니다. 불행히도, 나는 이것이 C에서 무언가를 필요로했던 우리의 행복없는 OP를 위해 작동하지 않을 것이라고 생각합니다.
AmeliaBR

5
감독 해줘서 고마워 참고로, Bing을 사용하고 있습니다. Google이 이와 같은 요청을보다 복잡하게하기 때문에 헤더를 추가해야 Google에 요청이 브라우저에서 온 것임을 확신시킬 수 있습니다.
Vroo

2
@abarnert 흠 ... 빙은 "시간"을 이해합니까? 그러나 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;

그런 다음 각 인수의 부호와 크기를 취하십시오. 2의 보수에서 숫자를 부정하려면 모든 비트를 반전시키고 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++실제로는 추가로 간주됩니까? 나는 선생님이 그것을 허락하려고 내기했다.
  • 2의 보수에 의존하지만 이는 구현 정의 동작이며 대상 플랫폼이 지정되지 않았습니다.
  • 마찬가지로 뺄셈과 나눗셈도 허용되지 않는다고 가정합니다.
  • << 실제로 2의 거듭 제곱으로 곱하기 때문에 기술적으로 허용되지 않아야합니다.
  • 불필요한 다이어그램은 불필요합니다. 또한 한 줄을 저장하기 위해 바꿨을 수도 있습니다.
  • 반복되는 시프트 -1는 부호 비트를 찾는 가장 좋은 방법이 아닙니다. 내장 상수가 없더라도 -1의 논리 시프트를 수행 한 다음 모든 비트를 반전시킬 수 있습니다.
  • XOR -1은 모든 비트를 반전시키는 가장 좋은 방법은 아닙니다.
  • 부호 크기 표현이 포함 된 전체 카라 데는 필요하지 않습니다. 서명되지 않은 모듈 형 산술로 캐스팅하면 나머지가 수행됩니다.
  • MIN_INT(AKA signBit) 의 절대 값 이 음수이므로 해당 값에 대해 중단됩니다. 운 좋게도 여전히 0 MIN_INT * [even number] 이어야 하기 때문에 절반의 경우에도 작동 합니다.또한, plusOne대한 나누기 -1무한 언제 루프 결과의 오버 플로우를 일으키는. plusOne어떤 가치에도 잘 작동합니다. 혼란을 드려 죄송합니다.

실제 코드 트롤의 경우 +1 : 작동해야하는 것처럼 보이지만 OP에 영향을 줄 가능성이 높으며 그 이유를 모를 것입니다.
Kevin

1
shift, XOR 및 AND를 사용하여 추가 연산자없이 추가를 수행 할 수 있습니다. 이 모든 ++로 인해 머리가 아프다. 캐리가있는 단일 비트 ADD는 (x ^ y) | ((x & y) << 1) (이 엉뚱한 작은 텍스트 상자에 입력하여 발생하는 모든 오류.)
Julie in Austin in

트윗 담아 가기 알고리즘은 필요한 것보다 훨씬 비효율적입니다. 트롤 목록을 수정해야합니까? :-)
John Dvorak

1
@Juliein 오스틴 (x ^ y) | ((x & y) << 1)은 제대로 작동하지 않습니다. x 또는 y와 캐리가 모두 같은 위치에있을 때 캐리를 전파하지 않습니다 :)
hobbs

@ hobbs 솔루션 : ORing 대신 carry가 0이 아닌 경우 재귀 적으로 추가하십시오.
John Dvorak

14

부동 소수점 숫자에서도 작동합니다.

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

11

누구나 파이썬이 C보다 사용하기 쉽다는 것을 알고 있습니다. 그리고 파이썬에는 연산자를 사용할 수없는 경우를 위해 모든 연산자에 해당하는 함수가 있습니다. 정확히 우리의 문제 정의는 무엇입니까? 그래서:

#include <Python.h>

void multiply(int a, int b) {
    PyObject *operator_name, *operator, *mul, *pa, *pb, *args, *result;
    int result;

    operator_name = PyString_FromString("operator");
    operator = PyImport_Import(operator_name);
    Py_DECREF(operator_name);
    mul = PyObject_GetAttrString(operator, "__mul__");
    pa = PyLong_FromLong((long)a);
    pb = PyLong_FromLong((long)b);
    args = PyTuple_New(2);
    PyTuple_SetItem(args, 0, pa);
    PyTuple_SetItem(args, 1, pb);
    presult = PyObject_CallObject(mul, args);
    Py_DECREF(args);
    Py_DECREF(mul);
    Py_DECREF(operator);
    result = (int)PyLong_AsLong(presult);
    Py_DECREF(presult);
    return result;
}

int main(int argc, char *argv[]) {
    int c;
    Py_Initialize();
    c = multiply(2, 3);
    printf("2 * 3 = %d\n", c);
    Py_Finalize();
}

10

다른 대답은 이론적으로 건전하지 않습니다. 질문에 대한 첫 의견은 다음과 같이 말합니다.

"숫자"에 대해 더 구체적으로 기재하십시오

답이 가능해지기 전에 곱셈과 숫자를 정의해야합니다. 일단 그렇게하면 문제는 사소 해집니다.

수학적 논리를 시작할 때 가장 널리 사용되는 방법 은 ZF 집합 이론 위에 폰 노이만 서수만들고 Peano 공리 를 사용하는 것 입니다.

다른 세트를 포함 할 수있는 세트 유형이 있다고 가정하면 자연스럽게 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;
    }
}

이것을 정수, 이성, 실수, 초현실 등으로 확장하려면 무한 정밀도 (무한 메모리와 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 );
}

a [b] 핵이 부정 행위라고 생각한다면 (정말 추가이기 때문에) 대신 작동합니다. 그러나 테이블 조회에는 포인터 추가도 포함됩니다.

찾아보기 테이블을 사용하여 실제로 추가 한 컴퓨터 인 http://en.wikipedia.org/wiki/IBM_1620을 참조하십시오 .

테이블 메커니즘을 사용하여 실제로 하나의 명령으로 수행 할 수있는 작업의 속도를 높이는 것에 만족하는 것.

static unsigned sumtab[17][16]= {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15},
{ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16},
{ 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17},
{ 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18},
{ 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19},
{ 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20},
{ 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21},
{ 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22},
{ 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23},
{ 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24},
{10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25},
{11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26},
{12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27},
{13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28},
{14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29},
{15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30},
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}
};

unsigned add( unsigned a, unsigned b )
{
   static const int add4hack[8] =  {4,8,12,16,20,24,28,32};
   unsigned carry = 0;
   unsigned (*sumtab0)[16] = &sumtab[0];
   unsigned (*sumtab1)[16] = &sumtab[1];
   unsigned result = 0;
   int nshift = 0;
   while( (a|b) != 0 ){
      unsigned psum = (carry?sumtab1:sumtab0)[ a & 0xF ][ b & 0xF ];
      result = result | ((psum & 0xF)<<nshift);
      carry = psum >> 4;
      a = a >> 4
      b = b >> 4;
      nshift= add4hack[nshift>>2];  // add 4 to nshift.
   }
   return result;
}

아차,이 *(가 곱셈이 아니다 있지만) 문자
사지 (Sarge) 러시아 식 수프의 일종

어, 테이블 조회는 더하기를 사용합니다-(a [i])는 (* (a + i))와 같습니다.
Julie in Austin

@JulieinAustin 나는 그것을 언급했다. 필드를 병합하여 테이블 조회를 추가하지 않고 수행 할 수 있지만 (IBM 1620에 설명 된대로 링크 참조) C로 설정하는 것은 지저분합니다. 그냥 또는 안으로.
greggo

8

이 "코드 트롤"게시물에서 "속임수"가 무엇인지 확실하지 않지만 런타임시 표준 라이브러리 (C99)를 사용 *하거나 +사용 하지 않는 2 개의 임의의 정수를 곱 합니다.

#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 '

@ BЈовић : 제 잘못은, 트롤링이 이해에 관한 것이 아니라고 생각했습니다. 이름 변경 및 설명 추가
Matthias

죄송합니다. 그 태그가 무엇인지, Q가 실제로 무엇인지 이해하지 못했습니다. 당신은 그것을 되돌려 야합니다
BЈовић

이 경우 @Matthias는 그것이 어떻게 작동하는지 이해하는 것이 수렴 ​​수행이 어떻게 비틀 렸는지 이해할 수 있도록 도와줍니다. 실제 코드 트롤 상황에서 주석을
수정할

비트 리버스 숫자를 추가하고 (높은 값을 가진 소품으로) 'bitrev'명령이없는 경우 이것은 아마도 합리적인 접근 방식 일 것입니다 (c로 변경 한 후) > = 1 = 물론)
greggo

7

여기에는 많은 좋은 대답이 있지만, 많은 사람들이 현대 컴퓨터가 실제로 강력하다는 사실을 이용하지는 않습니다. 대부분의 CPU에는 여러 개의 처리 장치가 있으므로 왜 하나만 사용합니까? 이를 활용하여 뛰어난 성능 결과를 얻을 수 있습니다.

#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가 for 루프의 각 부분을 다른 실행 단위로 나누도록하므로 병렬로 곱합니다!

-fopenmp컴파일러에게 OpenMP를 사용하도록 지시하려면 플래그 를 사용해야합니다 .


트롤 부품 :

  1. 병렬 프로그래밍 사용에 대한 오해.
  2. 음수 (또는 큰 숫자)에서는 작동하지 않습니다.
  3. 실제로 for루프 의 일부를 나누지 않습니다 . 각 스레드는 루프를 실행합니다.
  4. 성가신 변수 이름과 변수 재사용.
  5. 미묘한 경쟁 조건이 있습니다 answer--. 대부분의 경우 표시되지 않지만 간혹 부정확 한 결과가 발생할 수 있습니다.

2
이것을 Paul R의 SIMD 답변과 결합하여 8x 대신 32x를 빠르게 실행할 수 있습니까? 실제로는 코어뿐만 아니라 GPU도 포함 시키려고합니다. 그때 정말 타오를 것입니다. :)
abarnert

2
OpenMPI를 사용하여 몇 대의 컴퓨터에서 병렬로 실행할 수도 있습니다.
millinon 2012 년

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 += xand, xor, 그리고 반 가산기의 명확한 이동을 구현 하며 캐리 비트 x는 더 이상 없을 때까지 다시 전송됩니다 (실제 CPU는 완전 가산기를 사용하지만 더 효율적이지만 C는 그렇지 않습니다) 당신이 +연산자 를 세지 않는 한 우리에게 필요한 연산자가 없습니다 .


4
그 asker는 트롤을하지 않았다. 당신 은 트롤해야합니다.
John Dvorak

2
스텔스 트롤입니다! 비밀 실패는 a == INT_MIN입니다.
Jander

1
@Jander 흠. 그래, 좋은 일이야 나는 (보통 2의 보수 시스템에서) a를 부정 한 결과가 여전히 부정적이며 while(a)루프가 끝나지 않는다고 추측 합니다.
hobbs

@ hobbs 그래, 나에게 맞는 소리. 그렇지 않으면 매우 예쁜 답변입니다.
Jander

6
 int bogomul(int A, int B)
{
    int C = 0;
    while(C/A != B)
    {

        print("Answer isn't: %d", C);
        C = rand();

    }
    return C;
}

1
결과가 넘치면 끔찍하게 실패합니다. 좋은! 그래도 인쇄하지 않아야한다고 생각합니다.
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에 대한 답을 전혀 쓸모 없게 만듭니다.

– […] 의도는 게으른 OP가 수용할만한 언어로 숙제를하는 것이지만 여전히 그를 좌절시키는 것입니다.


2
"곱셈과 덧셈 연산자를 사용하지 않고". 규칙의 좋은 굽힘-이것은 asker에게 절대적으로 쓸모가 없습니다 :-)
John Dvorak

2
이것은 실제로 C 솔루션이 아닙니다. 또한 ARM9에서 컴파일하지 못했습니다.
abarnert

1
@abarnert : 진술을 관련 인수로 인식하지 못했습니다.
Runium

@Sukminder : 문제는 "C 프로그램을 작성할 수 있습니까…?"입니다. 인라인 어셈블리는 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

씨#

뺄셈과 부정은 허용되지 않는다고 생각합니다 ... 어쨌든 :

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

1
이것은 내가 생각한 해결책입니다 ... 그러나 파티에 늦게 와서 누군가가 이미 그것을 작성했다는 것을 알 때까지 아래로 스크롤하는 문제라는 것을 알았습니다. 그래도 나에게서-(-1)을 얻습니다.
Floris

3

SSE 내장 함수가있는 C (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에 대해서만 작동
  • 지금까지 제거 할 수 없습니다-연산자, 아마도 여기에서 향상시킬 수 있습니다
  • printf에서 % n 형식 지정자를 사용하여 인쇄 된 문자 수를 계산합니다. (이를 게시하면 주로 % n 대신 C에서 % n이 있음을 상기시키기 위해 게시됩니다.)
  • '*'의 a * b 문자를 인쇄합니다

이제 'turing machine emulation'솔루션을 기다리고 있습니다.
greggo

1
while (을 O (N * N)로 만드는) strlen(cg) != a을 제거하는 매우 트롤링 방법 --입니다.
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
안돼 이것은 트롤링이 아닙니다. 이것은 전적으로 합리적인 방법입니다.
John Dvorak

1
너무 빠르기 때문에
트롤입니다

3

이 Haskell 버전은 음이 아닌 정수로만 작동하지만 아이들이 처음 배우는 방식으로 곱셈을합니다. 즉, 3x4는 4 가지 그룹으로 구성됩니다. 이 경우, 계수되는 "사물"은 막대의 노치 ( '|')입니다.

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

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

날씨가 정확하고 컴파일러에서 정의되지 않은 넌센스를 지원하는 경우 C99에서 작동 할 수 있습니다.


3

이후 영업 이익은 C를 요청하지 않았다 , 여기 (오라클) 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
나의 하나님, 그것은 가득합니다 *!
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.