• 获取集合的所有子集


    前言

    今天看到一个有意思的问题,要获取一个集合的所有子集,使用PHP语言表示的话,就是要找到一个数组的所有子集数组,如[1,2,3],结果应该是

    [1]
    [2]
    [3]
    [1,2]
    [1,3]
    [2,3]
    [1,2,3]
    

    PHP代码

    function powerSet($in,$minLength = 1) {
        $count = count($in);
        $members = pow(2,$count);
        $return = [];
        for ($i = 1; $i < $members; $i++) {
            $b = sprintf("%0".$count."b",$i);
            $out = [];
            for ($j = 0; $j < $count; $j++) {
                if ($b{$j} == '1') $out[] = $in[$j];
            }
            if (count($out) >= $minLength) {
                $return[] = $out;
            }
        }
        return $return;
    }
    

    上面代码的巧妙之处在于通过二进制的方式来解决问题。拿[1,2,3]来举例,一共有3个数字,构造子集就是从数组中拿出若干数字组成数组。3个数字可以看成二进制的3位,一共有如下情况

    001  ---  1
    010  ---  2
    011   --- 3
    100  ---  4
    101  ---  5
    110  ---  6
    111   --- 7
    

    可以看出我们只要把二进制表示方式中,找出所有「1」所在位置在数组中对应的数字找出来就是所有子集了。比如[1,2,3]对应关系如下

    001  ---  [3]
    010  ---  [2]
    011   --- [2,3]
    100  ---  [1]
    101  ---  [1,3]
    110  ---  [1,2]
    111   --- [1,2,3]
    

    这样就找到了所有子集了。sprintf("%0".$count."b",$i);就是把1 - \(2^n\)用二进制方式表示出来,$b{$j} == '1'就是找出1所在的数组索引下标。

    代码优化

    上面的代码有可以优化的地方,如下

    function powerSetUpgrade($in,$minLength = 1) {
        $count = count($in);
        $members = pow(2,$count);
        $return = [];
        for ($i = 1; $i < $members; $i++) {
            $out = [];
            for ($j = 0; $j < $count; $j++) {
                if ($i>>$j&1) $out[] = $in[$j];
            }
            if (count($out) >= $minLength) {
                $return[] = $out;
            }
        }
        return $return;
    }
    

    与上面代码的区别有2点

    • 去掉了$b = sprintf("%0".$count."b",$i);
    • 把取1的判断条件由$b{$j} == '1'变成$i>>$j&1
      通过位运算符>>&来找出「1」所在位置下标,这种方法比上面的性能要好,但是不是很好理解。还拿[1,2,3]举例,对应关系如下
    001  ---  [1]
    010  ---  [2]
    011   --- [1,2]
    100  ---  [3]
    101  ---  [1,3]
    110  ---  [2,3]
    111   --- [1,2,3]
    

    这种方式从对应的关系来看不是很直观,和正常的顺序是相反的,比如上面的方法001找到的是第3个数字「3」,很直观,但是通过位运算符001找到的却是第1个数字「1」,不过最终结果是一样的。

    参考

  • 相关阅读:
    C#值类型和引用类型的不同
    C# new用法总结-转
    C#中New关键词的几种用法
    命令行客户端MySQL的使用(1)——Python
    数据库的基本使用——MySQL
    生成器的创建方式——Python
    with与“上下文管理器”——Python
    魔法属性——Python
    property属性——Python
    正则匹配之贪婪和非贪婪——Python
  • 原文地址:https://www.cnblogs.com/whyly/p/16149331.html
Copyright © 2020-2023  润新知