免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5392 | 回复: 15
打印 上一主题 下一主题

位编码算法的类(分类算法) [复制链接]

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2003-11-27 11:04 |只看该作者 |倒序浏览
按照前段时间说过的位编码算法实现了一个类( http://www.chinaunix.net/forum/27/20031122/207556.html ),放出来大家看看,大家帮我改进。


  1. <?php
  2. Class BinTree
  3. {
  4.     //树结构
  5.     /*************************************
  6.     example:
  7.     $Structure = array(4,7,7,7,7);
  8.     树有4层,第一层4位编码,第二、第三、第四、第五层7位编码
  9.     ************************************/
  10.     var $Structure;
  11.    
  12.     /// 存储树的每层信息的数组
  13.     /*************************************
  14.     //特征码
  15.     //2^N-2^(N-(N1+N2+…+Ni))
  16.     example:
  17.     $Layer = array(   层的二进制编码长度       层的特征码       每一层每节点的公差
  18.                 1 =>; array('len' =>; 4, 'mark' =>; 4026531840, 'tolerance' =>; 268435456),
  19.                 2 =>; array('len' =>; 7, 'mark' =>; 4292870144, 'tolerance' =>; 2097152),
  20.                 3 =>; array('len' =>; 7, 'mark' =>; 4294950912, 'tolerance' =>; 16384),
  21.                 4 =>; array('len' =>; 7, 'mark' =>; 4294967168, 'tolerance' =>; 128),
  22.                 5 =>; array('len' =>; 7, 'mark' =>; 4294967295, 'tolerance' =>; 1)
  23.                 );
  24.     *************************************/
  25.     var $Layer;
  26.    
  27.     //二进制编码总长度
  28.     var $binMarkLen;
  29.    
  30.     //现在用来分析的目标节点,十进制
  31.     var $_decCurrentNode;
  32.    
  33.     //现在用来分析的目标节点,二进制
  34.     var $_binCurrentNode;
  35.    
  36.     //CurrentNodeID的所属的层
  37.     var $_CurrentLayer;
  38.    
  39.     // 初始化树
  40.     function BinTree($Structure)
  41.     {
  42.         $this->;binMarkLen = array_sum($Structure);
  43.         
  44.         $tmpLen = 0;
  45.         foreach ( $Structure as $k =>; $v )
  46.         {
  47.             $this->;Layer[$k+1]['len'] = $v;
  48.             
  49.             $tmpLen += $v;
  50.             $this->;Layer[$k+1]['mark'] = pow(2, $this->;binMarkLen) - pow(2, $this->;binMarkLen - $tmpLen);
  51.             $this->;Layer[$k+1]['tolerance'] = pow(2, $this->;binMarkLen - $tmpLen);
  52.         }
  53.         
  54.         return true;
  55.     }
  56.    
  57.     // 解析当前节点
  58.     function _parseNode($Node)
  59.     {
  60.         $this->;_decCurrentNode = $Node;
  61.         //节点二进制编码
  62.         $this->;_binCurrentNode = FillBit(base_convert($Node, 10, 2), $this->;binMarkLen);
  63.         
  64.         //节点处于树的第几层
  65.         $start = 0;
  66.         foreach ( $this->;Layer as $k =>; $v )
  67.         {
  68.             $binMark = substr($this->;_binCurrentNode, $start, $v['len']);
  69.             $testMark = str_repeat('0', $v['len']);
  70.             $start += $v['len'];
  71.             
  72.             if ( $binMark == $testMark )
  73.             {
  74.                 $this->;_CurrentLayer = $k - 1;
  75.                 break;
  76.             }
  77.             $this->;_CurrentLayer = count($this->;Layer);
  78.         }
  79.     }
  80.    
  81.     // 清除上次解析的结果
  82.     function _unparseNode()
  83.     {
  84.         $this->;_decCurrentNode = null;
  85.         $this->;_binCurrentNode = null;
  86.         $this->;_CurrentLayer = null;
  87.     }
  88.    
  89.     // 获得父节点
  90.     // 如果$all不为空则以数组格式返回所有父节点,否则仅返回上一层节点
  91.     function getParent($Node, $all = '')
  92.     {
  93.         if ( $this->;_decCurrentNode != $Node )
  94.         {
  95.             $this->;_parseNode($Node);
  96.         }
  97.         
  98.         if ( $this->;_CurrentLayer == 1 )
  99.         {
  100.             $ParentNode = 0;
  101.         }
  102.         else
  103.         {
  104.             if ( $all )
  105.             {
  106.                 for ( $i = 1; $i < $this->;_CurrentLayer; $i++ )
  107.                 {
  108.                     $ParentNode[$i] = $Node & $this->;Layer[$i]['mark'];
  109.                 }
  110.             }
  111.             else
  112.             {
  113.                 $ParentNode = $Node & $this->;Layer[$this->;_CurrentLayer - 1]['mark'];
  114.             }
  115.         }
  116.         
  117.         return $ParentNode;
  118.     }
  119.    
  120.     // 获得下级子节点值
  121.     // 如果$j没有赋值则返回所有的子节点
  122.     function getChild($Node, $j = '')
  123.     {
  124.         if ( $this->;_decCurrentNode != $Node )
  125.         {
  126.             $this->;_parseNode($Node);
  127.         }
  128.         
  129.         if ( $this->;_CurrentLayer == count($this->;Layer) )
  130.         {
  131.             return false;
  132.         }
  133.         else
  134.         {
  135.             $ChildLayer = $this->;_CurrentLayer + 1;
  136.             
  137.             if ( $j >; 0 )
  138.             {
  139.                 if ( ($j >; 0 ) and ($j < pow(2, $this->;Layer[$ChildLayer]['len'])) )
  140.                 {
  141.                     $ChildNode = $this->;Layer[$ChildLayer]['tolerance'] * $j + $Node;
  142.                 }
  143.                 else
  144.                 {
  145.                     return false;
  146.                 }
  147.             }
  148.             else
  149.             {
  150.                 for ( $i = 1; $i < pow(2, $this->;Layer[$ChildLayer]['len']); $i++ )
  151.                 {
  152.                     $ChildNode[] = $this->;Layer[$ChildLayer]['tolerance'] * $i + $Node;
  153.                 }
  154.             }
  155.         }
  156.         
  157.         return $ChildNode;
  158.     }
  159.    
  160.     // 得到同一层的下一个节点
  161.     function getNextNode($Node)
  162.     {
  163.         if ( $this->;_decCurrentNode != $Node )
  164.         {
  165.             $this->;_parseNode($Node);
  166.         }
  167.         $thisLayer = $this->;_CurrentLayer;
  168.         
  169.         $ParentNode = $this->;getParent($Node);
  170.         $this->;_unparseNode();
  171.         
  172.         $j = ($Node - $ParentNode)/$this->;Layer[$thisLayer]['tolerance'];
  173.         
  174.         if ( $j+1 < pow(2, $this->;Layer[$thisLayer]['len']) )
  175.         {
  176.             return $this->;Layer[$thisLayer]['tolerance'] + $Node;
  177.         }
  178.         else
  179.         {
  180.             return false;
  181.         }
  182.     }
  183. }

  184. Class DBBinTree extends BinTree
  185. {
  186.     //树实例所在的表
  187.     var $Table;
  188.    
  189.     //树实例所在的字段
  190.     var $Column;
  191.    
  192.     // 初始化
  193.     function DBBinTree($Structure, $table, $column)
  194.     {
  195.         parent::BinTree($Structure);
  196.         
  197.         $this->;Table = $table;
  198.         $this->;Column = $column;
  199.         
  200.         return true;
  201.     }
  202.    
  203.     // 获得父节点
  204.     // 如果$all不为空则以数组格式返回所有父节点,否则仅返回上一层节点
  205.     function SelectParent($Node, $all = '')
  206.     {
  207.         global $db;
  208.         
  209.         $ParentNode = $this->;getParent($Node, $all);
  210.         
  211.         if ( is_array($ParentNode) )
  212.         {
  213.             $sql = "select * from ". $this->;Table ." where ". $this->;Column ." in (". implode(',', $ParentNode) .")";
  214.         }
  215.         else
  216.         {
  217.             $sql = "select * from ". $this->;Table ." where ". $this->;Column ." = ". $ParentNode;
  218.         }
  219.         
  220.         $rowset = $db->;getAll($sql);
  221.         if ( !$db->;isError($rowset) )
  222.         {
  223.             return $rowset;
  224.         }
  225.         else
  226.         {
  227.             return false;
  228.         }
  229.     }
  230.    
  231.     // 获得子节点
  232.     // 如果$all不为空则以数组格式返回所有子节点,否则仅返回下一层节点
  233.     function SelectChild($Node, $all = '')
  234.     {
  235.         global $db;
  236.         
  237.         $this->;_parseNode($Node);
  238.         
  239.         $sql = "select * from ". $this->;Table ." where ". $this->;Column ." >; ". $Node ." and ". $this->;Column ." < ". $this->;getNextNode($Node);
  240.         
  241.         $res = $db->;query($sql);
  242.         if ( !$db->;isError($res) )
  243.         {
  244.             if ( $all )
  245.             {
  246.                 while ( $row = $res->;fetchRow() )
  247.                 {
  248.                     $rowset[] = $row;
  249.                 }
  250.             }
  251.             else
  252.             {
  253.                 $Child = $this->;getChild($Node);
  254.                
  255.                 while ( $row = $res->;fetchRow() )
  256.                 {
  257.                     if ( in_array($row[$this->;Column], $Child) )
  258.                     {
  259.                         $rowset[] = $row;
  260.                     }
  261.                 }
  262.             }
  263.             
  264.             return $rowset;
  265.         }
  266.         else
  267.         {
  268.             return false;
  269.         }
  270.     }
  271. }
  272. ?>;
复制代码

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
2 [报告]
发表于 2003-11-27 11:11 |只看该作者

位编码算法的类(分类算法)

测试数据库:
INSERT INTO bintree (binMark,decMark) VALUES ('01000000000000000000000000000000',1073741824);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000000000000000000000',1092616192);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101000000000000000',1092780032);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000000000',1092787200);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001000',109278720;
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101100000000000000',1092796416);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000110000000000000000',1092812800);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000110100000000000000',1092829184);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000111000000000000000',109284556;
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000111100000000000000',1092861952);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110010000000',109278732;
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110100000000',1092787456);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110110000000',1092787584);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001111000000000',1092787712);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001111010000000',1092787840);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001111100000000',109278796;
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001111110000000',1092788096);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001001',1092787209);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001010',1092787210);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001011',1092787211);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001100',1092787212);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001101',1092787213);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001110',1092787214);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000001111',1092787215);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101001110000010000',1092787216);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101000000010000000',1092780160);
INSERT INTO bintree (binMark,decMark) VALUES ('01000001001000101011111110000000',109279628;
longnetpro 该用户已被删除
3 [报告]
发表于 2003-11-27 18:59 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
4 [报告]
发表于 2003-11-27 19:01 |只看该作者

位编码算法的类(分类算法)

这种算法就是建立在事先规划好树的容量的基础上,层数和每层的数量事先都要有数,所以不适合那种无限分层的树
这种树结构比较死一点,我另外想了一个灵活些的树结构,打算先在我的项目里用用,如果好的话,再推荐给大家
longnetpro 该用户已被删除
5 [报告]
发表于 2003-11-27 21:13 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
6 [报告]
发表于 2003-11-27 21:45 |只看该作者

位编码算法的类(分类算法)

其实也不是什么很新鲜的主意,就是普通的树加一个辅助字段而已,是在这个问题的讨论中吸收了别人的意见的结果。
node_id   parent    code
1             0             01
2             1             0102
3             2             010203
我分析了一下,如果层非常多的话,位编码的方式就不可行了,因为位是不定长的,只有普通的那种树结构能够轻松满足。但是普通的那种树最大的毛病就是基于递归的遍历,所以想到加一个辅助字段来解决这个递归的毛病。
code字段其实就是路径信息,但是采用36进制,以2位为一层的话,每层可以容纳(36^2)-1也就是1295个节点,假设code字段用varchar(255)的话,可以容纳127层36^(254)-1个节点,容量非常大了,当然很少会用到这么大的树,如果真要处理这么大的树,肯定需要更好的方法。
采用36进制以后,占用的存储空间相对小,编码比较短,在采用like查询子节点的时候效率会比用低进制编码存储的路径效率要高,而且我敢肯定,效率肯定是比递归高的。并且不是每次对树的查询都需要对这个字段查询,比如只是想查上一级节点,同一层节点,下一层节点就可以直接的利用原来的方法。
这种方式适用于不能确定长度的树,我觉得一般的情况来说,我不会碰到特别大的树,一般不会超过4到5层。根据不同的情况,这两种树结合着用就差不多了。
具体怎么样,我也不好说,所以打算先写出来自己用用才真正清楚效果,既然老兄你问到了,我就说说,请指正。
longnetpro 该用户已被删除
7 [报告]
发表于 2003-11-27 22:16 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
8 [报告]
发表于 2003-11-27 22:45 |只看该作者

位编码算法的类(分类算法)

你的意见我想想,另外我有两点要说:
采用36进制是因为我用base_convert来换算进制,最高支持到36位,再高就不晓得怎么处理了,呵呵,longnetpro教我。
另外我发觉刚才在每层节点的容量上的记算是错误的,因为node_id是在数据库里用了auto_increment来实现的,因此,node_id是不可重复的,所以,编码里肯定不会出现0101或者0a0a这样的编码,这样实际上大大的降低了每层的容量,而且树越大,这个影响越明显,所以不能处理太大的树,不同的编码长度对应的上限之间的关系我还没有计算出来,估计应该是在可接受范围内的,太大的树现在不在考虑范围。

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
9 [报告]
发表于 2003-11-27 22:50 |只看该作者

位编码算法的类(分类算法)

呵呵,刚发完上一个帖子就想清楚上限了。
编码的进制是m
每层编码的长度是n
那么整个树的总节点容量上限是(m^n)-1,树的层数理论上限也是(m^n)-1,所以如果要采用这种方法的话,事先也要先计划好容量的。

论坛徽章:
0
10 [报告]
发表于 2003-11-27 23:14 |只看该作者

位编码算法的类(分类算法)

恩~~~看看~~~

能不能改成16进制编码,这样编码比较短~~~
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP