赞
踩
方法是:http://stackoverflow.com/questions/11694546/divide-a-number-by-3-without-using-operators这里的,我对其进行了一些解释和翻译,有需要的请点击上述链接查看原文。
问:在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(用C语言实现)
第一种方法:使用位操作符并实现“+”操作
- // 替换加法运算符
- int add(int x, int y) {
- int a, b;
- do {
- a = x & y; /*判断是否需要进位*/
- b = x ^ y; /*求和,忽略了进位*/
- x = a << 1; /*向左移一位,为进位准备*/
- y = b; /*求和结果,忽略了进位*/
- } while (a); /*判断是否需要进位,如果为0,意味着没有对应位同时为1的情况,说明b就是结果*/
- return b;
- }
- int divideby3 (int num) {
- int sum = 0;
- while (num > 3) {
- sum = add(num >> 2, sum);
- num = add(num >> 2, num & 3); /*加上num&3是为了不丢失除法过程中的尾数,3即为11,即末尾两位*/
- }
- if (num == 3)
- sum = add(sum, 1);
- 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
第二种方法:
- #include <stdio.h>
- #include <stdlib.h>
- int main()
- {
- FILE * fp=fopen("temp.dat","w+b");
- int number=1234; /*number不能过大*/
- 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;
- }
注意使用的编译器。
原理:
申请number大的空间,然后以3为单位读出空间的内容,读的次数就是number/3.当然这种方法number不能过大啊。
这是一个很巧妙的思想啊。不过有些函数我记不牢啦。
关键函数:
calloc
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)
返回值:读取的元素的个数
第三种方法:
- log(pow(exp(number),0.33333333333333333333)) /* :-) */
增强版:
使用了公式,对,有些事情本来就很简单的。
- log(pow(exp(number),sin(atan2(1,sqrt(8)))))
第四种方法:
- #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()是标准C语言函数
- printf("%d\n", r.quot);
- return 0;
- }
第五种方法:使用内联汇编
- #include <stdio.h>
- int main() {
- int dividend = -42, divisor = 3, quotient, remainder;
- __asm__ ( "movl %2, %%edx;"
- "sarl $31, %%edx;"
- "movl %2, %%eax;"
- "movl %3, %%ebx;"
- "idivl %%ebx;"
- : "=a" (quotient), "=d" (remainder)
- : "g" (dividend), "g" (divisor)
- : "ebx" );
- printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder);
- }
第六种方法:
- // 注意: itoa 不是个标准函数,但它可以实现
- // 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) str[0] = ' '; // remove sign if positive
- 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
- }
第七种方法:
- 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++; // 这是++操作符,不是+
- floor = r;
- r++; // 这也不是
- }
- return floor;
- }
替换掉上面算法的++运算符:
- unsigned inc(unsigned x) {
- for (unsigned mask = 1; mask; mask <<= 1) {
- if (mask & x)
- x &= ~mask;
- else
- return x & mask;
- }
- return 0; // 溢出(注意:这里的x和mask都是0)
- }
这个版本更快一些:
- unsigned add(char const zero[], unsigned const x, unsigned const y) {
- // 这是因为:如果foo是char*类型, &foo[bar] == foo+bar
- 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;
- }
第八种方法:实现乘法
- int mul(int const x, int const y) {
- return sizeof(struct {
- char const ignore[y];
- }[x]);
- }
第九种方法:极限
- 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 + ...
第十种方法:
- 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#,但本题更倾向于算法,所以语言并不是太重要吧?(当然只是在不使用语言特性的前提下。)
如果你还想了解更多的方法可以点击这里。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。