【算法】几个数组拆分题的算法(动态规划,矩阵递归和同余问题)

鸽了几个月,终于更了哈哈哈(确实本人懒癌晚期,往往就不更了)

这个学期虽然算是最鸽的一个学期,不过也有些东西,总共一学期做过的三次数组拆分问题感觉上已经够了一篇很不错的文章,其实就有的时候就懒得更了

下面的内容讲了三个数组的拆分问题,大概算是我这一学期在MATLAB课上弄的最多的内容了吧(虽说我在选之前这方面就已经很好了,但是为了在算法上面有一些提高,所以就报了这门课,虽说感觉大部分操什么的都直接水过去了,但是毕竟在算法上面有了一些新的思考和实战经验)

另外,因为作者的MATLAB不支持英文,所以作者手动把自己写的注释帮你们翻译成中文的,这个工作量也不小)

几个文件脚本的百度网盘下载链接(这几个脚本都是英文的):

链接:MATLAB英文脚本
提取码:r40j

另外,我的Matlab版本是2019a,如果用太低的版本比如2014,有可能导致不兼容问题

我们进入正题:

目录

一、动态规划数组拆分问题(背包问题)

(1)动态规划内容讲解

(2)代码实现背包算法解决拆分整数数组问题

二、矩阵递归遍历思想解决浮点数组拆分

(1)浅说一下为什么用遍历而不是用其它方法

(2)矩阵递归思路讲解

(3)代码实现

三、同余数组拆分问题的求解方法

(1)原理讲解

(2)代码实现


一、动态规划数组拆分问题(背包问题)

(1)动态规划内容讲解

题目: 有一个数组,数组中的数字都是整数且范围不太大,把它拆分为两个数组,两个数组可以大小不同,要求拆分得到的两个数组元素之和的差尽可能小(就是找出拆分方式中所得两个数组A,B让sum(A)-sum(B)最小,并且求出这个最小值)

当然了,这样的拆分方法有多种可能,但是我们只需要找到差值最小的拆分方式

如果使用遍历的方法解决这类问题,那么其时间开销可能是不能负担的,于是,为了不使用遍历的方法去解决这个拆分数组问题,我们考虑通过使用动态规划算法来减少运算量,将数组拆分问题转化为背包问题。

动态规划算法的基本概念可以参考这篇文章:教你彻底学会动态规划——入门篇_ChrisYoung1314的博客-CSDN博客_动态规划

或者B站上的动态规划视频:我参考的是这个:10分钟彻底搞懂“动态规划”算法

在这里简单介绍动态规划的概念:动态规划是一种通过空间换取时间的算法,其基本思路是使用表格记录已经运算完的运算结果,然后在之后运算时,如果可以用到已经算过的结果,则直接从空间中取得。

我们先对背包问题进行一定的分析:一个小偷有一固定重量的背包,他可以从博物馆拿走一些东西,不同的物品有不同的重量和价值(重量都是整数),小偷希望尽可能得到更多价值的物品,因此需要找到一种最佳的方案,在背包容量有限的情况下装入最多价值的物品。

现在我们讲解利用背包算法的步骤:

假设一些物品的重量分别为 w = [ 2 1 2 3 4 5],价值分别为 v = [ 4 3 4 5 7 9], 那么我们可以建立如下表格,其中,表格每一个单元格表示在相应背包条件下能装入的最大价值

我们从背包容量1~6分别讨论背包容量不同情况下的价值,对于第一个物品(w = 2,v = 4),如果背包容量小于2时,显然装不下第一个物品,那么,背包得到的最大价值是0,当背包容量大于2时,可以装下,则最大价值是4

 ​​

 对于第二个物品,在探讨时,我们分“能装下”和“装不下”两种情况讨论。

在填写第二行时,如果连这个物品都装不下,显然,背包所能装的价值和上一行的对应位置相同。(举个例子,比如假设第二个物品重量是3,那么在背包容量为1和2时,由于装不进这个物品,那么填的必然和上一行一样,分别是0和4),如下图绿框部分所示,对于绿框部分,由于背包容量连这个物品都装不下,那么显然背包能够装入的最大价值即为前面那次尝试得到的最大价值,所以直接继承上一行的就好。因此,第3行的第1格,由于物品重量为2,直接继承上一行得到3,第4行的第1,2格也是直接继承上一行的。

        当背包容量大于等于讨论需要装入的物品的重量时,我们讨论两种情况,一种是装入这个物品,另一种是不装这个物品。

