开灯关灯问题的两种解决方式(数学建模线性代数的应用和编程实现)

开灯关灯问题的两种解决方式

1.问题介绍
2.编程实现算法及其代码(详细)
3.就该题进行数学建模–体验线性代数的使用

1.问题介绍

有一个按钮组成的矩阵,其中每行有5个按钮,共5行。每个按钮旁边有一盏灯。当按下一个按钮,该按钮及其上下左右四个按钮控制的灯都会发生状态改变,即,如果灯原来是熄灭的,就会被点亮;如果灯原来是点亮的,就会被熄灭。(在矩阵角上的按钮会改变3盏灯的状)请写一个程序确定需要按下那些按钮,恰好使得所有灯都熄灭。

2.编程实现算法及其代码(详细)

1.解题思路

对于第一行每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、列的按钮,可以熄灭前4列的灯。此时如果 第5行灯都是熄灭的 那么就得到了问题的解。有了基本思想以后,要解决两个问题:

1.对应的按钮
为了叙述方便,为按钮矩阵中的每个位置分别指定一个坐标。用数组元素 puzzle[i][j] 表示位置 (i, j) 上灯的初始状态:0表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。用数组元素 press[i] [j] 表示为了让全部的灯都熄灭,是否要按下位置 (i, j) 上的按钮:1 表示要按下;0 表示不用按下。
因为灯的最后状态(按它下一行开关之前)与周围灯是否按按钮有关,如 puzzle[i] [j],决定它最后状态的相关按钮为 press[i] [j-1] (左),press[i][j](它本身),press[i] [j+1](右),press[i-1][j](上),还有它最初的状态 puzzle[i] [j]。考虑到按两次按钮作用会抵消,需要取它们的和与2的余数,所以puzzle[i] [j] 最后状态公式为:
puzzle[i][j]最后状态 = (press[i][j-1]+press[i][j]+press[i][j+1]+press[i-1][j]+ puzzle[i] [j] ) % 2
而press[i+1] [j](对应按钮) 由 puzzle[i] [j] 的最后状态决定,灯亮着(值为1)就要关掉它,press值取1,灯是熄灭的(值为0)不用处理,press值取0,它们是相等的,所以press[i+1] [j] = puzzle[i] [j] 最后状态。

2.验证第5行灯是否都熄灭
易知puzzle[5] [j] 的最终状态由按钮press[5] [j-1] (左),press[5][j](它本身),press[5] [j+1](右),press[4] [j](上)决定
当puzzle[5][j]为1时,即第5行j列灯亮着,则分为两种情况
(1). puzzle[5][j] 初始 =1,(press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j])%2=0 即操作相互抵消
(2). puzzle[5][j]初始 =0,(press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j]%2=1即操作不能抵消
注意这两种情况都是 puzzle[5][j]初始 !=press [5] [j-1] + press [5] [j] + press [5] [j+1] + press [4] [j]%2

由此可知, 当我们输入每一行的初始状态puzzle[i][j],并且枚举第一行的press[1][j]时,我们就可以根据解题思路得到一个可行解。

2.代码如下:(附有注释)

#include<stdio.h>
int press[6][8];
int puzzle[6][8];

bool guess(){
    int i,j;
    for(i=2;i<=5;i++)
    {
        for(j=1;j<=6;j++)
        {
            // 根据同列的上一行灯的最后状态,来决定是否按按钮
            press[i][j] = ( press[i-1][j]
                            + puzzle[i-1][j]
                            + press[i-1][j-1]
                            + press[i-2][j]
                            + press[i-1][j+1] ) %2;
        }
    }

    for(j=1;j<=6;j++) 
    {
        // 逐一判断第五行的灯是否都熄灭               
        if( puzzle[5][j] != (press[5][j] + press[5][j-1] + press[5][j+1] + press[4][j]) %2 )
            return false;
    }

    return true;
}

void process() {

    int c;
    for(c=1; c<=6; c++)
        press[1][c]=0;

    while( !guess() ) 
    {
        // 采用二进制进位的算法,从000000 - 111111枚举第一行按钮的方式
        press[1][1]++;
        c=1;                
        while(press[1][c]>1)
        {
            press[1][c]=0;
            c++;
            press[1][c]++;
        }


    }
}

