个人项目-数独

项目源代码的Github链接

https://github.com/yaoling1997/softwareFirstHomework

需求分析

一、生成数独
命令:sudoku.exe -c n
要求:
(1)输出到sudoku.txt
(2)不重复
(3)1<=n<=1000000
(4)可以处理异常情况,如:sudoku.exe -c abc
(5)左上角第一个数为(学号后两位相加)% 9 + 1=(9+9)%9+1=1
(6)参数不合法输出"invalid parameters"到文件
二、求解数独
命令:sudoku.exe -s absolute_path_of_puzzlefile
要求:
(1)输出到sudoku.txt
(2)0代表空格
(3)文件中数独题目数为n,1<=n<=1000000
(4)保证格式正确
(5)对每个数独题目求出一个可行解
(6)参数不合法输出"invalid parameters"到文件

解题思路

一、生成数独
  第一步想到的是通过枚举每个格子的数字来生成数独,但是显然效率太低。通过上网查阅数独的相关知识,其中有一篇博客http://mouselearnjava.iteye.com/blog/1941483讲到了通过转换法来生成数独。
  第一种方法是交换数字法,比如说将棋盘上所有1和9互换位置后仍然是一个可行的数独。
1241011-20170924235720837-1050513983.jpg
  第二种方法是交换行列法,行和列的转换,需要保证的是:交换只发生在前三行,中间三行,最后三行,前三列,中间三列以及最后三列之间。而不能越界交换,比如第一行和第四行交换就是不允许的。 还有其余的一些方法,不过这里用不到。
1241011-20170924235738962-462930707.jpg
  经过和室友们的讨论,我们发现了一种基于变换来生成矩阵的方法。假设我们有一个正确的数独,我们可以通过数字交换法,将1~9分别映射到不同的排列来生成另外9!-1个数独。不过我们的题目要求左上角那个数字是固定的,于是通过第一种方法实际上共有8!=40320种数独,暂且称它们为种子。可题目要求最多可是要生成一百万个数独啊,怎么办呢?别急,接下来我们可以对这8!个种子应用行变换法。为了保证左上角的那个数字固定,我们做行变换时不考虑第一行。每3行分为一个组,第一组有不交换2、3行和交换2、3行两种方法,第二组和第三组都可以采取映射的方式,分别有3!=6种方法,所以对于第一种方法生成的每一个种子,我们可以再生成2*6*6-1=71个数独。所以我们一共生成了8!*2*3!*3!=2903040 种数独满足题目的数量要求。你可能会问,之后生成的数独会重复吗?我可以大声告诉你,不可能。不同的种子之间第一行是不一样的,所以它们通过行交换法生成的新数独也不会重复。