在背包“不装这个物品”时,显然,背包得到的最大价值与上一行的相同,而在背包“装这个物品”时,所得到的最大价值可以由此计算:   用    物品本身的价值 + 背包装下这个物品后剩余容量能装的最大价值。

如上图,在装第三个物品时,第一格装不下,继承上面的,第二格可以装的下,于是我们比较(1)不装的价值->继承上一行得到4 (2)装的价值->物品本身的价值是4,加上剩余容量为0时装的价值,得到的是4,取最大值4得到背包容量为2时的最大价值4

第三个物品的第三格为例,不装的价值是7,装的价值用物品的价值4加上上一行背包容量为1时的价值,得到的是7,两者相等,以此类推填写完成整个表格

填写完成的表格如下:

下面来看背包问题的Matlab代码:

在代码中,我们通过定义一个矩阵B来存储我们填写表格中的每一个值,

w = [ 2 1 2 3 4 5];   % 每个物品的重量
v = [ 4 3 4 5 7 9];    % 物品的价值

BagCapacity = 6;  % 背包容量为6

B = zeros( length(w) , BagCapacity ) ;  % 建立矩阵B存储我们需要填写的表格

% 填写表格

for i = 1 : length(w)
    for j = 1:BagCapacity
        if i==1    % 对于第一行,直接填写,无需判断。当背包容量小于第一个物品的重量时,填0,其余填1
            if j<w(i)  
                B(i,j) = 0;
            else
                B(i,j) = v(i);
            end
        else   % 填写其它的行
            
            if j < w(i)  % 这个是对于装不下的情况(列数j小于物品的重量,装不下物品)
                B(i,j) =  B(i-1,j) ;   % 直接继承上一行
            else
                % 在能装下这个物品时,我们需要比较装和不装的两个价值
                if j==w(i)   % 刚好装下 -> 比较这个物品和上一行的价值
                    if v(i) > B(i-1,j)
                        B(i,j) = v(i) ;
                    else
                        B(i,j) = B(i-1,j) ;   % 继承上一行的内容
                    end
                else    % 比较上一行的不装物品和装下物品并加上剩余容量
                    v_1 = v(i) + B(i-1, j - w(i) ) ;
                    v_2 = B(i-1,j) ;
                    if v_1 > v_2
                        B(i,j) = v_1;
                    else 
                        B(i,j) = v_2;
                    end
                end
            end
        end
    end
end

B

我们运行上述脚本后,得到矩阵B,那么,显然矩阵B右下角的12即为背包容量为6时装得下的最大价值

如果我们需要记录最佳选择组合,只需要使用一个字符串数组来记录我们选择的最佳组合即可

w = [ 2 1 2 3 4 5];
v = [ 4 3 4 5 7 9];

BagCapacity = 6; 

B = zeros( length(w) , BagCapacity ) ;  % 建立矩阵B存储我们需要填写的表格
Choice  = repmat("",length(w) , BagCapacity);

% 填写表格

for i = 1 : length(w)
    for j = 1:BagCapacity
        if i==1    % 对于第一行,直接填写,无需判断。当背包容量小于第一个物品的重量时,填0,其余填1
            if j<w(i)  
                B(i,j) = 0;  % 不选,不改变Choice数组
            else
                B(i,j) = v(i);
                Choice(i , j) = num2str(i);
            end
        else   % 填写其它的行
            
            if j < w(i)  % 这个是对于装不下的情况(列数j小于物品的重量,装不下物品)
                B(i,j) =  B(i-1,j) ;   % 直接继承上一行
                Choice(i , j) = Choice(i-1 , j);
            else
                % 在能装下这个物品时,我们需要比较装和不装的两个价值
                if j==w(i)   % 刚好装下 -> 比较这个物品和上一行的价值
                    if v(i) > B(i-1,j)
                        B(i,j) = v(i) ;
                        Choice(i , j) = num2str( i ) ;
                    else
                        B(i,j) = B(i-1,j) ;   % 继承上一行的内容
                        Choice(i , j) = Choice( i-1 , j );
                    end
                else    % 比较上一行的不装物品和装下物品并加上剩余容量
                    v_1 = v(i) + B(i-1, j - w(i) ) ;
                    v_2 = B(i-1,j) ;
                    if v_1 > v_2
                        B(i,j) = v_1;
                        Choice(i,j) = join(  [ Choice(i-1, j-w(i) ) , num2str(i) ], ',');
                    else 
                        B(i,j) = v_2;
                        Choice(i,j) = Choice( i-1, j ) ;
                    end
                end
            end
        end
    end
