* Ve + operatörlerini kullanmadan çarpma için bir C programı nasıl yazılır?


68

Çarpma ve toplama operatörlerini kullanmadan iki sayıyı çarpan bir C programı yazmak mümkün mü?

Bunu Stack Overflow'ta buldum . Lütfen bu kötü programcıya sorunuyla ilgili yardım edin. Lütfen c = a/(1/((float)b))aynen olduğu gibi cevap vermeyin c = a*b. (Ve zaten bir cevap olarak verilmiştir.)

19 Ocak 2014'teki en çok oy alanların yanıtı kazandı.

Not: Bu bir sorusudur. Lütfen soruyu ve / veya cevapları ciddiye almayın. Daha fazla bilgi kod trolling'de .


2
@PaulR fantezinizi kullanın
John Dvorak

26
Code-golf.SE, StackOverflow'ta gördüğünüz sorularla alay edebileceğiniz bir yer olmamalıdır.
Gareth

17
@ Gareth, emin misin? Bunun ilk satırı bunun oldukça uygun olabileceğini gösteriyor.
Darren Stone

5
Birinin uykuya dayalı bir algoritma yazmasını bekliyorum
kb_sou

21
Bu soru göründüğü kadar saçma değil. Gerçek bilgisayar donanımında (transistörler) çarpma ve işlem ekleme - bunlar NOT, AND, VEYA, XOR gibi temel mantık işlemlerine sahiptir. Bu sorunun nasıl cevaplanacağını bulmak, bir bilgisayarın mantık kapıları düzeyinde gerçekten nasıl çalıştığı hakkında size mükemmel bir fikir verebilir.
Gabe

Yanıtlar:


147

Her zaman özyineleme kullan

Recusion doğru yoldur!

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
Yapabilseydim +3 verirdim: biri nihai özyineleme için, biri ??::parantez kullanmadan, biri kuralları kurallara uymaya çalışmadan çözmek için;)
yo '

10
Picasso bir programcıysa ...
R Hughes,

4
@SargeBorsch Ama eğlenceli nereye olmak o ?
Oberon

3
@HC_ incİşlev, en düşük bit olup olmadığını görmek için bağımsız değişkenini sınar 1; eğer öyleyse, kendini argümanın kalan üst bitlerinde çağırır ve sonucu, ayarlandığı gibi aynı düşük bit ile döndürür, 0eğer değilse (yani en düşük bit 0), 0a ile değiştirir 1ve sonucu döndürür. . Süreç, değerleri el ile, ikili rakamla ikili rakamla eklerseniz ne yapacağınıza benzer.
JAB

2
Artış fonksiyonu -1 için sonsuz bir döngüye girmiyor mu? (0xFFFF) ideone gösterir (-1 >> 1) == -1.
Destrictor

87

Programı her seferinde derlemeniz gerekecek, ancak herhangi bir C veya C ++ sürümünde herhangi bir pozitif tamsayı çarpımı yapıyor.

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

 typedef char buffer[A][B];

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

4
Bir yapının içine koyun ve belleğe ihtiyacınız yok.
Ben Jackson,

4
Hahahah harika!
Almo

1
"%zu"biçim dizesini kullanın .
Grijesh Chauhan

5
sadece sizeof(char[A][B])çalışacak (A <= 0 veya B <= 0 veya A * B
taşmadığı sürece

3
@DavidRicherby - Kodu basitleştirebilirim main(){return sizeof(char[A][B]);}ve kullanarak cc -DA=6 -DB=7 a.c; ./a.out; echo $?
derlersiniz

47

Küçük bir titizlikle tamam iseniz, Monte Carlo yöntemini kullanabilirsiniz :

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

Örnek:

$ ./mul 300 250
300 * 250 = 74954

Sanırım bu yeterince iyi olabilir;)


3
Benim oyumu aldın. Monte Carlo’nun NASA’nın aritmetiği için kullandığını duydum. Fakat bunu ++operatörün iki örneği olmadan görmek isterim .
Darren Stone

1
@DarrenStone-= -1
Timtech