int main(){
    int t,i,n,j;
    // 初始化0行的所有元素
    for(i=0;i<8;i++)
    press[0][i]=puzzle[0][i]=0;
    // 初始化0列,7列的所有元素
    for(i=1;i<6;i++)
        press[i][0]=puzzle[i][0]=press[i][7]=puzzle[i][7]=0;

    for(i=1;i<=5;i++)
        for(j=1;j<=6;j++)
            scanf("%d",&puzzle[i][j]);

    process();

    for(i=1;i<=5;i++){
        for(j=1;j<=6;j++){
            printf("%d ",press[i][j]);
        }
        printf("\n");
    }

    return 0;
}

这是我自己的理解,参照原文链接: https://blog.csdn.net/qianli_jiang/article/details/53420362?utm_source=app&app_version=4.5.2

3. 就该题进行数学建模——体验线性代数的应用

以下针对从全部熄灭到全部亮起的情况,0,1分别表示熄灭和亮着。 (在看以下内容前可以学习一下线性空间定义和线性空间中基,维数)
定义状态空间S为按钮矩阵上所有可能的状态组成的集合 ,即
S = ( e 1 , e 2 , . . . , e 25 ) ∣ e i = 0 , 1 , i = 1 , 2 , . . . , 25 S= {(e_1,e_2,...,e_{25}) | e_i=0,1, i=1,2,...,25} S=(e1,e2,...,e25)ei=0,1,i=1,2,...,25
显然灯全亮状态为 s 1 s_1 s1={1,1,1…,1} ∈ \in S,全暗状态为 s 0 s_0 s0={0,0,0,…0} ∈ \in S
定义S上的一个变换t : s = ( e 1 , e 2 , . . . , e 25 ) → s ′ = ( h 1 , h 2 , . . . , h 25 ) , s , s ′ ∈ S s=(e_1,e_2,...,e_{25}) \rightarrow s' =(h_1,h_2,...,h_{25}) , s,s'\in S s=(e1,e2,...,e25)s=(h1,h2,...,h25)s,sS 其中 h i = e i 或 者 1 − e i h_i=e_i 或者1-e_i hi=ei1ei,特别的定义零变换为恒等变换
O ( e 1 , e 2 , . . . , e 25 ) = ( e 1 , e 2 , . . . , e 25 ) O(e_1,e_2,...,e_{25}) =(e_1,e_2,...,e_{25}) O(e1,e2,...,e25)=(e1,e2,...,e25)
设V是S上所有变换所组成的集合, 对任意f,g ∈ \in V,定义V中的加法运算为变换合成运算,记为f ∘ \circ g,并用 ∑ \sum 表示加法运算 ∘ \circ 的连加符号,例如: f 1 ∘ f 2 ∘ f 3 . . . . . ∘ f 25 = ∑ i = 1 25 f i f_1\circ f_2\circ f_3.....\circ f_{25}=\sum_{i=1}^{25}f_i f1f2f3.....f25=i=125fi
容易验证该运算满足下列性质:


(1).f ∘ \circ g=g ∘ \circ f ∀ \forall f,g ∈ \in V;
(2).(f ∘ \circ g) ∘ \circ t= f ∘ \circ (g ∘ \circ t); ∀ \forall f,g,t ∈ \in V;
(3).对 ∀ \forall f ∈ \in V,f ∘ \circ f=O

性质3告诉我们: 任何一种点击方式(变换)连续或间断操作偶数次,此操作的作用将被抵消,故为了使操作次数尽量少,应该避免重复操作。
为此考虑二元数域 F 2 F_2 F2={0,1}(同一操作次数<=1) ,类似于实数域和有理数域,同样可以在 F 2 F_2 F2进行加减乘除,运算法则如下:

+01
001
110
-01
001
110
× \times ×01
000
101
÷ \div ÷01
0无意义0
1无意义1

那么我们可以定义二元域 F 2 F_2 F2到V的数乘运算:1* f=f ,0*f=O, ∀ \forall f ∈ \in V ,容易验证:

V是 F 2 F_2 F2上的线性空间(8个性质)