end

% 然后通过拆分字符串,得到我们想要的值。
best_choice = str2double(   split    (Choice( length(w) , BagCapacity),','))' ;

index = length(best_choice);

while index>= 1   
    if isnan(best_choice(index))  % 有可能拆分出NaN
        best_choice(index) = [];
    end
    index = index -1;
end

 best_choice = sort(best_choice,'descend')
 Best_Value = B( length(w),BagCapacity)

其中,4,2,1是下标,选择第1,2, 4个物品,显然总容量为6,总价值为12,达到最大。

下面我们只需要稍微改改代码,就可以实现整数数组的差最小拆分问题

(2)代码实现背包算法解决拆分整数数组问题

再加一遍题目:

对于一个任意的正整数数组(负整数我没怎么管,应该也行),我们要将它拆分为两个数组并使它们的差值达到最小,即一个数组内的数值尽可能接近平均数,那么可以简化为重量和价值相等的背包问题,这样,只需要将背包的容量设置为数组总和的平均值,就可以让选择后的结果尽可能地接近于平均值

w = [ 2 1 2 3 4 5];
v = w ;

BagCapacity = ceil(sum(w)/2);  % 数组的求和除以2然后向上取整

B = zeros( length(w) , BagCapacity ) ;  % 建立矩阵B存储我们需要填写的表格
Choice  = repmat("",length(w) , BagCapacity);

% 填写表格

for i = 1 : length(w)
    for j = 1:BagCapacity
        if i==1    % 对于第一行,直接填写,无需判断。当背包容量小于第一个物品的重量时,填0,其余填1
            if j<w(i)  
                B(i,j) = 0;  % 不选,不改变Choice数组
            else
                B(i,j) = v(i);
                Choice(i , j) = num2str(i);
            end
        else   % 填写其它的行
            
            if j < w(i)  % 这个是对于装不下的情况(列数j小于物品的重量,装不下物品)
                B(i,j) =  B(i-1,j) ;   % 直接继承上一行
                Choice(i , j) = Choice(i-1 , j);
            else
                % 在能装下这个物品时,我们需要比较装和不装的两个价值
                if j==w(i)   % 刚好装下 -> 比较这个物品和上一行的价值
                    if v(i) > B(i-1,j)
                        B(i,j) = v(i) ;
                        Choice(i , j) = num2str( i ) ;
                    else
                        B(i,j) = B(i-1,j) ;   % 继承上一行的内容
                        Choice(i , j) = Choice( i-1 , j );
                    end
                else    % 比较上一行的不装物品和装下物品并加上剩余容量
                    v_1 = v(i) + B(i-1, j - w(i) ) ;
                    v_2 = B(i-1,j) ;
                    if v_1 > v_2
                        B(i,j) = v_1;
                        Choice(i,j) = join(  [ Choice(i-1, j-w(i) ) , num2str(i) ], ',');
                    else 
                        B(i,j) = v_2;
                        Choice(i,j) = Choice( i-1, j ) ;
                    end
                end
            end
        end
    end
end

% 然后通过拆分字符串,得到我们想要的值。
best_choice = str2double(   split    (Choice( length(w) , BagCapacity),','))' ;

index = length(best_choice);

while index>= 1   
    if isnan(best_choice(index))  % 有可能拆分出NaN
        best_choice(index) = [];
    end
    index = index -1;