20
@Timtech |= 1(sayıların% 50'si, zamanın% 100'ü üzerinde çalışacak)
Darren Stone

2
+1, ancak çok yavaş olabileceğine dikkat edin ve 'count ++' :-)
greggo

1
Her zaman bir printfartış vardır: printf("%*cc%n\n", count, &count, 'c');('c' sayım sayısını, sonra başka bir 'c' basar ve tekrar yazılan karakter sayısını saklar count.
MSalters

45

Hangi boyutta bir sayı belirtmediğinizden, iki tane tek bit sayı demek istediğinizi varsayalım.

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

Yüksek verimli bir uygulama istiyorsanız, aşağıdaki küçük uygulamaları kullanın:

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

Türleri örtük olsa bile, yalnızca bitleri kabul ettiğini unutmayın; daha az kod alır ve bu nedenle daha verimlidir. (Ve evet, derler.)


8
Güzel. Sorunun kasıtlı olarak yanlış yorumlanması :-)
John Dvorak

6
Bunu için optimize edebilirsiniz return a && b;. Daha kısa, bu yüzden daha hızlı.
Ry

1
@ minitech: Kodu biraz daha kötü hale getirmek için buna karşı karar verdim. Bununla daha ileri gitmek isteseydim, yapardım return a&b;.
Cel Skeggs

1
C #include<stdbool.h>tanımlamalı trueve false.
leewz

1
Evet, #include<stdbool.h>sadece üç gibi görünüyor #define(eğer kendiniz yapabilirsiniz ler true, false, bool, ve bir bayrak aktive olmuş olduğunu işaretlemek için). Ayrıca diğer cevaplardan birinden bir numara alabilir intve "kısa" versiyon için örtük kullanabilirsiniz .
leewz

31

İşte yapılacak basit bir kabuk betiği:

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 ]*$"

GÜNCELLEME: Tabii ki, C içinde yapmak için, sadece içine sarın exec("bash", "-c", ...). (Teşekkürler, AmeliaBR)


41
Hangisinin daha korkunç olduğuna karar veremiyorum. Hesaplamanızı bir arama motoruna dış kaynak olarak kullandığınızı veya seçtiğiniz arama motorunun Bing olduğunu. Ne yazık ki, bunun C’de bir şeye ihtiyaç duyan mutlu OP’miz için işe yarayacağını sanmıyorum
AmeliaBR

5
Gözetimi aldığın için teşekkürler. Bilginize, Bing'i kullanıyorum çünkü Google böyle istekleri yayınlamayı daha karmaşık hale getiriyor - Google’ı isteğinizin gerçekten bir tarayıcıdan geldiğine ikna etmek için başlıklar eklemelisiniz.
Vroo

2
@ abarnert hmm ... Bing "zamanları" anlıyor mu? Olsa Wolfram Alpha olabilir.
John Dvorak

2
@JanDvorak: Evet, Wolfram çalışıyor. ( %20Herhangi bir +işareti kullanmaktan kaçınmak için not alın .) Ancak değeri çıkarmak için çıktıyı (C cinsinden) ayrıştırmanız gerekir. Çıktı metin değil, bir resim gibi göründüğü için özellikle zor olacak. HTML ayrıştırma artı OCR, bu soruna en iyi şekilde yanıt verilmesini sağlayabilir.
abarnert

3
@JanDvorak: Bu eğlenceli değil. Hiçbir ekleme veya çarpma olmadan basit bir OCR kütüphanesi yazan birini bekliyordum…
abarnert

27

Neden, INT64_MIN ve INT64_MAX arasında yinelemeli bir yarı arama yapalım!

#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 Mutlu bir şekilde bazı değerlerle sigsegv olacaktır. ;)


18

Ne yazık ki, bu sadece tamsayılar için çalışır.

Eklemeye izin verilmediğinden, önce bir artış işleci oluşturalım:

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

Sonra tabelayı ele almalıyız. İlk önce, işaret biti bulun:

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

Sonra her bir argümanın işaretini ve büyüklüğünü alın. İkinin tamamlayıcısındaki bir sayıyı reddetmek için, tüm bitleri ters çevirin ve bir tane ekleyin.

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