将所有的方格按照从左到右从上到下依次编号为1到25,用 g i g_i gi,i=1,2,3,…25表示鼠标点击第i个方格所产生的变换,记一次可行的游戏策略为x(一个25维向量)= ( x 1 , x 2 , . . . . x 25 ) T (x_1,x_2,....x_{25})^T (x1,x2,....x25)T,其中 x i x_i xi=0,1
则x满足: ∑ i = 1 25 ( x i ∗ g i ) ( s 0 ) = s 1 \sum_{i=1}^{25}(x_i*g_i)(s_0)=s_1 i=125(xigi)(s0)=s1 关键是求出x.
为此定义变换 f i ∈ V f_i\in V fiV,i=1,2,…25为 f i ( e 1 , e 2 , . . . , e i , . . . e 25 ) = ( e 1 , e 2 , . . . , 1 − e i , . . . , e 25 ) f_i(e_1,e_2,...,e_i,...e_{25}) =(e_1,e_2,...,1-e_i,...,e_{25}) fi(e1,e2,...,ei,...e25)=(e1,e2,...,1ei,...,e25)(神来之笔啊) 由定义知, f i f_i fi只会改变第i个方格的状态,而不会影响到别的方格, 容易得到{ f i f_i fi |i=1,2,…25}是线性空间V的一组基,故 g i g_i gi可以唯一的用{ f i f_i fi}表示为 g i = f i ∘ f i − 5 ∘ f i + 5 ∘ f i − 1 ∘ f i + 1 g_i=f_i\circ f_{i-5}\circ f_{i+5}\circ f_{i-1}\circ f_{i+1} gi=fifi5fi+5fi1fi+1,
化为矩阵形式为: ( g 1 , g 2 , g 3 , . . . g 25 g_1,g_2,g_3,...g_{25} g1,g2,g3,...g25)=( f 1 , f 2 , f 3 , . . . f 25 f_1,f_2,f_3,...f_{25} f1,f2,f3,...f25) A 25 ∗ 25 A_{25*25} A2525 …[1]
(由上式可知 A 25 ∗ 25 A_{25*25} A2525第i列只有第i-1,i-5,i+5,i+1,i-1行是1.可以根据这个写一下A,发现A可以表示成以下形式: A = H E O O O E H E O O O E H E O O O E H E O O O E H (1) A= \begin{matrix} H & E& O& O& O \\ E & H & E & O& O\\ O & E & H& E& O\\ O &O& E & H& E\\ O & O& O& E& H \end{matrix} \tag{1} A=HEOOOEHEOOOEHEOOOEHEOOOEH(1) 其中每个矩阵都是5*5,E为单位矩阵,O为零矩阵,
H = 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 (1) H= \begin{matrix} 1 & 1& 0& 0& 0 \\ 1 & 1 & 1 & 0& 0\\ 0 & 1 & 1& 1& 0\\ 0 &0& 1 & 1& 1\\ 0 &0& 0 & 1& 1 \end{matrix} \tag{1} H=1100011100011100011100011(1)将[1]代入得: (( f 1 , f 2 , f 3 , . . . f 25 f_1,f_2,f_3,...f_{25} f1,f2,f3,...f25) A 25 ∗ 25 A_{25*25} A2525x)( s 0 s_0 s0)= s 1 s_1 s1
而( f 1 , f 2 , f 3 , . . . f 25 f_1,f_2,f_3,...f_{25} f1,f2,f3,...f25) ( 1 , 1 , 1 , . . . , 1 ) T (1,1,1,...,1)^T (1,1,1,...,1)T= s 1 s_1 s1,故 A 25 ∗ 25 A_{25*25} A2525x= ( 1 , 1 , 1 , . . . , 1 ) T (1,1,1,...,1)^T (1,1,1,...,1)T
当这个方程有解,这些灯才能由暗全部亮起,否则不可能达到目标,至于解方程,注意因为这个x向量只能由0,1组成,所以解方程时不能直接走常规路线,具体可以参考抽象代数的书籍(笔者能力有限,请谅解)。
这是我写的第一篇文章,有很多不足之处,希望大家多多理解,不当之处请指正,谢谢!最后希望读者可以从这篇文章体会细心线性代数的应用,理解思想,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值