是否可以编写一个将两个数字相乘而不使用乘法和加法运算符的C程序?
我在Stack Overflow上找到了这个。请帮助这个可怜的程序员解决他的问题。请不要给出答案一样c = a/(1/((float)b))
,这是完全一样的c = a*b
。(并且已经给出了答案。)
2014年1月19日投票最多的答案将获胜。
是否可以编写一个将两个数字相乘而不使用乘法和加法运算符的C程序?
我在Stack Overflow上找到了这个。请帮助这个可怜的程序员解决他的问题。请不要给出答案一样c = a/(1/((float)b))
,这是完全一样的c = a*b
。(并且已经给出了答案。)
2014年1月19日投票最多的答案将获胜。
Answers:
挽回是正确的方法!
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));
}
??::
不带括号,一个用于在不尝试调整规则的情况下解决问题;)
inc
函数测试其参数以查看最低位是否为1
; 如果是这样,它将在参数的其余高位上调用自身,并返回与被设置为相同的低位的结果0
,而如果没有(即最低位是0
),则将其替换0
为a 1
并返回结果。此过程与您要手动添加值(逐位二进制数)的过程非常相似。
您每次都必须编译该程序,但它确实会在任何版本的C或C ++中精确地乘以任何正整数。
#define A 45 // first number
#define B 315 // second number
typedef char buffer[A][B];
main() {
printf("%d\n",sizeof(buffer));
}
"%zu"
格式字符串。
sizeof(char[A][B])
起作用(除非A <= 0或B <= 0或A * B溢出,在这种情况下,您应该会遇到“错误类型”错误)
main(){return sizeof(char[A][B]);}
,您可以使用cc -DA=6 -DB=7 a.c; ./a.out; echo $?
如果您不太确定,但可以使用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
我想这可能已经足够了;)
++
运算符实例的情况下看到它。
-= -1
|= 1
(将100%的时间处理50%的数字)
printf
增加:printf("%*cc%n\n", count, &count, 'c');
(打印“C” count次,然后又“C”,并存储在写回的字符数count
。
由于您没有指定数字的大小,因此我假设您是指两个一位数字。
#include <stdbool.h>
bool mul(bool a, bool b) {
if (a && b) {
return true;
} else {
return false;
}
}
如果想要最大效率的实现,请使用以下微型实现:
m(a,b){return a&b;}
请注意,即使类型是隐式整数,它仍然只接受位-它需要较少的代码,因此效率更高。(是的,它确实可以编译。)
return a && b;
。它更短,因此更快。
return a&b;
。
#include<stdbool.h>
定义true
和false
。
#include<stdbool.h>
似乎只是三个#define
s表示你可以做自己(true
,false
,bool
,一个标志,用来标记,它的被激活)。您还可以从其他答案之一中获得技巧,并将隐式int
用于“简短”版本。
这是一个简单的shell脚本:
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", ...)
。(感谢AmeliaBR)
为什么,让我们在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));
}
附注:它将很高兴带有一些值。;)
不幸的是,这仅适用于整数。
由于不允许加法,因此我们首先构建一个增量运算符:
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++
真的算作加法吗?我打赌老师打算允许它。<<
实际上是乘以2的幂,因此从技术上应该不允许这样做。-1
不是找到符号位的最佳方法。即使没有内置常数,也可以将逻辑右移-1,然后将所有位取反。MIN_INT
(AKA signBit
)的绝对值为负,因此该值将中断。幸运的是,它在一半情况下仍然有效,因为MIN_INT * [even number]
应该为零。plusOne
中断for时-1
,结果溢出时都会引起无限循环。plusOne
任何值都可以正常工作。对困惑感到抱歉。(x ^ y) | ((x & y) << 1)
不相当的工作,也不会传播时携带X或Y和携带在相同的位置都为真:)
众所周知,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();
}
从理论上讲,没有其他答案是正确的。正如对该问题的第一条评论所述:
请更详细地说明“数字”
我们需要定义乘法和数字,然后才能给出答案。一旦我们这样做,问题就变得微不足道了。
在开始进行数学逻辑运算时,最流行的方法是在ZF集合论的基础上构建von Neumann序数,然后使用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)所说的那样,上帝创造了自然数。所有其他一切都是人类的工作,那么,为什么要麻烦呢?
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;
}
*
char(尽管不是乘法)
我的巨魔解决方案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;
}
这里有很多很好的答案,但是看起来很多人没有利用现代计算机真正强大的事实。大多数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。
巨魔零件:
for
循环的各个部分-每个线程都运行循环。answer--
。大多数情况下,它不会显示,但偶尔会导致结果不准确。不幸的是,乘法是计算机科学中一个非常困难的问题。最好的解决方案是改用除法:
#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));
}
在现实生活中,我通常会以知识来回应巨魔,所以这里给出的答案根本就不会。据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
使用和,异或x
运算以及移入一组明显的半加法器,并将进位进位,直到不再有进位为止(实际的CPU将使用全加法器,效率更高,但是C不会)没有您需要的操作员,除非您算上+
操作员)。
while(a)
循环永远不会终止。
int bogomul(int A, int B)
{
int C = 0;
while(C/A != B)
{
print("Answer isn't: %d", C);
C = rand();
}
return C;
}
while(C/A != B || C%A)
?
把它扔进去:
#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认为可以接受的语言来做作业,但仍然使他感到沮丧。
#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;
}
是否可以编写一个将两个数字相乘而不使用乘法和加法运算符的C程序?
当然:
void multiply() {
printf("6 times 7 is 42\n");
}
但是,那当然是作弊;显然他希望能够提供两个数字,对吗?
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");
}
}
没有像指针算法这样的算法:
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
我认为不允许相减和取反...无论如何:
int mul(int a, int b)
{
int t = 0;
for (int i = b; i >= 1; i--) t -= -a;
return t;
}
具有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个并行乘法,而无需执行*
或+
在需要时执行。
#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;
}
strlen(cg) != a
是消除--
(使其成为O(N * N))的一种非常拖钓的方法。
可能太快了:-(
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;
}
int multiply(int a, int b) {
return sizeof(char[a][b]);
}
如果天气合适,并且您的编译器支持未定义的废话,这可能在C99中起作用。
由于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;
*
!
#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