İki pozitif tamsayıyı çarpmak için çarpımın geometrik anlamını kullanabiliriz:

// 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);

troller:

  • Eklemeye izin verilmez, ancak a++gerçekten ek olarak sayılır mı? Bahse girerim öğretmen izin vermeyi amaçlıyordu.
  • İkisinin tamamlayıcısına güvenir, ancak bu uygulama tarafından tanımlanmış bir davranış ve hedef platform belirtilmedi.
  • Benzer şekilde, çıkarma ve bölme işlemlerine de izin verilmediğini varsayar.
  • << aslında ikisinin gücü ile çarpmadır, bu nedenle teknik olarak izin verilmez.
  • gerekli olmayan diyagram gerekli değildir. Ayrıca, bir satır kaydetmek için devredilebilirdi.
  • tekrarlanan kayması -1, işaret bitini bulmanın en güzel yolu değildir. Yerleşik sabit olmasa bile, -1'e kadar bir mantıksal kayma yapabilir ve ardından tüm bitleri tersine çevirebilirsiniz.
  • XOR -1, tüm bitleri ters çevirmenin en iyi yolu değildir.
  • Büyüklük işareti temsil eden tüm meydan okumalar zorunlu değildir. Sadece imzasız döküm ve modüler aritmetik gerisini halleder.
  • MIN_INT(AKA signBit) mutlak değeri negatif olduğu için bu değer kırılır. Neyse ki, davaların yarısında hala çalışıyor, çünkü sıfır MIN_INT * [even number] olması gerekiyor.Ayrıca, sonuç taşar zaman sınırsız döngülere neden olarak plusOnekırılır -1. plusOneherhangi bir değer için sadece iyi çalışıyor. Karışıklık için özür dilerim.

Gerçek bir kod trolü için +1: Çalışması gerektiği gibi görünüyor, ancak OP'ye büyük olasılıkla patlayacak ve nedenini bilmiyor.
Kevin

1
HERHANGİ bir toplama operatörü olmadan sadece shift, XOR ve AND kullanarak ekleme yapmak mümkündür. Bütün bu ++ 'lar başımı ağrıtıyor - single bit ADD carry ile (x ^ y) | ((x & y) << 1) (bu boktan küçük metin kutusuna yazmanın neden olduğu hataları modulo olarak düzenleyin.)
Julie, Austin'de

@JulieinAustin Evet. Algoritma olması gerekenden daha yetersizdir. Trol listesini değiştirmeli miyim? :-)
John Dvorak

1
@JulieinAustin (x ^ y) | ((x & y) << 1), bu :) oldukça işe carry x veya y yaymak ve taşımak aynı pozisyonda ikisi de doğruysa olmayacak gelmez
hobbs

@ hobbs çözümü: ORing yerine, eğer sıfır sıfır değilse bunları yinelemeli olarak ekleyin.
John Dvorak

14

Kayan nokta sayıları için de geçerlidir:

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

11

Herkes Python'un C'den daha kolay kullanılacağını bilir. Ve Python, operatörü kullanamadığınız durumlar için her operatöre karşılık gelen fonksiyonlara sahiptir. Sorun tanımımız tam olarak hangisidir? Yani:

#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

Diğer cevapların hiçbiri teorik olarak sağlam değildir. Soru üzerine ilk yorumun dediği gibi:

Lütfen "sayılar" hakkında daha spesifik olun

Bir cevap bile mümkün olmadan önce çarpımı ve sayıları tanımlamamız gerekir. Bir kez yaptığımızda, sorun önemsiz hale geliyor.

Bunu matematik mantığının başlangıcında yapmanın en popüler yolu , ZF küme teorisinin üstüne von Neumann ordinallerini inşa etmek ve sonra Peano aksiyomlarını kullanmaktır .

Bu, diğer kümeleri içerebilecek bir küme türünüz olduğunu varsayarsak, doğal olarak C'ye çevirir. Bir şey ihtiva zorunda değildir ama (o döküm hiçbiri Önemsiz kılan setleri, void*en seti kütüphanelerde saçma), bu yüzden okuyucuya alıştırma olarak uygulanmasını bırakacağız.