end

 best_choice = sort(best_choice,'descend');
 Best_Value = B( length(w),BagCapacity);
 
 a = w(best_choice);  % 使用w(bestchoice)来取得拆分后的数组
 b = w ;  % 第二个数组从数组w中删除数组a中的元素
 
 for i = 1 : length(best_choice)
     b(best_choice(i)) = [];
 end
 
 mmin = abs(sum(a) - sum(b));
 disp(a)
 disp(b)
  

结果如下: 

可以看出第一个数组的和为9而第二个数组的和为8,达到了拆分的目的

下面我们来考虑另一种数组的拆分问题,如果是对于浮点数的数组拆分问题,那么背包问题的解决将变为重量和背包容量均为浮点型的,显然其中没有规律的间距,因此无法通过传统的背包办法解决问题

二、矩阵递归遍历思想解决浮点数组拆分

首先这个方法是在我们的一种假设认识下进行的:即我们采用矩阵直接进行运算相加的速度比单个元素分别相加的速度要快很多,即:定义矩阵A,B,则在MATLAB中,直接A+B相较于使用for循环将B元素分别加给A上是快很多的,因此我们考虑在编程中,尽可能地使用矩阵,避免元素上的操作来加快运算速度

首先这个办法毕竟是递归,由于这种遍历办法本身具有阶乘级别的复杂度,所以不能解决数组长度过大的问题(这种方法对于数组长度为10基本上是一两秒就出来,大概数组长度为11时需要20秒左右,数组长度为12就需要很长时间,还不能记录两个拆分出来的数组),所以具有其比较大的缺陷,但是毕竟是自己想的,思路可以用来借鉴:

(1)浅说一下为什么用遍历而不是用其它方法

首先我讲一下为什么我使用遍历方法而不去试图优化更好的思路(这主要是我试了几种其它的方法都失败了):

对于浮点数的拆分问题,我们可以将它想成一个凑平均值的问题。

我们以下面一个较为简单的数组为例:

 其求和是22.8,则均值为11.4,我们虽然可以考虑将所需要拆分的数组排序之后,从最小的开始凑,然后尽量接近平均值,但是这样很容易导致快到平均值时,加上一个数又大于平均值了,然后为了取得小于平均值的较大的数,就需要考虑从结果中减去某些值,但是由于浮点数的原因,无法确定是应该减去一个较大的数还是减去几个较小的数能获得更好的结果,所以这样又导致前面的部分需要进行遍历。

当然,如果获得已经大于平均值的值,那么显然不用继续遍历(再加数也不会得到更大的值)(当然,对于有负数的情况,可能还是需要继续考虑这种情况,遍历下去)

我也曾经考虑过去凑大于平均值的最小的数,但是如果想要必然得到的是最优解,你无法确定是改凑一个更大的后方的数再使用前面几个较小的数来凑,或者后边的小一点然后前面使用较大的一个或任意几个数。

        另外,假如通过动态规划来进行求解,那么,我们需要知道前面的一组数任意组合得到的所有解。那相当于对前面所有的组合进行了一次遍历。

       而且,由于浮点数的不确定性,前面数的所有不同组合加上新的数,都有可能成为最接近平均值的那一个。所以综上所述,遍历在浮点数组拆分的问题中是难以避免的,因此我考虑使用更加优化的遍历算法来求解浮点数组的拆分问题。

(2)矩阵递归思路讲解

        我们以这个数组为例(6*1的数组):

对于浮点数的组合相加,我们要遍历递归所有可能的拆分出的数组的数字各个组合,因此,我们首先考虑所有可能的两个数的浮点数之和:

这个和可以由矩阵来进行表示

        这个成为一个5x5的矩阵,比原先的6个减去了一行

         那么,所有三个数字的和,也可以由上面两个矩阵的和获得,这样就相当于是利用了前一步解决的问题,类似于动态规划的思路,减少了运算量。

        接下来,我们考虑所有3个数字的所有可能和:由于三个数的可能的和可以由前面的两个数字的可能和来表示:

        因为每两个数字都有新的数字组合,我们每次取上面的一行,进行构造矩阵,如下图所示:

则第一次取第一行[3.5 , 4.1 , 6.3 , 7.1]构造矩阵,第二次取[2.5+3.1, .....]构造矩阵,则所有三个数的和可以如下表示