二、求解数独
  一开始想到的是暴力枚举没有填数的地方,不过还是上网查阅一下资料比较好。这里有一篇博客http://www.cnblogs.com/grenet/archive/2013/06/19/3138654.html讲的比较详细。不过这篇博客用到的求解方法仍然是比较低端的暴力,只是有一些小优化。我在求解的时候先将所有的没填数字的格子存到一个栈中,找出每个格子可以填的数字,存放到对应的set中。在搜索的时候先从可填数字少的空格开始搜索,这样的话一旦这个格子填上数字后会使得剩下的某些格子可填的数字变少,从而可以更快地求出解。测试了一下10000组随机数据要跑74s左右。当然还有一些更高端的做法,比如说将数独转化为精确覆盖问题并用DLX算法求解。我是参考的刘汝佳大佬写的白书(算法竞赛入门经典——训练指南)写的,不过经测试对于自己构造的10000组随机数据DLX算法要跑109s,比一开始写的暴力都慢,不过DLX的最坏求解时间是要优于暴力的。小伙伴们可以尝试交一发poj3074,这题我写的DLX可以过但暴力过不了。不过最终我还是决定交暴力,所以DLX算法在这里就不过多叙述了。
  本来是决定交暴力的,但是发现DLX慢是因为自己写的太丑了,用性能分析工具一看发现自己用的vector特别耗时间,改成数组后效率有了质的飞跃。所以这里还是简单介绍一下DLX算法的实现。我是主要是参考了白书的算法。首先我们要知道什么是精确覆盖问题(详情可以参考http://www.cnblogs.com/grenet/p/3145800.html)
1241011-20170925171314229-873228443.png
有这么一个矩阵我们要选择一些行使得每一列有且仅有一个1
我们可以实现这么一个链表
1241011-20170925171609323-740143893.png
第一行是新建的虚拟节点。每次递归时选择节点数最少的一列,然后枚举这一列上的行进行覆盖,记录选择的节点。覆盖的时候有会引起一些列的删除(所选行在对应列上有节点),这些新删的列有会引起一些行的删除(删除列在对应行上有节点)。递归会来有要还原链表的状态。当链表只剩下一个虚拟节点0时表示我们已经找到解了。
有了DLX算法,我们只需要将数独问题转化为精确覆盖问题就可以求解了。精确在覆盖问题的矩阵中,行代表决策,列代表需要满足的条件。我们可以将数独问题转化一下。(r,c,v)代表一个决策,在r行c列放数字v。需要满足的条件可以表示为:slot(a,b),a行b列缺数字;row(a,b),a行缺数字b;col(a,b),a列缺数字b;sub(a,b),第a个小矩形缺数字b。每个决策满足4个条件。因此链表一共9*9*9+1=729+1行(包括虚拟节点所占行),一共9*9*4=324列(不包括头节点),总共不超过9*9*9*4+324+1=3241个节点。构建好链表后就可以套用DLX算法求解啦!

设计实现

c++中我直接用结构体来实现数据的组织,根据需求和实现方法的不同分为了三个结构体:SolveC、SolveS、DLX、SolveS_DLX
SolveC:解决生成数独问题
其中包含三个函数,分别是:
(1)void initPermutation(int a[matrixLen])
用来生成用于映射的初始排列
(2)void rowChange(int row)
对当前种子进行行变换来获得新的数独,调用rowChange函数递归求解
(3)void solve()
解决问题的入口,调用initPermutation函数生成用于映射的初始排列,负责实现数字交换法,并调用rowChange函数来生成新的数独
SolveS:解决求解数独问题
其中包含五个函数,分别是:
(1)void init()
在求解每一个数独问题前清空相应的数据结构
(2)void initSetOfThisEmptyPos(int x, int y)
对每一个空格求出初始状态下所能填的所有数字,并放入它所对应的set中
(3)int removeItem(int x, int y, int xx, int yy)
在(x,y)上填上假想的数字后去更新空格(xx,yy)所对应的可填数字集合
(4)bool dfsSolve(vector<Node> emptyPos)
找到当前状态下可填数最少的空格,调用removeItem函数对其它空格可填数集合进行更新,并调用dfsSolve函数递归求解问题
(5)void solve()
解决问题的入口,负责读入数据(scanf),并调用相应的函数(init,initSetOfThisEmptyPos)给数据结构赋上初值,调用dfsSolve函数进行递归求解
DLX:解决精确覆盖问题
其中包含六个函数,分别是:
(1)void init(int n)
初始化链表和记录每一列节点个数的数组S以及一些变量
(2)void addRow(int r, int columns[], int cnt)
添加一行节点到链表里
(3)void remove(int c)
移除节点标号为c所在的列,并删除相应的行
(4)void void restore(int c)
恢复节点标号为c所在的列,并恢复相应的行
(5)bool dfs(int d)
调用dfs函数递归求解问题,调用remove函数删除列,调用restore函数恢复对应列
(6)bool solve(int ansRe[], int &ansSize)
解决问题的入口,负责调用dfs函数递归求解并返回求出来的结果到ansRe中
SolveS_DLX:用DLX算法解决求解数独问题
其中包含三个函数,分别是:
(1)int encode(int a, int b, int c)
给决策(a,b,c)编码
(2)void decode(int code, int &a, int &b, int &c)
将编码解码成决策(a,b,c)
(3)void solve()
解决问题的入口,负责读入数据(scanf),调用solver.init函数初始化DLX链表,调用solver.addRow函数添加一行节点,调用encode函数给决策编码,调用solver.solve函数求解问题,调用decode函数解码,通过output函数将结果输出到文件

流程图

SolveC.solve():
1241011-20170925083647104-1432045050.png
SolveS.solve():
1241011-20170925085428745-563284810.png
SolveS_DLX.solve():
1241011-20170925193604260-465054311.png

单元测试

单元测试中需要实现相应函数来判断一个数独终局是否合法,并对-c参数和-s参数分别测试代码正确性。由于最后还是决定交DLX算法,于是把暴力的结构体代码全部注释掉了。

    TEST_METHOD(TestMethod1){SolveC solveC;int a[matrixLen];solveC.initPermutation(a);//Assert::AreEqual(a[matrixLen-1],firstNum);}TEST_METHOD(TestMethod2){SolveC solveC;solveC.n = 2;solveC.rowChange(3);Assert::AreEqual(solveC.n, 0);}TEST_METHOD(TestMethod3){SolveC solveC;solveC.n = 200;solveC.solve();Assert::AreEqual(solveC.n, 0);Assert::AreEqual(checkMatrix(solveC.newSeed), true);}TEST_METHOD(TestMethod4){SolveC solveC;solveC.n = 2000;solveC.solve();Assert::AreEqual(solveC.n, 0);Assert::AreEqual(checkMatrix(solveC.newSeed), true);}TEST_METHOD(TestMethod5){SolveC solveC;solveC.n = 10000;solveC.solve();Assert::AreEqual(solveC.n, 0);Assert::AreEqual(checkMatrix(solveC.newSeed), true);}TEST_METHOD(TestMethod6){SolveS_DLX solveS;freopen("C:/Users/acer-pc/Desktop/git/softwareFirstHomework/sudoku/sudoku/1.in", "r", stdin);           solveS.solve();Assert::AreEqual(checkMatrix(solveS.matrix), true);}TEST_METHOD(TestMethod7){SolveS_DLX solveS;Assert::AreEqual(solveS.encode(2,4,8),207);}TEST_METHOD(TestMethod8){SolveS_DLX solveS;int a, b, c;solveS.decode(207,a,b,c);Assert::AreEqual(a, 2);Assert::AreEqual(b, 4);Assert::AreEqual(c, 8);}TEST_METHOD(TestMethod9){DLX solve;solve.init(5);Assert::AreEqual(solve.sz,6);}TEST_METHOD(TestMethod10){Assert::AreEqual(firstNum,1);}

1241011-20170925170134042-1469654463.png

覆盖率分析

1241011-20170925170702870-1746412061.png
可以看到实现功能的头文件solver.h的覆盖率为百分之百

性能分析与代码改进

设置参数为 “-c 1000000”
第一次运行的耗时为27.509秒
1241011-20170925094027979-1062451562.png
可见耗时最多的函数是output函数,output函数里我用putchar()函数逐个字符输出。
我把输出函数单独提出来在VS上新建项目并测试,发现耗时为19.035秒。我换了个IDE平台进行测试,发现只需要1.931秒
无奈把输出方式成了fputs输出,运行耗时变为了15.243秒
1241011-20170925114135604-1754142945.png
貌似还是不够快,从上图我们可以看出swap()函数是相当费时的。
找到用到swap函数的地方,改成了手写的交换元素,程序跑得更快了,耗时3.435秒
1241011-20170925130146807-1327046566.png
设置参数为 “-s 1.in”
1.in中有4000组随机数据,第一次运行的耗时为47.113秒
1241011-20170925162222479-1252434006.png
从上图可以看出vector的push_back相当耗时,于是我尝试着把vector全部替换为数组。哇,太神奇了,尽然只需要0.787秒!
1241011-20170925162500276-87914604.png

代码说明

    const int seed[matrixLen][matrixLen] = {{ 1, 2, 3, 4, 5, 6, 7, 8, 9 },{ 4, 5, 6, 7, 8, 9, 1, 2, 3 },{ 7, 8, 9, 1, 2, 3, 4, 5, 6 },{ 2, 1, 4, 3, 6, 5, 8, 9, 7 },{ 3, 6, 5, 8, 9, 7, 2, 1, 4 },{ 8, 9, 7, 2, 1, 4, 3, 6, 5 },{ 5, 3, 1, 6, 4, 2, 9, 7, 8 },{ 6, 4, 2, 9, 7, 8, 5, 3, 1 },{ 9, 7, 8, 5, 3, 1, 6, 4, 2 }};

进行变换前需要一个正确的数独种子,从网上获得

    void rowChange(int row) {if (row >= matrixLen) {output(newSeed);n--;return;}int per[3];for (int i = 0; i < 3; i++)per[i] = row + i;do {int temp[3][matrixLen];for (int i = row; i < row + 3; i++) {memcpy(temp[i % 3], newSeed[per[i % 3]], sizeof(temp[0]));}for (int i = row; i < row + 3; i++) {swap(temp[i % 3], newSeed[i]);}rowChange(row + 3);if (!n)return;for (int i = row; i < row + 3; i++) {swap(temp[i % 3], newSeed[i]);}} while (next_permutation(per, per + 3));}

行从0开始标号,这是对3,4,5和6,7,8行的变换从而生成新的数独

    while (n > 0) {for (int i = 0; i < matrixLen; i++)for (int j = 0; j < matrixLen; j++) {newSeed[i][j] = trans[numToPos[seed[i][j]]];}rowChange(3);if (!n)break;swap(newSeed[1], newSeed[2]);rowChange(3);next_permutation(trans, trans + matrixLen - 1);}

对每个种子,都采用行变换来生成新的数独,通过algorithm库中的next_permutation函数来生成下一个全排列

    void addRow(int r, int columns[], int cnt) {//r 行号,columns存放这一行的哪些列为1int first = sz;//sz 当前新建节点标号for (int i = 0; i < cnt; i++) {int c = columns[i];L[sz] = sz - 1;R[sz] = sz + 1;D[sz] = c;U[sz] = U[c];D[U[c]] = sz;U[c] = sz;row[sz] = r;col[sz] = c;S[c]++;sz++;}R[sz - 1] = first;L[first] = sz - 1;}

往DLX的链表中添加一行节点,修改对应的指针(这里是数组模拟链表)

    #define FOR(i,A,s) for(int i= A[s];i!=s;i=A[i])

顺着链表A,遍历除s外的其它元素

    void remove(int c) {L[R[c]] = L[c];R[L[c]] = R[c];FOR(i, D, c)FOR(j, R, i) {U[D[j]] = U[j];D[U[j]] = D[j];--S[col[j]];}}

移除标号为c的节点所在的列

    void restore(int c) {//恢复标号c所在的列FOR(i, U, c)FOR(j, L, i) {++S[col[j]];U[D[j]] = j;D[U[j]] = j;}L[R[c]] = c;R[L[c]] = c;}

恢复标号c所在的列,注意恢复的顺序与移除的顺序相反

    int encode(int a, int b, int c) {return (a * matrixLen + b) * matrixLen + c + 1;}

将决策(a,b,c)进行编码,映射到对应的行号上

    void decode(int code, int &a, int &b, int &c) {code--;c = code%matrixLen;code /= matrixLen;b = code %matrixLen;code /= matrixLen;a = code;}

将code解码获得决策(a,b,c),用于获得答案矩阵

        for (int r = 0; r < matrixLen; r++)for (int c = 0; c < matrixLen; c++)for (int v = 0; v < matrixLen; v++) {if (matrix[r][c] == 0 || matrix[r][c] == v + 1) {int columns[10];int cnt = 0;//列号从1开始columns[cnt++] = encode(SLOT, r, c);columns[cnt++] = encode(ROW, r, v);columns[cnt++] = encode(COL, c, v);columns[cnt++] = encode(SUB, r / 3 * 3 + c / 3, v);solver.addRow(encode(r, c, v), columns, cnt);}}()

枚举决策(r,c,v),向链表中逐行添加节点,每个决策对应四个可满足的条件(SLOT,ROW,COL,SUB)

表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划6030
· Estimate· 估计这个任务需要多少时间6030
Development开发57002820
· Analysis· 需求分析 (包括学习新技术)480360
· Design Spec· 生成设计文档480180
· Design Review· 设计复审 (和同事审核设计文档)36060
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)6060
· Design· 具体设计360240
· Coding· 具体编码18001200
· Code Review· 代码复审360120
· Test· 测试(自我测试,修改代码,提交修改)1800600
Reporting报告360420
· Test Report· 测试报告120120
· Size Measurement· 计算工作量120120
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划120180
合计61203270

GUI展示

1241011-20170926125059010-1880604123.png
采用QT+VS2017开发
程序一开始随机生成数独题目,玩家可以用鼠标点击挖空的地方,用键盘输入数字。点击submit按钮即可提交自己的答案。在按钮旁边会显示文字提示玩家的答案是否正确。真是太好玩了,赶紧叫上小伙伴们一起哈皮吧!

转载于:https://www.cnblogs.com/yaoling1997/p/7589188.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/571934.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

navicat premium 连接出现的问题

1、listener does not currently know of service requested in connect descriptor 2、问题截图&#xff1a; 3、问题原因&#xff1a;服务名或者SID不正确 4、改正方法&#xff1a;打开 图中tnsnames.ora文件 找到 XE就是服务名 正确连接&#xff1a; 转载于:https://www.cnb…

quartz java spring_从零开始学 Java - Spring 使用 Quartz 任务调度定时器

生活的味道睁开眼看一看窗外的阳光&#xff0c;伸一个懒腰&#xff0c;拿起放在床一旁的水白开水&#xff0c;甜甜的味道&#xff0c;晃着尾巴东张西望的猫猫&#xff0c;在窗台上舞蹈。你向生活微笑&#xff0c;生活也向你微笑。请你不要询问我的未来&#xff0c;这有些可笑。…

excel查重复_智学网怎么登录 智学网怎么查分数 智学网统一登录平台网址

阅读本文前&#xff0c;请您先点击上面的蓝色字体&#xff0c;再点击“关注”&#xff0c;这样您就可以继续免费收到最新文章了。每天都有分享。完全是免费订阅&#xff0c;请放心关注。注&#xff1a;本文转载自网络&#xff0c;不代表本平台立场&#xff0c;仅供读者参考&…

树莓派Java程序运行_树莓派上Java程序作为linux服务并开机自动启动

http://www.iigrowing.cn/shu_mei_pai_shang_java_cheng_xu_zuo_wei_linux_fu_wu_bing_kai_ji_zi_dong_qi_dong.html刚刚买了&#xff0c; 树莓派&#xff0c; 准备做一些程序&#xff0c; 放到树莓派上&#xff0c; 平时树莓派上不接显示器等各种设备&#xff0c;直接随着adsl…

小程序 mathjs渲染公式_Mac 3D渲染和动画制作----KeyShot 9 Pro

KeyShot 9 Pro for Mac是应用在Mac上的一款3D渲染和动画制作软件&#xff0c;keyshot是您快速创建精彩视觉效果所需的一切。在实时3D渲染工作流程显示结果即时&#xff0c;缩短了创建逼真的产品拍摄的时间。从科学上准确的材料和环境预设到高级材料编辑和动画&#xff0c;创建交…

用AsyncTask来获取网络图片

先看下运行结束 这里有两个Button 点击第一个Button运行的结果&#xff01;第一张有一个旋转的灰色圈圈&#xff01; 点击第二个Button运行的结果如下&#xff1a;带进度条的 ok,看下实现方法 先在配置清单中加一个访问网络的权限&#xff01; <uses-permission android:nam…

bilibili有电脑版吗_哪个手机便签软件有电脑版?有跨平台的桌面便签软件吗 - 学显...

如果仔细观察的话&#xff0c;你会发现&#xff1a;现在很多手机桌面上都有一款名叫“便签”的app小软件。其实&#xff0c;这是手机系统自带的一款备忘小工具&#xff0c;是为了方便用户记事而设计的。也就是说&#xff0c;平时如果有什么事儿需要记下来的话&#xff0c;就可以…

win10关机后自动重启_安卓手机重启和关机后再开机,区别原来这么大!别不当回事...

现在大家用智能手机&#xff0c;只会在以下几种情况关机&#xff0c;一种是用到没电&#xff0c;自动关机了。另一种是手机有点卡顿&#xff0c;通过关机再开机的方式&#xff0c;释放RAM内存&#xff0c;提高手机运行速度。而现在的很多手机&#xff0c;比如小米手机&#xff…

前端周报:前端面试题及答案总结;JavaScript参数传递的深入理解

1、2017前端面试题及答案总结 |掘金技术征文 "金三银四&#xff0c;金九银十"&#xff0c;用来形容求职最好的几个月。但是随着行业的饱和&#xff0c;初中级前端er就业形势不容乐观。 行业状态不可控&#xff0c;我们能做的当然只是让自己变得更加具有竞争力。 今年…

python二级考试真题_全国BIM技能等级考试真题全套(一/二级,全专业,28套)

BIM技能等级考试即将到来&#xff0c;你准备好了吗&#xff1f;今日为大家整理了一套网友上传的BIM等级考试全套真题&#xff0c;供大家学习参考~BIM一级真题解析课程限免&#xff1a;(点我)全国BIM技能等级考试真题解析(一级)仅限6月5日一天免费第一期全国BIM技能等级考试一级…

Awk使用方法简介

AWK AWK简介&#xff1a;awk是一个强大的文本分析工具&#xff0c;相对于grep的查找&#xff0c;sed的编辑&#xff0c;awk在其对数据分析并生成报告时&#xff0c;显得尤为强大。简单来说awk就是把文件逐行的读入&#xff0c; 以空格为默认分隔符将每行切片…

矩形波如何傅立叶展开_金科文化会不会连续拉板?两位同学展开激辩

【编者按&#xff1a;金科文化是我们昨晚理论上留下的两道作业题之一&#xff0c;要求同学们根据课程内容&#xff0c;判断一下它是否符合连板青云的条件&#xff1f;后市如何预判&#xff1f;操作计划怎么制定&#xff1f;今天&#xff0c;很多同学就此展开了分析和讨论。今晚…

java web 不用框架_MyShop-不用框架的基础javaweb项目

javaweb基础开发Servletjspmysqlhtmlcssjs(找一个前端模板&#xff0c;一大堆html,自己写的没有模板的美观)数据库设计来一个最基础的用户表drop table ifexists myshop_user;create table myshop_user(USER_ID varchar(32) not null,USER_NAME varchar(20) not null,USER_PASS…

程序包管理器控制台 Add-Migration 用法

需要注意的是&#xff1a; 1.任何对数据库的操作都在代码和程序包管理控制台完成&#xff0c;千万不要自己去修改数据库&#xff0c;no&#xff01; 2.ef中创建数据库的表必须要有主键~&#xff01;否则 就不让你成功~ 现在来说如何映射 第一步创建在代码model中创建类 第二步 …

ipad如何连接电脑_电脑无法连接外网远程调试,一文教你如何用手机让台式机连接外网...

在弱电施工中&#xff0c;设备调试是一个很重要的环节&#xff0c;施工这么久就是为了实现弱电各系统的功能&#xff0c;调试是每一个弱电人系必需会的技能&#xff0c;也是检验你结合能力一种体现。在调试中会遇到各种你想不到的问题&#xff0c;当遇到问题时&#xff0c;你应…

java 多线程两种方式_JAVA多线程实现的两种方式

java多线程实现方式主要有两种&#xff1a;继承Thread类、实现Runnable接口1、继承Thread类实现多线程继承Thread类的方法尽管被我列为一种多线程实现方式&#xff0c;但Thread本质上也是实现了Runnable接口的一个实例&#xff0c;它代表一个线程的实例&#xff0c;并且&#x…

安卓手机浏览器排行_安卓手机性能排行榜:国产手机集体“出位”,华为却在角落哭泣?...

华为手机的芯片一直都坚称是国产的骄傲&#xff0c;但是现在的华为芯片出现了一系列的问题&#xff0c;甚至有人预言在今年九月份之后&#xff0c;华为就不再会获得台积电的芯片供应了。这样&#xff0c;对华为来说无疑是不好的势头&#xff0c;而且芯片刚被市场认可&#xff0…

_Linux结束进程到底有多少种方法?

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章。如需学习视频&#xff0c;请在微信搜索公众号“智传网优”直接开始自助视频学习。1. 前言我们经常在Linux里使用kill命令来结束某后台进程。但kill命令实际上是向进程发送信号&#xff0c;并且有多种信号。终止运行一…

mulitpartfile怎么接收不到值_光端机电源正常,但是运行不了怎么办?

光端机&#xff0c;是光信号传输的终端设备&#xff0c;在安防领域有很好的应用。伴随着监控的发展&#xff0c;视频光端机就是把1路到多路的模拟视频信号通过各种编码转换成光信号通过光纤介质来传输的设备分&#xff0c;分为模拟光端机和数字光端机。其中&#xff0c;光传输系…

aes 加密_结合RSA与AES实现前后端加密通信

结合RSA与AES实现前后端加密通信一、思路使用RSA秘钥生成工具生成一对公钥(A)和私钥(B)&#xff0c;前端保留A&#xff0c;后端保留B。前端发送数据时&#xff0c;先生成一串随机16位字符串作为AES的秘钥(C)&#xff0c;然后使用A使用RSA算法对C进行加密&#xff0c;得到加密后…