Yani, önce:

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

Bunu tamsayılara, rasyonellere, gerçeklere, sürreallere vb. Genişletmek istiyorsanız, sınırsız hassasiyetle (sonsuz bellek ve CPU'ya sahip olduğunuz varsayılarak) yapabilirsiniz. Ancak Kroenecker'in dediği gibi, Tanrı doğal sayıları yaptı; Geri kalan her şey insanın işi, yani gerçekten, neden rahatsız ediyor?


1
Vay. Benden daha yavaşsın.
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 );
}

Bir [b] hackinin hile yaptığını düşünüyorsanız (gerçekten bir eklenti olduğundan) bunun yerine işe yarar. Ancak tablo aramaları da işaretçi ekler içerir.

Bkz. Http://en.wikipedia.org/wiki/IBM_1620 - arama tablolarını kullanarak gerçekten ekleyen bir bilgisayar ...

Tek bir komutta yapılabilecek bir işlemi “hızlandırmak” için bir masa mekanizması kullanma konusunda tatmin edici bir şey.

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

Hata! *Char var (çarpma olmasa da)
Sarge Borsch

Ah, tablo araması toplama kullanır - (a [i]), (* (a + i)) ile aynıdır.
Julie, Austin’de

@JulieinAustin Bundan bahsettim. Tablo arama ekleri olmadan, alanları birleştirerek yapılabilir (IBM 1620'de gösterildiği gibi, bağlantıya bakın) ancak C'yi ayarlamak karmaşık bir durumdur; Sadece ya da içeri girdim
greggo

8

Bu "kod trolü" yayınlarında neyin "hile" oluşturduğundan emin değilim, ancak bu, çalışma sırasında standart kitaplıkları kullanan (C99) hiç *veya +operatör olmadan, 2 rasgele tam sayıyı çoğaltır .

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

8

İçin troll çözümüm 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 Taşınma değerlendirmesinde güzel uygulama. Ben kodunuzu :) gibi
yo'

@ BЈовић: Benim hatam, trolling'in bir şey yapmadığına inanıyordum. Değiştirilen isimler ve yorumlar eklendi.
Matthias

üzgünüm. Bu etiketin ne olduğunu ve Q'nun gerçekte ne anlama geldiğini yanlış anladım. Geri almalısın
BЈовић

Bu durumda @Mattia, nasıl çalıştığını anlamakta fayda sağlar, böylece yakınsak taşıma operasyonunun ne kadar büküldüğünü anlayabiliriz. Gerçek bir kod-troll durumda, yorumlar :-) yeniden düzenlenebilir
greggo

Bit ters sayılar eklemek istiyorsanız (yüksek taşıma potansiyeline sahip) ve bir 'bitrev' talimatınız yoksa, bu muhtemelen makul bir yaklaşımdır (c> ye değiştirdikten sonra > = Elbette 1)
greggo

7

Burada çok sayıda iyi cevap var, ancak çoğu modern bilgisayarların gerçekten güçlü olmasından faydalanıyor gibi görünmüyor. Çoğu işlemcide birden fazla işlem birimi vardır, peki neden sadece birini kullanmalı? Mükemmel performans elde etmek için bundan faydalanabiliriz.

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

İşte kullanımına bir örnek:

$ ./multiply
5 6
5 x 6 = 30

#pragma omp parallelYönerge OpenMP farklı bir yürütme birimine döngü her bölümünü bölmek yapar, bu yüzden paralel çarparak ediyoruz!

-fopenmpDerleyiciye OpenMP kullanmasını söylemek için bayrağı kullanmanız gerektiğini unutmayın .


Troll parçaları:

  1. Paralel programlama kullanımı konusunda yanıltıcı.
  2. Negatif (veya büyük) sayılar üzerinde çalışmaz.
  3. Aslında fordöngünün parçalarını bölmez - her iş parçacığı döngüyü çalıştırır.
  4. Can sıkıcı değişken isimleri ve değişken yeniden kullanımı.
  5. Üzerinde ince bir yarış durumu var answer--; çoğu zaman görünmez, ancak bazen yanlış sonuçlara neden olur.

