zoukankan      html  css  js  c++  java
  • PHP算法学习(5) 位运算

    svn地址:svn://gitee.com/zxadmin/live_z 

    2019年2月14日11:38:46

    <?php
    
    /*
     * 位运算学习笔记
     * 
     * 1,php所有的数都是有符号的,无法指定是否是无符号的 unsign
     * 2.计算机底层运算都是补码运算的
     * 3,0反码,补码都是0
     * 4,正数的反码,补码全都一样
     * 5,二进制的最高位是符号位,0是正数,1是负数
     * 6,负数的反码符号位不变,其他未取反
     * 7,负数的补码等于反码+1
     * 8,负数(取反)=》反码 +1 =》补码
     * 9,补码-1=》反码(取反)=》负数
     * 10,右移 低位溢出,符号位不变 ,并用符号位补溢出的到位
     * 11,符号位不变 低位补0
     * 
     * 32位的1表示是     31个0+1
     * 64位的1表示是    63个0+1
     * 参考 http://php.net/manual/zh/function.decbin.php
     * 测试:  print_r(decbin(-50))  32 位
     * 
      $a & $b    And(按位与)    将把 $a 和 $b 中都为 1 的位设为 1。
      $a | $b    Or(按位或)    将把 $a 和 $b 中任何一个为 1 的位设为 1。
      $a ^ $b    Xor(按位异或)    将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。
      ~ $a    Not(按位取反)    将 $a 中为 0 的位设为 1,反之亦然。
      $a << $b    Shift left(左移)    将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。
      $a >> $b    Shift right(右移)    将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。
    
      ⋅⋅右移 >>:将二进制进行右移,低位丢掉,高位补零 符号位不变 。
      ⋅⋅左移 <<:将二进制进行左移,低位补零,高位丢掉 符号位不变 。
      ----------------------------------------------------------------------------------------------------------------------
      &
      按位与
      如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
      |
      按位或
      两个相应的二进制位中只要有一个为1,该位的结果值为1
      ^
      按位异或
      若参加运算的两个二进制位值相同则为0,否则为1
      ~
      取反
      ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0
      <<
      左移
      用来将一个数的各二进制位全部左移N位,右补0
      >>
      右移
      将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补0
    
      要使用八进制表达,数字前必须加上 0(零)。要使用十六进制表达,数字前必须加上 0x。要使用二进制表达,数字前必须加上 0b。
      Example #1 整数文字表达
      $a = 1234; // 十进制数
      $a = -123; // 负数
      $a = 0123; // 八进制数 (等于十进制 83)
      $a = 0x1A; // 十六进制数 (等于十进制 26)
      $a = 0b11111111; // 二进制数字 (等于十进制 255)
     *
     * printf() - 输出格式化字符串, using %b, %032b or %064b as the format
     */
    
    final class BitOperation {
        /*
         * 注意:位运算只能支持int类型
         */
    
        public static $format = true;
    
        //计算原码 吧数字十进制转换成二进制,并补充字符到系统的位数指定长度的原码
        public static function OriginalCode(int $number) {
            $OriginalCode = self::DecimalToBinary($number);
            return $OriginalCode;
        }
    
        //计算反码
        public static function InverseCode(int $number) {
            $OriginalCode = self::DecimalToBinary($number);
    //        p($OriginalCode);
            $InverseCode = '';
            if ($number >= 0) {
                return $OriginalCode;
            } else {
                $array = str_split($OriginalCode);
                foreach ($array as $k => &$v) {
                    //符号位不变
                    if ($k == 0) {
                        continue;
                    }
                    //1变0 0变1
                    if ($v == '0') {
                        $v = 1;
                    } elseif ($v == '1') {
                        $v = 0;
                    }
                }
                foreach ($array as $k1 => $v1) {
                    $InverseCode .= $v1;
                }
            }
            return $InverseCode;
        }
    
        //计算补码
        public static function ComplementCode(int $number) {
            $InverseCode = self::InverseCode($number);
            $ComplementCode = '';
            if ($number >= 0) {
                return $InverseCode;
            } else {
    //            $array = str_split($InverseCode);
    //            for ($index = (count($array) - 1); $index >= 0; $index--) {
    //                $str .= $return[$index];
    //            }
                /*
                 * 不好直接计算
                 */
                return decbin($number);
            }
            return self::Format($ComplementCode);
        }
    
        /*
         * 十进制转二进制
         * 或者直接借用 decbin函数直接转换效率更高,这次是学习代码,为了写清楚原理
         * 注意这里的效率慢其实只要 self::PhpDigit()函数造成的,如果定义死计算是64,32效率快很多倍
         */
    
        public static function DecimalToBinary(int $number) {
            $return = [];
            $abs = abs($number);
            while ($abs > 0) {
                $return[] = $abs % 2;
                $abs = $abs >> 1;
            }
            $str = '';
            for ($index = (count($return) - 1); $index >= 0; $index--) {
                $str .= $return[$index];
            }
            $plus_or_minus = $number >= 0 ? true : false;
            return self::FillingLength($str, $plus_or_minus);
        }
    
        //优化版 十进制转二进制
        public static function DecimalToBinaryFast(int $number) {
            $return = [];
            $str = decbin(abs($number));
            $plus_or_minus = $number >= 0 ? true : false;
            return self::FillingLength($str, $plus_or_minus);
        }
    
        /*
         * $plus_or_minus true +  false -
         */
    
        public static function FillingLength($string = null, $plus_or_minus = true) {
            $length = self::PhpDigit();
            $res = '';
            if ($plus_or_minus) {
                $res = str_pad($string, $length, '0', STR_PAD_LEFT);
            } else {
                $res = str_pad($string, $length - 1, '0', STR_PAD_LEFT);
                $res = '1' . $res;
            }
            return $res;
        }
    
        public static function Format($string = null, $separation = 4) {
            if (mb_strlen($string) == 32 || mb_strlen($string) == 64) {
                $re = chunk_split($string, $separation, ".");
                $re = trim($re, '.');
                $re = str_replace('.', '&nbsp;', $re);
                return $re;
            } else {
                return $string;
            }
        }
    
        /*
         * 你的系统是64,但是你运行的php版本不一定是64特别是在windows上
         */
    
        public static function PhpDigit() {
            $phpinfo = self::PhpInfoArray();
            if (strtolower($phpinfo['General']['Architecture']) == 'x86') {
                return 32;
            } else {
                return 64;
            }
        }
    
        public static function PhpInfoArray() {
            ob_start();
            phpinfo();
            $info_arr = array();
            $info_lines = explode("
    ", strip_tags(ob_get_clean(), "<tr><td><h2>"));
            $cat = "General";
            foreach ($info_lines as $line) {
                // new cat?
                preg_match("~<h2>(.*)</h2>~", $line, $title) ? $cat = $title[1] : null;
                if (preg_match("~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~", $line, $val)) {
                    $info_arr[trim($cat)][trim($val[1])] = trim($val[2]);
                } elseif (preg_match("~<tr><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td><td[^>]+>([^<]*)</td></tr>~", $line, $val)) {
                    $info_arr[trim($cat)][trim($val[1])] = array("local" => trim($val[2]), "master" => trim($val[3]));
                }
            }
            return $info_arr;
        }
    
        public static function CountIntOne_1(int $n) {
            //计算一个十进制数转换为二进制数中‘1’的个数
            //例如十进制11 = 二进制1011,则结果是3个1
            //解题思路:利用 n & (n - 1) 可以将最后一个1变0
            //xxxx1000 & (xxxx1000 - 1) = xxxx1000 & xxxx0111 = xxxx0000
            // 1011 & (1011 - 1) = 1011 & 1010 = 1010
            //直到最后一个1被与为0,得出结果
            $r = 0;
            while ($n != 0) {
                $r++;
                $n &= ($n - 1);
            }
            return $r;
        }
    
        /*
         * 
         */
    
        public static function CountIntOne_2(int $n) {
            /*
             * 十进制转二进制过程计算余数
             * 1%2 = 1 1 2~0
             * 2%2 = 0 10 2~1
             * 3%2 = 1 11 2~0 + 2~1
             * 4%2 = 0 100 2~3
             * 5%2 = 1 101 2~0 + 2~3     5%2 = 1  1%2 =1
             * 
             * 利用的是数学计算过程,因为是对2求余,所有有可能是0 1 两个元素,0就是二进制位数0 1就是二进制位数的1
             */
            $r = 0;
            while ($n > 0) {
                $t = $n % 2;
                $n = $n >> 1;
                if ($t == 1) {
                    $r++;
                }
            }
            return $r;
        }
    
        /**
         * @param int $a
         * @param int $b
         * @return int  $a + $b;
         */
        public static function add(int $a, int $b): int {
            $sum = $a;
            while ($b) {
                $sum = $a ^ $b;       // 不考虑进位
                $b = ($a & $b) << 1;  //  只考虑进位
                $a = $sum;
            }
            return $sum;
        }
    
        /**
         * 相反数 <= 二进制表达取反+1(补码)
         * @param int $n
         * @return int
         */
        public static function negateNumber(int $n): int {
            return self::add(~$n, 1);
        }
    
        /**
         * a-b = a + (-b)
         * @param int $a
         * @param int $b
         * @return int
         */
        public static function minus(int $a, int $b): int {
            return self::add($a, self::negateNumber($b));
        }
    
        /**
         * @param int $a
         * @param int $b
         * @return int  $a * $b
         */
        public static function multiple(int $a, int $b): int {
            $res = 0;
            while ($b) {
                if (($b & 1)) {
                    $res = self::add($res, $a);
                }
                $a <<= 1;
                $b >>= 1;
            }
            return $res;
        }
    
    //    public static function isNegative(int $n): bool {
    //        return $n < 0;
    //    }
        //-1就是负数, 0就是正数 直接右移31位即可看最左边第一个
        public static function isNegative(int $n) {
            return $n >> 31;
        }
    
        public static function maxInt(int $n) {
            return PHP_INT_MAX;
        }
    
        //交换两个int数
        public static function swap(int &$x, int &$y) {
            $x ^= $y;
            $y ^= $x;
            $x ^= $y;
        }
    
        //交换两个int数,注意返回int
        public static function average(int $x, int $y) {
            return ($x & $y) + (($x ^ $y) >> 1);
        }
    
        //判断奇偶
        public static function isOddEven(int $n) {
            return $n & 1;
        }
    
        public static function abs($x) {
            $y = $x >> 31;
            return ($x ^ $y) - $y;
        }
    
        /**
         * a/b  a = MIN_INTEGER, b!=MIN_INTEGER ?
         * @param int $a
         * @param int $b
         * @return int
         */
        public static function p(int $a, int $b): int {
    
            $x = self::isNegative($a) ? self::negateNumber($a) : $a;
            $y = self::isNegative($b) ? self::negateNumber($b) : $b;
            $res = 0;
            for ($i = 31; $i > -1; $i = self::minus($i, 1)) {
                if (($x >> $i) >= $y) {
                    $res |= (1 << $i);
                    $x = self::minus($x, $y << $i);
                }
            }
            return self::isNegative($a) ^ self::isNegative($b) ? self::negateNumber($res) : $res;
        }
    
        /**
         * @return int $a / $b
         */
        public static function pide(int $a, int $b): int {
    
            if ($b === 0) {
                throw new RuntimeException("pisor is 0");
            }
            if ($a === self::MIN_INTEGER && $b === self::MIN_INTEGER) {
                return 1;
            } else if ($b === self::MIN_INTEGER) {
                return 0;
            } else if ($a === self::MIN_INTEGER) {
                $res = self::p(self::add($a, 1), $b);
                return self::add($res, self::p(self::minus($a, self::multiple($res, $b)), $b));
            } else {
                return self::p($a, $b);
            }
        }
    
    }

    其实位运算的基础能力对于算法是很重要的,特别是在优化算法的时候,如果代码有问题进群反馈

    参考:

    https://www.cnblogs.com/kingkoo/p/6117690.html

    https://www.cnblogs.com/XuYiHe/p/4966309.html

    https://www.cnblogs.com/luowei010101/archive/2011/11/24/2261575.html

    https://www.cnblogs.com/qiaogaojian/p/5873105.html

    https://www.cnblogs.com/kiven-code/archive/2012/09/15/2686922.html

     https://blog.csdn.net/lmhacm/article/details/77287571

  • 相关阅读:
    LeetCode120 Triangle
    LeetCode119 Pascal's Triangle II
    LeetCode118 Pascal's Triangle
    LeetCode115 Distinct Subsequences
    LeetCode114 Flatten Binary Tree to Linked List
    LeetCode113 Path Sum II
    LeetCode112 Path Sum
    LeetCode111 Minimum Depth of Binary Tree
    Windows下搭建PHP开发环境-WEB服务器
    如何发布可用于azure的镜像文件
  • 原文地址:https://www.cnblogs.com/zx-admin/p/10373747.html
Copyright © 2011-2022 走看看