zoukankan      html  css  js  c++  java
  • 算法笔记-分治法

     分治法:将问题分割,从一个大块分解为一个个小块,将问题规模变小。先解决小问题,在不停的整合结果集。 

    (1)合并排序

      思路:把数组(假如长度为8)中每一个元素分为一个数组(因为这个数组只有一个元素,所以可以看成是有序数组)。然后不停的把两个有序组数组成一个有序数组(两个数组谁的第一个小就array_shift谁)。过程图:

       代码:

     1 <?php
     2 $arr = [42, 15, 20, 6, 8, 38, 50, 12];
     3 
     4 function sliceArr($arr)                 //切分
     5 {
     6     $arr = array_chunk($arr, 1);
     7     while (count($arr) != 1)
     8         $arr = array_chunk ($arr, 2);
     9     return $arr;
    10 }
    11 
    12 function mergeArr($arr)                 
    13 {
    14     if (is_array($arr[0][0])) {
    15         $arr = [mergeArr($arr[0]), mergeArr($arr[1])];
    16     }
    17     $c = count($arr[0]) + count($arr[1]);
    18     for ($i=0; $i<$c; $i++) {         //把两个有序数组合并成一个有序
    19         $ret[] = (empty($arr[1]) || (! empty($arr[0]) && $arr[0][0] < $arr[1][0])) ? array_shift($arr[0]) : array_shift($arr[1]);
    20     }
    21     return $ret;
    22 }
    23 print_r(mergeArr(sliceArr($arr, 1)));

      (2)快速排序

      思路:不停的根据数组的第一个值把数组分为三部分(大于,小于和等于这个值),然后再拼到一起。

      代码:

     1 <?php
     2 $arr = [42, 15, 20, 6, 8, 38, 50, 12];
     3 
     4 function quickSort($arr)
     5 {
     6     if (count($arr) < 2)
     7         return $arr;
     8     
     9     $max = $min = $mid = array();
    10     foreach ($arr as $v) {
    11         $v <= $arr[0] ? $v == $arr[0] ? $mid[] = $v : $min[] = $v : $max[] = $v;
    12     }
    13     return array_merge(quickSort($min), $mid, quickSort($max));
    14 }
    15 print_r(quickSort($arr));

     (3)大数相加

      思路:比如3431+235可以转换为(1+5)+(30+30)+(400+200)+(3000),可以用数组保存数组,比如3431可以表示为[1, 3, 4, 1],其中键值表示位数,比如$arr[0]表示个位(10的0次方),$arr[1]表示十位(10的1次方)。注意处理进位问题

      代码:

     1 <?php
     2 function add($n1, $n2)
     3 {
     4     list($narr1, $narr2) = is_array($n1) ? [$n1, $n2] : [splitNum($n1), splitNum($n2)];
     5     $c = max([count($narr1), count($narr2)]);
     6     for ($i=0; $i<$c; $i++) {                                                   //各位次依次相加
     7         list($res[], $carry) = ($sum = $narr1[$i] + $narr2[$i] + $carry) >= 10 ? [$sum - 10, 1] : [$sum, 0];       
     8     }
     9     $carry && $res[] = $carry;                                                  //进位
    10     return $res;
    11 }
    12 
    13 function splitNum($n)       //例如:将213转换为[3, 1, 2]
    14 {
    15     $res = [];
    16     while($n != 0) {
    17         list($res[], $n) = [$n % 10, intval($n / 10)];
    18     }
    19     return $res;
    20 }

    (4)大数相乘

      思路:比如3431*235可以转换为(3*10^3 + 4*10^2 + 3*10^1 + 1*10^0)*(2*10^2 + 3*10^1 + 5*10^1),然后乘法分配律。比如计算3*10^3 * 3*10^1,只计算3*3就可以了,转换为数组之后,数组前面填充(3+1)个0,然后再利用上面的加法,把全部项加到一起

      代码:

     1 <?php
     2 function add($n1, $n2)
     3 {
     4     list($narr1, $narr2) = is_array($n1) ? [$n1, $n2] : [splitNum($n1), splitNum($n2)];
     5     $c = max([count($narr1), count($narr2)]);
     6     for ($i=0; $i<$c; $i++) {                                                   //各位次依次相加
     7         list($res[], $carry) = ($sum = $narr1[$i] + $narr2[$i] + $carry) >= 10 ? [$sum - 10, 1] : [$sum, 0];       
     8     }
     9     $carry && $res[] = $carry;                                                  //进位
    10     return $res;
    11 }
    12 
    13 function mult($n1, $n2)
    14 {
    15     list($narr1, $narr2, $res) = [splitNum($n1), splitNum($n2), []];
    16     foreach ($narr1 as $k1 => $v1) {
    17         foreach ($narr2 as $k2 => $v2) {
    18             //array_merge后面的是相乘的结果,前面的是进位 比如200 * 80 = 2*8 000 表示为[0, 0, 0, 6, 1]
    19             $tmp = array_merge(array_fill(0, $k1 + $k2, 0), splitNum($v1 * $v2));
    20             $res = add($tmp, $res);
    21         }
    22     }
    23     return $res;
    24 }
    25 
    26 function splitNum($n)       //例如:将213转换为[3, 1, 2]
    27 {
    28     $res = [];
    29     while($n != 0) {
    30         list($res[], $n) = [$n % 10, intval($n / 10)];
    31     }
    32     return $res;
    33 }
    34 print_r(mult(3278, 29926));

      补充:也可以用字符串表示数字,然后以之前列式子的方法计算,比如:314 + 45,乘法同样(和上面不同的是,这次只是拆分了一个数;还有就是用字符串表示的并非数组):

            3  1  4

          +    4  5

              3  5  9

     1 <?php
     2 function fadd($p1, $p2) {return $p1 + $p2;}
     3 function fmul($p1, $p2) {return $p1 * $p2;}
     4 
     5 function cel($p1, $p2, $oper = 'fadd')
     6 {
     7     $p1 = str_split($p1); $p2 = str_split($p2);                     //初始化
     8     end($p1); end($p2);
     9     
    10     do {
    11         $digit = $oper(current($p1), current($p2));                 //这个函数做乘法的时候也可以用到
    12         $res = (substr($digit, -1, 1) + $over) . $res;              //个位 + 之前的进位
    13         $over = substr($digit, 0, strlen($digit) - 1);              //现在的进位
    14         prev($p1); prev($p2);
    15     } while((current($p1) !== false || current($p2) !== false) || $over);
    16     return $res;
    17 }
    18 
    19 function mul($p1, $p2)
    20 {
    21     $p1 = str_split($p1); end($p1);                                 //初始化,把p1作为被乘数(下面的那个)
    22     $len = strlen($p2);
    23     do {
    24         $res[] = cel($p2, str_repeat(current($p1), $len), 'fmul');  //根据cel的逻辑  132*5 相当于cel(132, 555, 'fmul')
    25     } while(prev($p1) !== false);
    26     
    27     foreach ($res as $k => $v) {                                    //这里把res里的值都加一起,但要注意需要乘上10的$k次方(就是在字符串后面添加k个0)
    28         $count = cel($count, $v . str_repeat('0', $k));
    29     }
    30     return $count;
    31 }
    32 
    33 $p1 = '5462'; $p2 = '56';
    34 echo cel($p1, $p2);         //相加:5518
    35 echo mul($p1, $p2);         //相乘:305872

    (5)大文件排序

      说明:如何可以把一个1G的文件排序

      思路:php的默认分配的内存是是128M,装不下1G的文件的。而且如果如果想排序,需要将其放在数组中,1G的文件转为数组结构要占据更多的空间(自测5M的文件转为数组差不多就100M了),执行sort要再多出5M左右。所以可以先将1G无序文件拆分成若干个5M有序小文件,然后再将小文件聚合。

      代码:

     1 <?php
     2 ini_set('max_execution_time', 6000);
     3 $root = 'D:/wangjianheng/';
     4 $sort_files_folder = 'sort_files/';
     5 $nums_filse_name = 'num.txt';                           //总大小约1.1G, 共212100210行
     6 $limit = 1024000;                                       //php分配内存一般是128M, limit行放到数组中总共大概100M
     7 $sort_file_name = "sort_file%d.txt";
     8 $sort_file_num = 1;
     9 $res_file_name = 'res.txt';
    10 $step = 0;
    11 
    12 $fp = fopen($root . $nums_filse_name, "r");
    13 while(! feof($fp)) {                                    //拆分成若干个排序好的文件
    14  $nums[] = (int) fgets($fp);
    15  if ($step++ > $limit) {
    16      $step = 0;
    17      rsort($nums);                                       //排序大概额外需要4M
    18      writeText(sprintf($sort_files_folder . $sort_file_name, $sort_file_num++), $nums);
    19  }
    20 }
    21 
    22  if ($nums) {                                           //留了点尾巴
    23      rsort($nums);
    24      writeText(sprintf($sort_files_folder . $sort_file_name, $sort_file_num++), $nums);
    25  }
    26 fclose($fp);
    27 
    28 $sort_files = scandir($root . $sort_files_folder);
    29 foreach ($sort_files as $sort) {
    30     if (strpos($sort, 'sort_file') !== false) {
    31         $fp_obj = new stdClass();
    32         $fp_obj->fp = fopen($root . $sort_files_folder . $sort, 'r');
    33         $fp_obj->current = (int) fgets($fp_obj->fp);
    34         $fps[] = $fp_obj;
    35     }
    36 }
    37 
    38 $res_fp = fopen($root . $res_file_name, 'w');           //开始把已排序文件聚合
    39 while ($fps) {
    40     foreach ($fps as $k => & $fp) {
    41         if (feof($fp->fp)) {
    42             fclose($fp->fp);
    43             unset($fps[$k]);
    44             continue;
    45         }
    46         if (! isset($max) || $fp->current > $max->current) {
    47             $max = & $fp;                               //取一个最大值
    48         }
    49     }
    50     fwrite($res_fp, (string) $max->current . "
    ");
    51     $max->current = (int) fgets($max->fp);
    52     unset($max);
    53 }
    54 fclose($res_fp);
    55 
    56 function writeText($fileName, & $nums)
    57 {
    58     $root = 'D:/wangjianheng/';
    59     $fp = fopen($root . $fileName, "w");
    60     foreach ($nums as $num) {
    61         fwrite($fp, $num . "
    ");
    62     }
    63     $nums = [];
    64 }
  • 相关阅读:
    [代码]codeforces 150c Smart Cheater
    [存档]Young Tableau
    [转载][毁童年]种太阳
    [转载]教练,我也想再要一个同桌
    静夜
    CodeProject每日精选: Applications
    设计模式 装饰模式(Decorator) 精选经验合集
    槟榔味儿办公室
    盗不盗非常盗
    月末之周末
  • 原文地址:https://www.cnblogs.com/wangjianheng/p/11720375.html
Copyright © 2011-2022 走看看