2
Neden bunu Paul R'nin SIMD cevabı ile birleştirmiyorsunuz, böylece 8x yerine 32x hızlı çalıştırabilirsiniz? Gerçi gerçekte, GPU'yu çekirdeklerin yanı sıra dahil etmek istersiniz; o zaman gerçekten alevlenirdi. :)
abarnert

2
Paralel olarak birkaç makinede çalıştırmak için OpenMPI kullanabilirsiniz.
millinon

6

Ne yazık ki, çarpma bilgisayar bilimlerinde çok zor bir problemdir. En iyi çözüm, bunun yerine bölme kullanmaktır:

#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

Gerçek hayatta, genellikle bilgiyle trolling'e yanıt veririm, işte burada hiç trol yapmayan bir cevap. Görebildiğim intkadarıyla tüm değerler için çalışıyor .

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

Bu, benim anladığım kadarıyla, bir işlemcinin tam sayı çarpımı yapması gibi. Öncelikle, argümanlardan en az birinin ( a), her ikisinin de anegatif olması durumunda işareti çevirerek pozitif olduğundan emin oluruz (ve hayır, olumsuzlamayı bir tür toplama veya çarpma olarak saymayı reddediyorum). Ardından while (a)döngü b, ayarlanan her bit için sonuca kaydırılmış bir kopya ekler a. doDöngü uygular r += xkullanıyor ve xor ve geri beslenen taşıma bit ile açıkça yarım toplayıcılar bir dizi ne de değişen xdaha verimlidir tam toplayıcıları kullanmak istiyorsunuz artık (gerçek işlemci, kalmayıncaya kadar, ancak C kokan Operatörü saymazsanız, bunun için gereken operatörlere sahip değiliz +).


4
Asker troll yapmadı. Troll olman gerekiyordu.
John Dvorak

2
Bu gizli bir trol! Gizli başarısızlık == INT_MIN'de.
Jander

1
@Jander hmm. Evet, bu iyi bir tane. Tahmin ediyorum (sıradan ikisinin tamamlayıcı sistemlerinde), a'nın olumsuzlanmasının sonucunun hala negatif olduğunu ve while(a)döngü hiçbir zaman sonlanmadığını tahmin ediyorum .
Hobbs

@ hobbs Evet, bu bana doğru geliyor. Aksi takdirde çok güzel bir cevap.
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
Sonuç taşarsa bu korkunç başarısız olur. Güzel! Yine de basmamalısın.
John Dvorak

2
a = 2 için başarısız, b = 2, c = 5
BЈовић

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

2
Bunun gerçekten Deep Thought'un halefi ile aynı şeyi yapma çabası olduğunu, ancak cevabın 42 olduğu yer yerine, muhtemelen tüm evrenler için olduğunu unutmayın. Böcek olmasa, bu çok etkileyici olurdu. Ve Vogons durumunda hata işleme eksikliği.
abarnert

1
Birden fazla iş parçacığı gerekiyor. Bilirsin, verimli kılmak için.
greggo

6

Bunu karışıma atmak:

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

Bilgi sayfasından .

- Kodda, kabul edilemez veya makul olmayan bir şeyi, her şeyi çöpe atmadan çıkaramayacağınız bir şekilde tanıtmak, cevabı OP için tamamen yararsız hale getirir.

- […] Amaç, ödevi tembel OP'nin kabul edilebilir olduğunu düşündüğü bir dilde yapmak ama yine de onu sinirlendirmektir.


2
msgstr "çarpma ve toplama operatörlerini kullanmadan". Kuralların güzel bir şekilde bükülmesi - bu kesinlikle asker için yararsız olacak :-)
John Dvorak

2
Bu gerçekten bir C çözümü değil. Ayrıca, benim ARM9 üzerinde derleme başarısız olur.
abarnert

1
@ abarnert: İfadenizi ilgili bir argüman olarak tanıyamadı.
Runium,