相似地,所有4个数的和,都可以取上方所有矩阵的行(行内元素>=2)再次构造矩阵,表示所有4个数的和

        由于上面的矩阵构造方式很类似,这启发我们定义一个函数来定义一种新的矩阵构造方式,即:

我们保留原来的向量[1,2.5 ......6.1],然后进行截取(写字不好看哈)

 

为了使用矩阵进行操作,我们简化构造方式为利用如图矩阵方式构造:

 所以我们定义我们需要的构造矩阵函数如下:

 运行结果(是由[1 2.5 3.1 ....6.1]和[2.5 3.1 ....6.1]构造的矩阵),显然符合我们的要求

        在这里面的一个编程难点就是矩阵的递归,矩阵递归的基本思想,就是每次利用矩阵的每一行(除最后一行)按我们定义的方法构造一个矩阵(比如得到了2个数的和),如果这个矩阵的的行数仍然大于等于2,那么就再调用函数,用这个矩阵的每一行(除最后一行)按照我们定义的方法继续构造矩阵(这样就能得到3个数的和),每次取矩阵中的最小值并记录。以此类推。

        对于矩阵的最后一行,我们不用构造(直接将其作为结果与平均值比较),而构造矩阵也会出现由长度为2的向量构造出1x1的矩阵的情况,所以我加上一个判断语句,如果构造的矩阵是1x1的,则直接比较矩阵的值和平均值

        我们首先定义一个函数FindApr来寻找某个数组中,最接近于平均值的数,并返回和平均值的最小差值:

        

 我们定义一个函数FindMiniMinus,用来递归构造矩阵并搜寻最小值,截图如下:

其中,a是初始时候的向量,在迭代过程中始终保持不变

 另外需要注意的一点是,第一次,我们是使用初始数组A和A本身来调用这个函数的(首先是用A[1,2.5 ......6.1]和传入A之后截取的部分),后边的都要传入之后自动进行截取Create_Mat(ar,a(length(a)-n+2:end))

还要注意的一点,就是因为在调用函数中,只是计算了所构造的矩阵中的差最小值,因此只是计算了2,3,4.....个数字之和的最小值,所以我们还要加一句

m = FindMiniMinus(A , A,  ave)  %调用写好的函数获取与平均差值的最小值

m1 = min(abs(A-ave));  % 单个数字与平均值的最小差值

if m1<m
    m=m1;
end

(3)代码实现

% 矩阵编程思想解决浮点型数组拆分问题
clc
tic 
A = [1,2.5,3.1,4.8,5.3,6.1];
ave = sum(A)/2;

A = sort(A);
m = FindMiniMinus(A , A,  ave);
m1 = min(abs(A-ave));

if m1<m
    m=m1;
end

if m1<m
    m=m1;
end

disp(m)
toc


function overall_mini = FindMiniMinus( a, ar,ave)
    % 其实a可以作为全局变量的,但是怕出问题就没用
    % a -- 传入的初始向量
    % ar -- 传入的行向量(有可能是从矩阵中取的某一行)
    n = length(ar);
    overall_mini = inf ;   % 使用overall_mini来存储差的绝对值最小值
    if n > 1
        C = Create_Mat( ar ,   a(length(a)-n+2 :end) ) ;    % 调用Create_Mat函数,构造矩阵,寻找最小差值
        m = FindApr(C , ave); % 首先,找出数组C中最接近平均值的数
        if m == 0  % 当有数和平均值相等时,直接返回0以节省时间
            overall_mini = 0;
        else
            if m < overall_mini     % 找到更小的差值,更新
                overall_mini = m ;
            end
            % ------这一段是对每一行,递归调用函数----------
            for j = 1:n-2  % 对C的每一行(最后一行不用)调用函数构造矩阵
                m = FindMiniMinus( a , C(j,:)  , ave);  % 每次截取C的一行,递归调用函数,
                % ---------这一句,通过调用本身,进行递归调用--------
                if m < overall_mini
                    overall_mini = m ;
                end
            end
        end
    else % 对于只有一个数据的行向量(这是)(则直接返回它与平均值差值的绝对值即可)
        overall_mini = abs(ar - ave) ;
    end
end

