[算法]不使用*、/、+、-、%操作符求一个数的1/3

原地址:http://sd.csdn.net/a/20120807/2808268-divide-a-number-by-3-without-operators.html

       导读:算法一直是程序员进阶的一道龙门,通常算法都是为了更高效地解决问题而创造的,但也有的只是出于学术性,并不在意其实际意义。这是近日在国外技术问答网站stackoverflow的一个热门问题,不知道你能给出几种解决方法?

问:在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(用C语言实现)

第一种方法:使用位操作符并实现“+”操作

 
 
  1. // 替换加法运算符
  2. int add(int x, int y) {
  3. int a, b;
  4. do {
  5. a = x & y;
  6. b = x ^ y;
  7. x = a << 1;
  8. y = b;
  9. } while (a);
  10. return b;
  11. }
  12. int divideby3 (int num) {
  13. int sum = 0;
  14. while (num > 3) {
  15. sum = add(num >> 2, sum);
  16. num = add(num >> 2, num & 3);
  17. }
  18. if (num == 3)
  19. sum = add(sum, 1);
  20. return sum;
  21. }

原理:n = 4 * a + b; n / 3 = a + (a + b) / 3; 然后 sum += a, n = a + b 并迭代; 当 a == 0 (n < 4)时,sum += floor(n / 3); i.e. 1, if n == 3, else 0

第二种方法:

 
 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. FILE * fp=fopen("temp.dat","w+b");
  6. int number=12346;
  7. int divisor=3;
  8. char * buf = calloc(number,1);
  9. fwrite(buf,number,1,fp);
  10. rewind(fp);
  11. int result=fread(buf,divisor,number,fp);
  12. printf("%d / %d = %d", number, divisor, result);
  13. free(buf);
  14. fclose(fp);
  15. return 0;
  16. }

第三种方法:

 
 
  1. log(pow(exp(number),0.33333333333333333333)) /* :-) */

增强版:

 
 
  1. log(pow(exp(number),sin(atan2(1,sqrt(8)))))

第四种方法:

 
 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(int argc, char *argv[])
  4. {
  5. int num = 1234567;
  6. int den = 3;
  7. div_t r = div(num,den); // div()是标准C语言函数
  8. printf("%d\n", r.quot);
  9. return 0;
  10. }

第五种方法:使用内联汇编

 
 
  1. #include <stdio.h>
  2. int main() {
  3. int dividend = -42, divisor = 3, quotient, remainder;
  4. __asm__ ( "movl %2, %%edx;"
  5. "sarl $31, %%edx;"
  6. "movl %2, %%eax;"
  7. "movl %3, %%ebx;"
  8. "idivl %%ebx;"
  9. : "=a" (quotient), "=d" (remainder)
  10. : "g" (dividend), "g" (divisor)
  11. : "ebx" );
  12. printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder);
  13. }

第六种方法:

 
 
  1. // 注意: itoa 不是个标准函数,但它可以实现
  2. // don't seem to handle negative when base != 10
  3. int div3(int i) {
  4. char str[42];
  5. sprintf(str, "%d", INT_MIN); // put minus sign at str[0]
  6. if (i>0) str[0] = ' '; // remove sign if positive
  7. itoa(abs(i), &str[1], 3); // put ternary absolute value starting at str[1]
  8. str[strlen(&str[1])] = '\0'; // drop last digit
  9. return strtol(str, NULL, 3); // read back result
  10. }

第七种方法:

 
 
  1. unsigned div_by(unsigned const x, unsigned const by) {
  2. unsigned floor = 0;
  3. for (unsigned cmp = 0, r = 0; cmp <= x;) {
  4. for (unsigned i = 0; i < by; i++)
  5. cmp++; // 这是++操作符,不是+
  6. floor = r;
  7. r++; // 这也不是
  8. }
  9. return floor;
  10. }

替换掉上面算法的++运算符:

 
 
  1. unsigned inc(unsigned x) {
  2. for (unsigned mask = 1; mask; mask <<= 1) {
  3. if (mask & x)
  4. x &= ~mask;
  5. else
  6. return x & mask;
  7. }
  8. return 0; // 溢出(注意:这里的x和mask都是0)
  9. }

这个版本更快一些:

 
 
  1. unsigned add(char const zero[], unsigned const x, unsigned const y) {
  2. // 这是因为:如果foo是char*类型, &foo[bar] == foo+bar
  3. return (int)(uintptr_t)(&((&zero[x])[y]));
  4. }
  5. unsigned div_by(unsigned const x, unsigned const by) {
  6. unsigned floor = 0;
  7. for (unsigned cmp = 0, r = 0; cmp <= x;) {
  8. cmp = add(0,cmp,by);
  9. floor = r;
  10. r = add(0,r,1);
  11. }
  12. return floor;
  13. }

第八种方法:实现乘法

 
 
  1. int mul(int const x, int const y) {
  2. return sizeof(struct {
  3. char const ignore[y];
  4. }[x]);
  5. }

第九种方法:极限

 
 
  1. public static int div_by_3(long a) {
  2. a <<= 30;
  3. for(int i = 2; i <= 32 ; i <<= 1) {
  4. a = add(a, a >> i);
  5. }
  6. return (int) (a >> 32);
  7. }
  8. public static long add(long a, long b) {
  9. long carry = (a & b) << 1;
  10. long sum = (a ^ b);
  11. return carry == 0 ? sum : add(carry, sum);
  12. }

原理:

因为, 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 + ...

第十种方法:

 
 
  1. public static int DivideBy3(int a) {
  2. bool negative = a < 0;
  3. if (negative) a = Negate(a);
  4. int result;
  5. int sub = 3 << 29;
  6. int threes = 1 << 29;
  7. result = 0;
  8. while (threes > 0) {
  9. if (a >= sub) {
  10. a = Add(a, Negate(sub));
  11. result = Add(result, threes);
  12. }
  13. sub >>= 1;
  14. threes >>= 1;
  15. }
  16. if (negative) result = Negate(result);
  17. return result;
  18. }
  19. public static int Negate(int a) {
  20. return Add(~a, 1);
  21. }
  22. public static int Add(int a, int b) {
  23. int x = 0;
  24. x = a ^ b;
  25. while ((a & b) != 0) {
  26. b = (a & b) << 1;
  27. a = x;
  28. x = a ^ b;
  29. }
  30. return x;
  31. }

注:本例是C#实现,因为作者更熟悉C#,但本题更倾向于算法,所以语言并不是太重要吧?(当然只是在不使用语言特性的前提下。)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值