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

  1.  

    导读:算法一直是程序员进阶的一道龙门,通常算法都是为了更高效地解决问题而创造的,但也有的只是出于学术性,并不在意其实际意义。这是近日在国外技术问答网站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#,但本题更倾向于算法,所以语言并不是太重要吧?(当然只是在不使用语言特性的前提下。)

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值