% use this function is to find the minimum minus of every element  a matrix
% that is bigger than the average
function m = FindApr(A ,ave )   %       ******求和平均值最接近的值*****
C = (A-ave);
s = size(A);
if s(1) >1
    m = min(min(abs(C)));
else
    m = min(abs(C));
end
end



function C = Create_Mat(A,B)    % 构造我们需要的矩阵
    if length(A)>1 % 对于长度大于1的情形
        n = length(A);
        T= flip(tril(ones(n-1)).*A(1:n-1))';
        C = T;
        for i = 1:length(B)
            C(i,:) = C(i,:)+B;
            B = [B(2:n-1),0];  % 每一次截取前半段然后和0拼接得到第二个矩阵的下一行
        end
    end
end

(这个加上显示C和ave之后)当然,这样运行结果直接就是0,我们检查矩阵C,发现显然5.3+6.1 = 11.4即为平均值,所以我们可以随便改改数值:

 

这个是把6.1改成了6.15之后的测试结果,可以看出,构造的矩阵符合我们的要求

于是,显然,拆分之后的数组与平均值的差值为m,则两个数组的最小差值为2m(这个不太好记录怎么拆分),返回2m 即为拆分成为两个浮点数组的最小值。

        但是,这个是一个遍历的方法,所以它的时间复杂度是比较高的,一般对于长度为11的数组,可能就需要20秒左右了

三、同余数组拆分问题的求解方法

首先这个是我们一道题目(先放题):

