当前位置:   article > 正文

在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(更新了一些解释,用C语言实)_c语言1/3怎么表示

c语言1/3怎么表示

方法是:http://stackoverflow.com/questions/11694546/divide-a-number-by-3-without-using-operators这里的,我对其进行了一些解释和翻译,有需要的请点击上述链接查看原文。

问:在不使用*、/、+、-、%操作符的情况下,如何求一个数的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); /*判断是否需要进位,如果为0,意味着没有对应位同时为1的情况,说明b就是结果*/
  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); /*加上num&3是为了不丢失除法过程中的尾数,3即为11,即末尾两位*/
  17.     } 
  18.     if (num == 3) 
  19.         sum = add(sum, 1); 
  20.     return sum;  

原理:

有一位网友的解释很到位:

Here's a trick i found which got me a similar solution.

In decimal: 1 / 3 = 0.333333, the repeating numbers make this easy to calculate using a / 3 = a/10*3 + a/100*3 + a/1000*3 + (..). 

In binary it's almost the same: 1 / 3 = 0.0101010101 (base 2), which leads to a / 3 = a/4 + a/16 + a/64 + (..). 

Dividing by 4 is where the bit shift comes from.

 The last check on num==3 is needed because we've only got integers to work with. 

ps:In base 4 it gets even better: a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..). The base 4 also explains why only 3 is rounded up at the end, while 1 and 2 can be rounded down

下面是公式啦:

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.     FILE * fp=fopen("temp.dat","w+b"); 
  5.     int number=1234; /*number不能过大*/
  6.     int divisor=3; 
  7.     char * buf = calloc(number,1); 
  8.     fwrite(buf,number,1,fp); 
  9.     rewind(fp); 
  10.     int result=fread(buf,divisor,number,fp); 
  11.     printf("%d / %d = %d", number, divisor, result); 
  12.     free(buf); 
  13.     fclose(fp); 
  14.     return 0; 

注意使用的编译器。

原理:

申请number大的空间,然后以3为单位读出空间的内容,读的次数就是number/3.当然这种方法number不能过大啊。

这是一个很巧妙的思想啊。不过有些函数我记不牢啦。

关键函数:

 calloc

  void *calloc(unsigned n,unsigned size);
  功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。

 fwrite

 fwrite(const void* buffer,size_t size,size_t count,FILE* stream);
  返回值:返回实际写入的数据块数目
  (1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
  (2)size:要写入内容的单字节数;
  (3)count:要进行写入size字节的数据项的个数;
  (4)stream:目标文件指针。
返回实际写入的数据项个数count

rewind
  函数名: rewind
  功 能: 将文件内部的位置指针重新指向一个流(数据流/文件)的开头
  注意:不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
  用 法: void rewind(FILE *stream);

fread
  功 能: 从一个流中读数据
  函数原型: size_t fread(void*buffer,size_tsize,size_tcount,FILE*stream);

参 数:
  1.用于接收数据的地址(指针)(buffer)
  2.单个元素的大小(size) :单位是字节而不是位,例如读取一个int型数据就是4个字节
  3.读取size个字节的次数(count)
  4.提供数据的文件指针(stream)
  返回值:读取的元素的个数

第三种方法:

 
 
  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.     int num = 1234567; 
  5.     int den = 3; 
  6.     div_t r = div(num,den); // div()是标准C语言函数
  7.     printf("%d\n", r.quot); 
  8.     return 0; 

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

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

第六种方法:

 
 
  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 

第七种方法:

 
 
  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; 

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

 
 
  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)

这个版本更快一些:

 
 
  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; 

第八种方法:实现乘法

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

第九种方法:极限

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

原理:

因为, 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. public static int Negate(int a) { 
  19.     return Add(~a, 1); 
  20. public static int Add(int a, int b) { 
  21.     int x = 0
  22.     x = a ^ b; 
  23.     while ((a & b) != 0) { 
  24.         b = (a & b) << 1
  25.         a = x
  26.         x = a ^ b; 
  27.     } 
  28.     return x; 

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

如果你还想了解更多的方法可以点击这里

例题:http://ac.jobdu.com/problem.php?pid=1507

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/195099
推荐阅读
相关标签
  

闽ICP备14008679号