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

  • 相关阅读:
    梯度下降法实现python[转载]
    PAT Maximum Subsequence Sum[最大子序列和,简单dp]
    PAT Sign In and Sign Out[非常简单]
    PAT 1015 Reversible Primes[求d进制下的逆][简单]
    outlook 召回邮件 (zz)
    The Microsoft.Jet.OLEDB.4.0 provider is not registered on the local machine (zz)
    Determine Microsoft Database AccessEngine Version (zz)
    企业的十三中死法
    c#事件学习
    20071017我们的新家
  • 原文地址:https://www.cnblogs.com/zx-admin/p/10373747.html
Copyright © 2011-2022 走看看