(1)原理讲解

        我们只需要找出相应的一种拆分方式,对应于一个{0,1,2.....n-1}任意去掉一个元素之后能分成两组并且模n同余。

        因为题目中已经说了总存在有一种分配方式我们主要思考余数如何分配的问题:我们仍然以比较简单的数组( n=7 )[0 1 2 3 4 5 6]为例, 显然,任意去除一个数,为了保证两个拆分出的数组同余,我们应当计算出这两个余数的总和:去除3,则剩余的数字和为18, 18模7余4 , 则我们考虑将其拆分成两个模7余2的数组(9,9 )显然,126,450满足要求 ,但去除4,剩余数字和为17,模7余3,那么我们考虑通过凑的方式,凑一个7,则余数成为10,分配到两个数字上,则模7余5,那么两个数字应该是12和5,显然,561,230 满足要求

        那么我们就可以通过这个数组和去掉的值,计算拆分后的两个数组的总和大小

        我们来理一下整体的思路:首先,计算数组的和除n得到的余数m, 检验这个余数是奇数还是偶数

        如果是偶数,则拆分出的两个数应该相等,并且都等于和的一半,如果是奇数,则两个数字为:(1):(sum + n + 余数)/2   (2):(sum - n + 余数)/2

        然后通过递归遍历,求解长度为(n-1)/2的子数组,找出符合条件的即可。

        另外要说明一下为什么这样分配两个数组的值(拆出来的数组能够凑出来这两个值),但是我确实不会证明 :我们可以这样来理解(我也试图进行一些证明): 对于一个长度为2n的连续数组,拆分之后,长度为n,又因为拆分数组时,可以替换元素,所以数组的和可以连续变化(对于[0 1 2 3 5 6],拆分后假设有[0 1 2 ],那么[0 1 3] 也是一种可能,其和是连续变化的(除了在去掉的点),所以,设n = 2m+1 ,则数组的整体的和为m(2m+1)(恰好除n余数为0),则任意去掉一个数之后,最大的m个数的和可以达到m+m+1+m+2+....+2m-1]) = (3m-1)*m/2 ,而将一个数组拆分为两个数组之后,其中较大的那个应当小于(m/2+1)(2m+2)(由于不加补的2m+1 是m(2m+1)/2),这个值是(m+2)(m+1),由于连续变化,应当可以凑出相应的值(也可以从最大的m个数和最小的m个数的和的范围与两个值的关系理解,    另外,由于在初始时,如果不去除一个数,那么两个数组的和显然可以分成1/2sum,所以中间的部分相对地容易凑出来————这一段确实我自己也写不出来啥,其实得到的和也是我猜的,如果错了请谅解并和我联系

(2)代码实现

function [A,B] = ModnSameRem(n,d)

global Arr
Arr = [];

% 这些是针对数据检验的,数据要求n是一个正整数且为奇数,d必须小于n且为正整数
if mod(n,1)~= 0 || mod(n,2) ~=1
    error("n should be a positive odd number into this function");
end

if d>n-1 || d<0 || mod(d,1)~=0
    error("d should be a less than n integer");
end

L= 0:1:n-1;   % 首先建立数组,从0到n-1
L(d+1) = [] ;
IsImpled = 0 ;

LeavedNum =  mod( sum(L) , n);   % 计算总和对n的模
if mod(LeavedNum,2) ~= 0  % 检查总和模n是否为偶数,如果是,则分配到两个
    IsImpled = 1;
end

Judge = floor(sum(L)/(n));  % Judge是包含的n的个数
% 这一段的思路是分别计算每个数所含的n的数量和所含的余数,然后得到两个数Sum_1和Sum_2
% 这段代码和我上面讲的计算方法稍有不同,但总体思路一样
if IsImpled == 0  % 判断余数是否需要加一个n再分配
    if mod(Judge,2) == 0
        Sum_1 = Judge/2 *n + LeavedNum/2 ;
        % Sum_2 = Sum_1 
    else
        Sum_1 = (Judge-1) / 2 * n + LeavedNum/2 ;
        % Sum_2 = (Judge+1) / 2 * n + LeavedNum/2 ;
    end
else % 有一个n需要分配到余数中
    if mod(Judge,2) == 0
        Sum_1 = (Judge/2-1) * n + (LeavedNum + n)/2 ;
        % Sum_2 = Judge/2 * n + (LeavedNum + n)/2 ;
        
    else
        Sum_1 = (Judge-1)/2 * n + (LeavedNum + n)/2;
        %Sum_2 = Sum_1;
    end
end



% 下面这一段是递归遍历求解符合相应长度的子数组,我就不讲了,直接当函数抄来用就好
Find_Subarray(L , length(L)/2 ,Sum_1) ;
% disp(L)

A = Arr ;  % This is the first array
% delete corresponding elements

index_i = 1;
while index_i <= length(A)
    index_j =1 ;  % every time
    while index_j <= length(L)
        if L(index_j) == A(index_i)
            L(index_j) = [];
            % note that delete the element would cause the change of the length of the array L
        end
        index_j = index_j + 1;
    end
    index_i = index_i + 1;
end

B = L;    % obtain the array B

end
% ModnSameRem(9,6)

function Find_Subarray(A , Length,TotSum)    % we use every number of 
    if nargin == 1
        n = length(A);
        for i = 1: n
            CheckEverySubarray( A , i , A(i) ) ;  % from index i to run the method
        end
        
    else
        
        n = length(A);
        for i = 1: n - Length + 1
            CheckEverySubarray( A , i , A(i) , Length , TotSum) ;  % from index i to run the method
        end
    end
end

function CheckEverySubarray( A , i , pre_array, Length , TotSum)
global Arr
if nargin == 3
    n = length(A);
    disp(pre_array)  %------------*************---------> change this line
    if i~= n   % when i is equal to n, don't travel the array
        for j = i+1:n
            CheckEverySubarray(A , j , [pre_array,A(j)] ,Length,TotSum);   % add element A(j) to the pre_array
        end
    end
else
    n = length(A);
    if length(pre_array) +(n-i+1) >= Length
        if length(pre_array) == Length
            if sum(pre_array) == TotSum
                Arr = pre_array;
            end
        else
            for j = i+1:n
                CheckEverySubarray(A , j , [pre_array,A(j)] ,Length,TotSum);   % add element A(j) to the pre_array
            end
        end
    end

end

end

测试代码:

ModnSameRem(11,6)

运行结果: 

(这个是拆分的一个数组)

这篇文章的话,其实也没参考多少东西,后面两种方法主要我自己弄得。。。

 原创声明的话--->同步声明原创至CSDN“程序菜鸟一只”和微信公众号“FPRSP的小屋”

(公众号里面也有一些教程内容,以后如果有比较系统的教程的话我会优先更新在公众号里面,目前也有不算很多的MATLAB教程)

想来玩的可以来玩

猜你喜欢

转载自blog.csdn.net/sbsbsb666666/article/details/124443594
今日推荐