如何在不使用 *
, /
, +
, -
, %
运算符的情况下将一个数除以3?
这个数可以是有符号或无符号的。
如何在不使用 *
, /
, +
, -
, %
运算符的情况下将一个数除以3?
这个数可以是有符号或无符号的。
+
运算符,因此您只需要使用位运算符添加值即可:// replaces the + operator
int add(int x, int y)
{
while (x) {
int t = (x & y) << 1;
y ^= x;
x = t;
}
return y;
}
int divideby3(int num)
{
int sum = 0;
while (num > 3) {
sum = add(num >> 2, sum);
num = add(num >> 2, num & 3);
}
if (num == 3)
sum = add(sum, 1);
return sum;
}
正如Jim所评论的那样,这个程序是有效的,因为:
n = 4 * a + b
n / 3 = a + (a + b) / 3
所以sum += a
,n = a + b
,并循环迭代
当a == 0 (n < 4)
时,sum += floor(n / 3);
即1,if n == 3, else 0
1/3=0.333333
,重复的数字使得使用 a/3=a/10*3+a/100*3+a/1000*3+(..)
很容易进行计算。在二进制中,也几乎是一样的:1/3=0.0101010101(二进制)
,这导致 a/3=a/4+a/16+a/64+(..)
。除以 4 所产生的位移就是其中的位移操作。最后检查 num==3 是必要的,因为我们只能使用整数进行计算。 - Yorick Sijslinga / 3 = a * 0.111111 (四进制) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..)
。四进制还解释了为什么只有3会在最后进行四舍五入,而1和2可以向下取整。 - Yorick Sijslingn == 2^k
,以下内容是正确的:x % n == x & (n-1)
,因此在这里使用 num & 3
来执行 num % 4
,因为 %
不被允许。 - aplavin愚蠢的情况需要一个愚蠢的解决方案:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * fp=fopen("temp.dat","w+b");
int number=12346;
int divisor=3;
char * buf = calloc(number,1);
fwrite(buf,number,1,fp);
rewind(fp);
int result=fread(buf,divisor,number,fp);
printf("%d / %d = %d", number, divisor, result);
free(buf);
fclose(fp);
return 0;
}
如果需要包括小数部分,只需将result
声明为double
类型,并将fmod(number,divisor)
的结果添加到其上即可。
它的工作原理说明
fwrite
函数写入number
字节(例如上面的例子中是123456)。rewind
函数将文件指针重置为文件开头。fread
函数从文件中读取最多number/divisor
个长度为divisor
的记录,并返回它所读取的元素数量。log(pow(exp(number),0.33333333333333333333)) /* :-) */
Math.log(Math.pow(Math.exp(709),0.33333333333333333333))
和Math.log(Math.pow(Math.exp(709),Math.sin(Math.atan2(1,Math.sqrt(8)))))
。请注意,我已将其翻译为通俗易懂的中文,但未更改原意,并且没有提供任何解释或其他内容。 - Shaheer#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int num = 1234567;
int den = 3;
div_t r = div(num,den); // div() is a standard C function.
printf("%d\n", r.quot);
return 0;
}
你可以使用(与平台相关的)内联汇编,例如x86:(也适用于负数)
#include <stdio.h>
int main() {
int dividend = -42, divisor = 5, quotient, remainder;
__asm__ ( "cdq; idivl %%ebx;"
: "=a" (quotient), "=d" (remainder)
: "a" (dividend), "b" (divisor)
: );
printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder);
return 0;
}
asm
指令是可行的。此外我要补充一点,C 编译器并不是唯一具有内嵌汇编器的编译器,Delphi 也有。 - Seth Carnegie// Note: itoa is non-standard but actual implementations
// don't seem to handle negative when base != 10.
int div3(int i) {
char str[42];
sprintf(str, "%d", INT_MIN); // Put minus sign at str[0]
if (i>0) // Remove sign if positive
str[0] = ' ';
itoa(abs(i), &str[1], 3); // Put ternary absolute value starting at str[1]
str[strlen(&str[1])] = '\0'; // Drop last digit
return strtol(str, NULL, 3); // Read back result
}
itoa
实现一个完整的、能够正常工作的程序,并且该函数能够使用任意进制,那么我会点赞。我之前并不知道itoa
可以使用任意进制。 - Mysticial/
和 %
... :-) - R.. GitHub STOP HELPING ICEprintf
来显示您的十进制结果。 - Damian Yerrick(注意:查看下面的修订版以获取更好的解决方案!)
这并不像听起来那么棘手,因为你说“不使用[..] +
[..] 运算符”。如果您想完全禁止使用+
字符,请参见下文。
unsigned div_by(unsigned const x, unsigned const by) {
unsigned floor = 0;
for (unsigned cmp = 0, r = 0; cmp <= x;) {
for (unsigned i = 0; i < by; i++)
cmp++; // that's not the + operator!
floor = r;
r++; // neither is this.
}
return floor;
}
那么只需输入div_by(100,3)
就可以将100
除以3
。
++
运算符:unsigned inc(unsigned x) {
for (unsigned mask = 1; mask; mask <<= 1) {
if (mask & x)
x &= ~mask;
else
return x & mask;
}
return 0; // overflow (note that both x and mask are 0 here)
}
+
,-
,*
,/
,%
这些 字符 的任何运算符的情况下略微更快的版本。unsigned add(char const zero[], unsigned const x, unsigned const y) {
// this exploits that &foo[bar] == foo+bar if foo is of type char*
return (int)(uintptr_t)(&((&zero[x])[y]));
}
unsigned div_by(unsigned const x, unsigned const by) {
unsigned floor = 0;
for (unsigned cmp = 0, r = 0; cmp <= x;) {
cmp = add(0,cmp,by);
floor = r;
r = add(0,r,1);
}
return floor;
}
我们使用add
函数的第一个参数,因为我们无法在不使用*
字符的情况下表示指针的类型,除了在函数参数列表中,其中语法type[]
与type* const
相同。
顺便提一句,您可以使用类似于AndreyT提出的0x55555556
技巧来轻松实现乘法函数:
int mul(int const x, int const y) {
return sizeof(struct {
char const ignore[y];
}[x]);
}
++
,那为什么不直接使用/=
呢? - qwertz++
也是一个快捷方式,等同于num = num + 1
。 - qwertz+=
最终是num = num + 1
的快捷方式。 - qwertz>>
运算符是“除以2的n次方”的运算符,即它是根据算术而非机器表示来指定的。 - R.. GitHub STOP HELPING ICE这是我的解决方案:
public static int div_by_3(long a) {
a <<= 30;
for(int i = 2; i <= 32 ; i <<= 1) {
a = add(a, a >> i);
}
return (int) (a >> 32);
}
public static long add(long a, long b) {
long carry = (a & b) << 1;
long sum = (a ^ b);
return carry == 0 ? sum : add(carry, sum);
}
首先,注意:
1/3 = 1/4 + 1/16 + 1/64 + ...
现在,剩下的就很简单了!
a/3 = a * 1/3
a/3 = a * (1/4 + 1/16 + 1/64 + ...)
a/3 = a/4 + a/16 + 1/64 + ...
a/3 = a >> 2 + a >> 4 + a >> 6 + ...
现在我们只需要将a的这些位移值相加即可!糟糕!我们不能直接相加,所以我们需要使用位运算符编写一个加法函数!如果你熟悉位运算符,我的解决方案应该看起来非常简单...但是万一你不熟悉,我将在最后通过一个示例进行解释。
另一个需要注意的事情是,首先我要向左移30位!这是为了确保小数部分不会被四舍五入。
11 + 6
1011 + 0110
sum = 1011 ^ 0110 = 1101
carry = (1011 & 0110) << 1 = 0010 << 1 = 0100
Now you recurse!
1101 + 0100
sum = 1101 ^ 0100 = 1001
carry = (1101 & 0100) << 1 = 0100 << 1 = 1000
Again!
1001 + 1000
sum = 1001 ^ 1000 = 0001
carry = (1001 & 1000) << 1 = 1000 << 1 = 10000
One last time!
0001 + 10000
sum = 0001 ^ 10000 = 10001 = 17
carry = (0001 & 10000) << 1 = 0
Done!
这仅仅是你小时候学过的进位加法!
111
1011
+0110
-----
10001
这个实现失败了,因为我们不能添加方程中的所有项:
a / 3 = a/4 + a/4^2 + a/4^3 + ... + a/4^i + ... = f(a, i) + a * 1/3 * 1/4^i
f(a, i) = a/4 + a/4^2 + ... + a/4^i
假设 div_by_3(a)
的结果为 x,则有 x <= floor(f(a, i)) < a / 3
。 当 a = 3k
时,我们得到了错误的答案。
n/3
的级数逼近始终小于n/3
,这意味着对于任何n=3k
,结果将是k-1
而不是k
。 - Xyand将一个32位的数字除以3,可以先将其乘以0x55555556
,然后取64位结果的高32位。
现在只需使用位运算和移位操作来实现乘法即可...
*
运算符吗? - luiscubal