你会如何除以3为数字,没有使用*
,/
,+
,-
,%
,运营商?
该数字可以签名也可以不签名。
你会如何除以3为数字,没有使用*
,/
,+
,-
,%
,运营商?
该数字可以签名也可以不签名。
Answers:
这是执行所需操作的简单功能。但这需要+
运算符,因此您要做的就是用位运算符添加值:
// 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 (base 2)
,导致a / 3 = a/4 + a/16 + a/64 + (..)
。除以4是移位的来源。需要对num == 3进行最后检查,因为我们只有整数可以使用。
a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..)
。基数4还解释了为什么最后只对3进行四舍五入,而对1和2进行四舍五入的原因。
n == 2^k
符合下列条件:x % n == x & (n-1)
,所以这里num & 3
被用来执行num % 4
,同时%
是不允许的。
惯用条件需要惯用的解决方案:
#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
as 声明为double
并将其结果添加到fmod(number,divisor)
。
解释如何运作
fwrite
写入number
字节(数目在上面的例子中为123456)。rewind
将文件指针重置为文件的开头。fread
从文件中读取最大长度的number
“记录” divisor
,并返回其读取的元素数。如果写入30个字节,然后以3为单位读回文件,则将得到10个“单位”。30/3 = 10
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)))))
您可以使用(取决于平台的)内联汇编,例如,对于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也是如此。
asm
指令仅在C99标准的附录J-通用扩展名中提及。
使用itoa转换为基数为3的字符串。放下最后一个Trit,然后转换回10。
// 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
我进行完整的实施,我会投票赞成。
/
和%
... :-)
printf
用于显示十进制结果的实现也是如此。
(注意:请参见下面的“编辑2”以获得更好的版本!)
这听起来并不那么棘手,因为您说的是“不使用[..] +
[..] 运算符 ”。如果要禁止同时使用该+
字符,请参见下文。
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
。
FWIW,您可以使用类似的技巧轻松地实现乘法功能,以使用AndreyT0x55555556
提出的技巧:
int mul(int const x, int const y) {
return sizeof(struct {
char const ignore[y];
}[x]);
}
++
:为什么不简单使用/=
?
++
也是快捷方式:For num = num + 1
。
+=
最终是的快捷方式num = num + 1
。
在Setun计算机上可以轻松实现。
要将整数除以3,请向右右移1位。
我不确定在这种平台上是否完全可以实现符合标准的C编译器。我们可能需要稍微延长规则,例如将“至少8位”解释为“能够容纳至少从-128到+127的整数”。
>>
运算符是“除以2 ^ n”运算符,即它是根据算术而不是机器表示法指定的。
这是我的解决方案:
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的这些位移值相加!糟糕!但是,我们无法添加,因此,我们必须使用按位运算符编写一个add函数!如果您熟悉按位运算符,则我的解决方案应该看起来很简单...但是万一您不知道,我将在最后讲一个示例。
还要注意的另一件事是,我先左移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
。
要将32位数字除以3,可以将其乘以0x55555556
然后取64位结果的高32位。
现在剩下要做的就是使用位运算和移位来实现乘法...
multiply it
。那不是暗示使用禁止的*
运算符吗?
另一个解决方案。这应该处理除整数的最小值以外的所有整数(包括负整数),这需要作为硬编码异常处理。这基本上是通过减法进行除法,但仅使用位运算符(移位,xor和&和补数)。为了获得更快的速度,它会减去3 *(2的幂减小)。在c#中,它每毫秒执行大约444个DivideBy3调用(1,000,000除法为2.2秒),因此运行速度并不惊人,但没有x / 3那样快。相比之下,Coodey的不错的解决方案比该解决方案快约5倍。
public static int DivideBy3(int a) {
bool negative = a < 0;
if (negative) a = Negate(a);
int result;
int sub = 3 << 29;
int threes = 1 << 29;
result = 0;
while (threes > 0) {
if (a >= sub) {
a = Add(a, Negate(sub));
result = Add(result, threes);
}
sub >>= 1;
threes >>= 1;
}
if (negative) result = Negate(result);
return result;
}
public static int Negate(int a) {
return Add(~a, 1);
}
public static int Add(int a, int b) {
int x = 0;
x = a ^ b;
while ((a & b) != 0) {
b = (a & b) << 1;
a = x;
x = a ^ b;
}
return x;
}
这是c#,因为这就是我的方便,但是与c的区别应该很小。
(a >= sub)
算作一个减法?
这真的很容易。
if (number == 0) return 0;
if (number == 1) return 0;
if (number == 2) return 0;
if (number == 3) return 1;
if (number == 4) return 1;
if (number == 5) return 1;
if (number == 6) return 2;
(为了简洁起见,我当然省略了一些程序。)如果程序员厌倦了全部输入,我相信他(她)可以编写一个单独的程序来为他生成程序。我碰巧知道某个操作员,/
它将极大地简化他的工作。
Dictionary<number, number>
而不是重复的if
语句,这样就可以节省O(1)
时间!
使用计数器是一个基本的解决方案:
int DivBy3(int num) {
int result = 0;
int counter = 0;
while (1) {
if (num == counter) //Modulus 0
return result;
counter = abs(~counter); //++counter
if (num == counter) //Modulus 1
return result;
counter = abs(~counter); //++counter
if (num == counter) //Modulus 2
return result;
counter = abs(~counter); //++counter
result = abs(~result); //++result
}
}
执行模数功能也很容易,请检查注释。
这是基础2中的经典除法算法:
#include <stdio.h>
#include <stdint.h>
int main()
{
uint32_t mod3[6] = { 0,1,2,0,1,2 };
uint32_t x = 1234567; // number to divide, and remainder at the end
uint32_t y = 0; // result
int bit = 31; // current bit
printf("X=%u X/3=%u\n",x,x/3); // the '/3' is for testing
while (bit>0)
{
printf("BIT=%d X=%u Y=%u\n",bit,x,y);
// decrement bit
int h = 1; while (1) { bit ^= h; if ( bit&h ) h <<= 1; else break; }
uint32_t r = x>>bit; // current remainder in 0..5
x ^= r<<bit; // remove R bits from X
if (r >= 3) y |= 1<<bit; // new output bit
x |= mod3[r]<<bit; // new remainder inserted in X
}
printf("Y=%u\n",y);
}
用Pascal编写程序并使用DIV
运算符。
由于问题已被标记 C,您可能可以在Pascal中编写一个函数,然后从C程序中调用它;这样做的方法是特定于系统的。
但是,这是一个在我的Ubuntu系统上fp-compiler
安装了Free Pascal 软件包的示例。(我这样做是出于固执的错位;我不声称这是有用的。)
divide_by_3.pas
:
unit Divide_By_3;
interface
function div_by_3(n: integer): integer; cdecl; export;
implementation
function div_by_3(n: integer): integer; cdecl;
begin
div_by_3 := n div 3;
end;
end.
main.c
:
#include <stdio.h>
#include <stdlib.h>
extern int div_by_3(int n);
int main(void) {
int n;
fputs("Enter a number: ", stdout);
fflush(stdout);
scanf("%d", &n);
printf("%d / 3 = %d\n", n, div_by_3(n));
return 0;
}
建立:
fpc divide_by_3.pas && gcc divide_by_3.o main.c -o main
示例执行:
$ ./main
Enter a number: 100
100 / 3 = 33
int div3(int x)
{
int reminder = abs(x);
int result = 0;
while(reminder >= 3)
{
result++;
reminder--;
reminder--;
reminder--;
}
return result;
}
ADD
,INC
并且它们没有相同的操作码。
没有交叉检查该答案是否已经发布。如果程序需要扩展为浮点数,则可以将这些数字乘以10 *所需的精度数,然后可以再次应用以下代码。
#include <stdio.h>
int main()
{
int aNumber = 500;
int gResult = 0;
int aLoop = 0;
int i = 0;
for(i = 0; i < aNumber; i++)
{
if(aLoop == 3)
{
gResult++;
aLoop = 0;
}
aLoop++;
}
printf("Reulst of %d / 3 = %d", aNumber, gResult);
return 0;
}
这应该适用于任何除数,而不仅仅是三个。当前仅适用于未签名,但将其扩展到已签名应该没有那么困难。
#include <stdio.h>
unsigned sub(unsigned two, unsigned one);
unsigned bitdiv(unsigned top, unsigned bot);
unsigned sub(unsigned two, unsigned one)
{
unsigned bor;
bor = one;
do {
one = ~two & bor;
two ^= bor;
bor = one<<1;
} while (one);
return two;
}
unsigned bitdiv(unsigned top, unsigned bot)
{
unsigned result, shift;
if (!bot || top < bot) return 0;
for(shift=1;top >= (bot<<=1); shift++) {;}
bot >>= 1;
for (result=0; shift--; bot >>= 1 ) {
result <<=1;
if (top >= bot) {
top = sub(top,bot);
result |= 1;
}
}
return result;
}
int main(void)
{
unsigned arg,val;
for (arg=2; arg < 40; arg++) {
val = bitdiv(arg,3);
printf("Arg=%u Val=%u\n", arg, val);
}
return 0;
}
以下脚本生成一个无需使用运算符即可解决问题的C程序* / + - %
:
#!/usr/bin/env python3
print('''#include <stdint.h>
#include <stdio.h>
const int32_t div_by_3(const int32_t input)
{
''')
for i in range(-2**31, 2**31):
print(' if(input == %d) return %d;' % (i, i / 3))
print(r'''
return 42; // impossible
}
int main()
{
const int32_t number = 8;
printf("%d / 3 = %d\n", number, div_by_3(number));
}
''')
我认为正确的答案是:
为什么不使用基本运算符进行基本运算?
使用fma()库函数的解决方案适用于任何正数:
#include <stdio.h>
#include <math.h>
int main()
{
int number = 8;//Any +ve no.
int temp = 3, result = 0;
while(temp <= number){
temp = fma(temp, 1, 3); //fma(a, b, c) is a library function and returns (a*b) + c.
result = fma(result, 1, 1);
}
printf("\n\n%d divided by 3 = %d\n", number, result);
}
使用cblas,它是OS X加速框架的一部分。
[02:31:59] [william@relativity ~]$ cat div3.c
#import <stdio.h>
#import <Accelerate/Accelerate.h>
int main() {
float multiplicand = 123456.0;
float multiplier = 0.333333;
printf("%f * %f == ", multiplicand, multiplier);
cblas_sscal(1, multiplier, &multiplicand, 1);
printf("%f\n", multiplicand);
}
[02:32:07] [william@relativity ~]$ clang div3.c -framework Accelerate -o div3 && ./div3
123456.000000 * 0.333333 == 41151.957031
第一:
x/3 = (x/4) / (1-1/4)
然后找出如何求解x /(1- y):
x/(1-1/y)
= x * (1+y) / (1-y^2)
= x * (1+y) * (1+y^2) / (1-y^4)
= ...
= x * (1+y) * (1+y^2) * (1+y^4) * ... * (1+y^(2^i)) / (1-y^(2^(i+i))
= x * (1+y) * (1+y^2) * (1+y^4) * ... * (1+y^(2^i))
y = 1/4时:
int div3(int x) {
x <<= 6; // need more precise
x += x>>2; // x = x * (1+(1/2)^2)
x += x>>4; // x = x * (1+(1/2)^4)
x += x>>8; // x = x * (1+(1/2)^8)
x += x>>16; // x = x * (1+(1/2)^16)
return (x+1)>>8; // as (1-(1/2)^32) very near 1,
// we plus 1 instead of div (1-(1/2)^32)
}
尽管它使用了+
,但是已经有人通过位运算器实现加法。