@Sukminder: Soru şu ki: "Bir C programı yazmak mümkün mü?" Satır içi düzeneği C değildir. Bazı C derleyicilerinin de satır içi derleme yapabileceği gerçeği, bazı C derleyicilerinin de C ++ veya ObjC yapabildiğinden daha fazla, C ++ veya ObjC'nin C kodu sayılmasını sağlamaz.
abarnert

2
@ abarnert: C programlarında yaygın olarak kullanılan gömülü koddur. Bir melez olsa da, bunun bir C programı olduğunu iddia edebilir . Olası OP bile C kodu olarak tanınırdı. Açıkça python değil, ya da?
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

Çarpma ve toplama operatörlerini kullanmadan iki sayıyı çarpan bir C programı yazmak mümkün mü?

Elbette:

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

Ama elbette hile yapmak; Açıkçası, iki sayıyı tamamlayabilmeyi istiyor, değil mi?

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

Neden, bana hiç belli değildi!
leewz

4

İşaretçi aritmetik benzeri bir aritmetik yok:

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

İşlev fçarpımı uygular. mainbasitçe iki argümanla çağırır.
Negatif sayılar için de işe yarar.


Olumsuz a, evet, olumsuz bsanmıyorum. Ancak bu, birçok yaratıcı şekilde düzeltilebilir. En basit olanı sign_a ^ = sign_b, sign_b = 0 olur.
MS

@ MSalters, tüm işaret kombinasyonları için test edildi ve çalışıyor (Linux / gcc ile).
15'te ugoren

3

C #

Sanırım çıkarma ve olumsuzlamaya izin verilmiyor ... Neyse:

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

1
Bu tam olarak düşündüğüm çözümdü ... ama partiye geç geldiğimde, birinin zaten yazdığını bulana kadar aşağı kaydırma meselesi olduğunu biliyordum. Yine de benden bir - (- 1) alırsın.
Floris

3

SSE özdeşliğine sahip C (çünkü SIMD ile her şey daha iyidir):

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

Bu uygulamanın en büyük avantajı, gerek olmadan *veya +gerekmeden 4 paralel çarpma gerçekleştirmek için kolayca adapte edilebilmesidir .


Bunun trolling olduğunu sanmıyorum ...
John Dvorak

Gerçekten mi ? SIMD'in anlamsız, berrak ve mimariye özgü kullanımının onu kod trolling için nitelendirebileceğini düşündüm?
Paul R

hmm ... doğru. Bunun mimariye özgü olduğunu bilmiyordum.
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;
}
  • sadece çarpma sonucu için çalışmak <1 000 000
  • Şimdiye kadar operatörden kurtulmak mümkün değil, muhtemelen burada
  • basılan karakterleri saymak için printf'de% n format belirteci kullanma (bunu% C'nin yerine% n'nin varlığını hatırlatmak için ağırlıklı olarak hatırlatmak için gönderdim.)
  • '*' A * b karakterlerini yazdırır

Şimdi 'turing machine emulation' çözümünü bekliyor.
greggo

1
Bu arada ( strlen(cg) != aortadan kaldırmak için --O (N * N)) ortadan kaldırmak için çok trolling bir yöntemdir .
MSalters

3

Muhtemelen çok hızlı :-(

   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. Bu trolling değil. Bu, bunu yapmanın tamamen makul bir yoludur.
John Dvorak

1
Çok güzel çünkü çok hızlı :-)
Timtech

3

Bu Haskell sürümü yalnızca negatif olmayan tamsayılarla çalışır, ancak çocukların ilk öğrendiği şekilde çarpma işlemini yapar. Yani, 3x4, 4 şeyden oluşan 3 gruptur. Bu durumda, sayılan "şeyler" bir çubuktaki çentiklerdir ('|').

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

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

Bu, hava doğru olduğunda C99'da çalışabilir ve derleyiciniz tanımsız saçmalığı destekliyor.


3

Yana OP C sormadım , burada (Oracle) SQL biri!

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
Tanrım, *s dolu !
Paul R

1
@PaulR :) ama operatör değiller .
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 izleri içerebilir.


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

Test sürüşü:

$ ./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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.