编程语言
首页 > 编程语言> > 国产哈希算法WJLHA1.0.1的JAVA源码,是否强过MD5、SHA、SM3?欢迎来测!

国产哈希算法WJLHA1.0.1的JAVA源码,是否强过MD5、SHA、SM3?欢迎来测!

作者:互联网

WJLHA1.0.1(java版本)源代码,欢迎专业人士使用和破解(碰撞技术)。郑重提醒:未经授权,严禁商用
WJLHA1.0.1的C源码请点击下面的连接:
WJLHA1.0.1版本C源码
使用过MD5、SHA、SM3算法的朋友应该知道这个算法的价值。此次增加JAVA版本,以方便专业人员测试与学习。如遇疑问,请留言。JAVA与C版已实现互通,均可自定义,同一文件两个版本计算出的值相同。

哈希(Hash)算法(散列算法),数学领域叫单向散列函数或杂凑函数。本文提供的算法,感兴趣的可以自行推导到数据检错应用方法(类如CRC、奇偶校验的应用)。该类算法其中的一个应用方法如图:
百度单向散列函数词条的截图

WJLHA1.0.1算法的JAVA源码:
源代码包括了WJLHashAlgorithm.java欢迎测试。

/******************************************************************************
杰林码-散列算法-JAVA版本
理论来源《杰林码-加权概率模型单向散列函数》
代码实现:王杰林
时间:2020.05.07
版本号:V1.0.1
******************************************************************************/

public class WJLHashAlgorithm {
	// 编码位长
	private int RC_CODE_BITS = 31;
	// 低位移去位长
	private int RC_SHIFT_BITS = RC_CODE_BITS - 8;
	// 区间范围的最大值(Rmax) & 0x00000000FFFFFFFFL目的是转成正数
	private long RC_MAX_RANGE = Unsigned((int) (1 << RC_CODE_BITS));
	// 区间范围的最小值(Rmin)
	private long RC_MIN_RANGE = Unsigned((int) (1 << RC_SHIFT_BITS));
	// 符号0和符号1的概率
	double p0 = 0.0;
	double p1 = 0.0;
	// 杰林码系数
	double JIELINCOE = 0.0;
	// 初始化编码器值
	long EFLow = RC_MAX_RANGE;
	long EFRange = RC_MAX_RANGE;
	int EFDigits = 0;
	int EFFollow = 0;
	int EOut_buff_loop = 0;
	// 每个字节中比特1的个数
	private short[] CntOfOneSymbol =  {
			0x00,0x01,0x01,0x02,0x01,0x02,0x02,0x03,0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,
			0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,
			0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,
			0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,
			0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,
			0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,
			0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,
			0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,
			0x01,0x02,0x02,0x03,0x02,0x03,0x03,0x04,0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,
			0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,
			0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,
			0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,
			0x02,0x03,0x03,0x04,0x03,0x04,0x04,0x05,0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,
			0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,
			0x03,0x04,0x04,0x05,0x04,0x05,0x05,0x06,0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,
			0x04,0x05,0x05,0x06,0x05,0x06,0x06,0x07,0x05,0x06,0x06,0x07,0x06,0x07,0x07,0x08
		};
	// 每个字节中各位置的比特值
	private byte[][] bitOfByteTable =	{
		{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,1},{0,0,0,0,0,0,1,0},{0,0,0,0,0,0,1,1},{0,0,0,0,0,1,0,0},{0,0,0,0,0,1,0,1},{0,0,0,0,0,1,1,0},{0,0,0,0,0,1,1,1},	//0~7
		{0,0,0,0,1,0,0,0},{0,0,0,0,1,0,0,1},{0,0,0,0,1,0,1,0},{0,0,0,0,1,0,1,1},{0,0,0,0,1,1,0,0},{0,0,0,0,1,1,0,1},{0,0,0,0,1,1,1,0},{0,0,0,0,1,1,1,1},	//8~15	
		{0,0,0,1,0,0,0,0},{0,0,0,1,0,0,0,1},{0,0,0,1,0,0,1,0},{0,0,0,1,0,0,1,1},{0,0,0,1,0,1,0,0},{0,0,0,1,0,1,0,1},{0,0,0,1,0,1,1,0},{0,0,0,1,0,1,1,1},	//16~23
		{0,0,0,1,1,0,0,0},{0,0,0,1,1,0,0,1},{0,0,0,1,1,0,1,0},{0,0,0,1,1,0,1,1},{0,0,0,1,1,1,0,0},{0,0,0,1,1,1,0,1},{0,0,0,1,1,1,1,0},{0,0,0,1,1,1,1,1},	//24~31
		{0,0,1,0,0,0,0,0},{0,0,1,0,0,0,0,1},{0,0,1,0,0,0,1,0},{0,0,1,0,0,0,1,1},{0,0,1,0,0,1,0,0},{0,0,1,0,0,1,0,1},{0,0,1,0,0,1,1,0},{0,0,1,0,0,1,1,1},	//32~39
		{0,0,1,0,1,0,0,0},{0,0,1,0,1,0,0,1},{0,0,1,0,1,0,1,0},{0,0,1,0,1,0,1,1},{0,0,1,0,1,1,0,0},{0,0,1,0,1,1,0,1},{0,0,1,0,1,1,1,0},{0,0,1,0,1,1,1,1},	//40~47
		{0,0,1,1,0,0,0,0},{0,0,1,1,0,0,0,1},{0,0,1,1,0,0,1,0},{0,0,1,1,0,0,1,1},{0,0,1,1,0,1,0,0},{0,0,1,1,0,1,0,1},{0,0,1,1,0,1,1,0},{0,0,1,1,0,1,1,1},	//48~55
		{0,0,1,1,1,0,0,0},{0,0,1,1,1,0,0,1},{0,0,1,1,1,0,1,0},{0,0,1,1,1,0,1,1},{0,0,1,1,1,1,0,0},{0,0,1,1,1,1,0,1},{0,0,1,1,1,1,1,0},{0,0,1,1,1,1,1,1},	//56~63
		{0,1,0,0,0,0,0,0},{0,1,0,0,0,0,0,1},{0,1,0,0,0,0,1,0},{0,1,0,0,0,0,1,1},{0,1,0,0,0,1,0,0},{0,1,0,0,0,1,0,1},{0,1,0,0,0,1,1,0},{0,1,0,0,0,1,1,1},	//64~71
		{0,1,0,0,1,0,0,0},{0,1,0,0,1,0,0,1},{0,1,0,0,1,0,1,0},{0,1,0,0,1,0,1,1},{0,1,0,0,1,1,0,0},{0,1,0,0,1,1,0,1},{0,1,0,0,1,1,1,0},{0,1,0,0,1,1,1,1},	//72~79
		{0,1,0,1,0,0,0,0},{0,1,0,1,0,0,0,1},{0,1,0,1,0,0,1,0},{0,1,0,1,0,0,1,1},{0,1,0,1,0,1,0,0},{0,1,0,1,0,1,0,1},{0,1,0,1,0,1,1,0},{0,1,0,1,0,1,1,1},	//80~87
		{0,1,0,1,1,0,0,0},{0,1,0,1,1,0,0,1},{0,1,0,1,1,0,1,0},{0,1,0,1,1,0,1,1},{0,1,0,1,1,1,0,0},{0,1,0,1,1,1,0,1},{0,1,0,1,1,1,1,0},{0,1,0,1,1,1,1,1},	//88~95
		{0,1,1,0,0,0,0,0},{0,1,1,0,0,0,0,1},{0,1,1,0,0,0,1,0},{0,1,1,0,0,0,1,1},{0,1,1,0,0,1,0,0},{0,1,1,0,0,1,0,1},{0,1,1,0,0,1,1,0},{0,1,1,0,0,1,1,1},	//96~103
		{0,1,1,0,1,0,0,0},{0,1,1,0,1,0,0,1},{0,1,1,0,1,0,1,0},{0,1,1,0,1,0,1,1},{0,1,1,0,1,1,0,0},{0,1,1,0,1,1,0,1},{0,1,1,0,1,1,1,0},{0,1,1,0,1,1,1,1},	//104~111
		{0,1,1,1,0,0,0,0},{0,1,1,1,0,0,0,1},{0,1,1,1,0,0,1,0},{0,1,1,1,0,0,1,1},{0,1,1,1,0,1,0,0},{0,1,1,1,0,1,0,1},{0,1,1,1,0,1,1,0},{0,1,1,1,0,1,1,1},	//112~119
		{0,1,1,1,1,0,0,0},{0,1,1,1,1,0,0,1},{0,1,1,1,1,0,1,0},{0,1,1,1,1,0,1,1},{0,1,1,1,1,1,0,0},{0,1,1,1,1,1,0,1},{0,1,1,1,1,1,1,0},{0,1,1,1,1,1,1,1},	//120~127
		{1,0,0,0,0,0,0,0},{1,0,0,0,0,0,0,1},{1,0,0,0,0,0,1,0},{1,0,0,0,0,0,1,1},{1,0,0,0,0,1,0,0},{1,0,0,0,0,1,0,1},{1,0,0,0,0,1,1,0},{1,0,0,0,0,1,1,1},	//128~135
		{1,0,0,0,1,0,0,0},{1,0,0,0,1,0,0,1},{1,0,0,0,1,0,1,0},{1,0,0,0,1,0,1,1},{1,0,0,0,1,1,0,0},{1,0,0,0,1,1,0,1},{1,0,0,0,1,1,1,0},{1,0,0,0,1,1,1,1},	//136~143
		{1,0,0,1,0,0,0,0},{1,0,0,1,0,0,0,1},{1,0,0,1,0,0,1,0},{1,0,0,1,0,0,1,1},{1,0,0,1,0,1,0,0},{1,0,0,1,0,1,0,1},{1,0,0,1,0,1,1,0},{1,0,0,1,0,1,1,1},	//144~151
		{1,0,0,1,1,0,0,0},{1,0,0,1,1,0,0,1},{1,0,0,1,1,0,1,0},{1,0,0,1,1,0,1,1},{1,0,0,1,1,1,0,0},{1,0,0,1,1,1,0,1},{1,0,0,1,1,1,1,0},{1,0,0,1,1,1,1,1},	//152~159
		{1,0,1,0,0,0,0,0},{1,0,1,0,0,0,0,1},{1,0,1,0,0,0,1,0},{1,0,1,0,0,0,1,1},{1,0,1,0,0,1,0,0},{1,0,1,0,0,1,0,1},{1,0,1,0,0,1,1,0},{1,0,1,0,0,1,1,1},	//160~167
		{1,0,1,0,1,0,0,0},{1,0,1,0,1,0,0,1},{1,0,1,0,1,0,1,0},{1,0,1,0,1,0,1,1},{1,0,1,0,1,1,0,0},{1,0,1,0,1,1,0,1},{1,0,1,0,1,1,1,0},{1,0,1,0,1,1,1,1},	//168~175
		{1,0,1,1,0,0,0,0},{1,0,1,1,0,0,0,1},{1,0,1,1,0,0,1,0},{1,0,1,1,0,0,1,1},{1,0,1,1,0,1,0,0},{1,0,1,1,0,1,0,1},{1,0,1,1,0,1,1,0},{1,0,1,1,0,1,1,1},	//176~183
		{1,0,1,1,1,0,0,0},{1,0,1,1,1,0,0,1},{1,0,1,1,1,0,1,0},{1,0,1,1,1,0,1,1},{1,0,1,1,1,1,0,0},{1,0,1,1,1,1,0,1},{1,0,1,1,1,1,1,0},{1,0,1,1,1,1,1,1},	//184~191
		{1,1,0,0,0,0,0,0},{1,1,0,0,0,0,0,1},{1,1,0,0,0,0,1,0},{1,1,0,0,0,0,1,1},{1,1,0,0,0,1,0,0},{1,1,0,0,0,1,0,1},{1,1,0,0,0,1,1,0},{1,1,0,0,0,1,1,1},	//192~199
		{1,1,0,0,1,0,0,0},{1,1,0,0,1,0,0,1},{1,1,0,0,1,0,1,0},{1,1,0,0,1,0,1,1},{1,1,0,0,1,1,0,0},{1,1,0,0,1,1,0,1},{1,1,0,0,1,1,1,0},{1,1,0,0,1,1,1,1},	//200~207
		{1,1,0,1,0,0,0,0},{1,1,0,1,0,0,0,1},{1,1,0,1,0,0,1,0},{1,1,0,1,0,0,1,1},{1,1,0,1,0,1,0,0},{1,1,0,1,0,1,0,1},{1,1,0,1,0,1,1,0},{1,1,0,1,0,1,1,1},	//208~215
		{1,1,0,1,1,0,0,0},{1,1,0,1,1,0,0,1},{1,1,0,1,1,0,1,0},{1,1,0,1,1,0,1,1},{1,1,0,1,1,1,0,0},{1,1,0,1,1,1,0,1},{1,1,0,1,1,1,1,0},{1,1,0,1,1,1,1,1},	//216~223
		{1,1,1,0,0,0,0,0},{1,1,1,0,0,0,0,1},{1,1,1,0,0,0,1,0},{1,1,1,0,0,0,1,1},{1,1,1,0,0,1,0,0},{1,1,1,0,0,1,0,1},{1,1,1,0,0,1,1,0},{1,1,1,0,0,1,1,1},	//224~231
		{1,1,1,0,1,0,0,0},{1,1,1,0,1,0,0,1},{1,1,1,0,1,0,1,0},{1,1,1,0,1,0,1,1},{1,1,1,0,1,1,0,0},{1,1,1,0,1,1,0,1},{1,1,1,0,1,1,1,0},{1,1,1,0,1,1,1,1},	//232~239
		{1,1,1,1,0,0,0,0},{1,1,1,1,0,0,0,1},{1,1,1,1,0,0,1,0},{1,1,1,1,0,0,1,1},{1,1,1,1,0,1,0,0},{1,1,1,1,0,1,0,1},{1,1,1,1,0,1,1,0},{1,1,1,1,0,1,1,1},	//240~247
		{1,1,1,1,1,0,0,0},{1,1,1,1,1,0,0,1},{1,1,1,1,1,0,1,0},{1,1,1,1,1,0,1,1},{1,1,1,1,1,1,0,0},{1,1,1,1,1,1,0,1},{1,1,1,1,1,1,1,0},{1,1,1,1,1,1,1,1}		//248~255
	};
	// 由于java中没有无符号整型运算,在这里采用模拟运算方式,经过与C++的无符号类型验证通过的
	private long Unsigned(int value) {
		long result = value;
		if (value > 0xffffffffL) {
			result = (value % 0xffffffffL) - 1;
		} else if (value < 0) {
			result = value + 0xffffffffL + 1;
		}
		if (result < 0) {
			result = Unsigned((int) result);
		}
		return result;
	}
	// 将有符号字节转成无符号字节
	public int Unsigned(byte value) {
		int result = value;
		if (value > 0xff) {
			result = (value % 0xff) - 1;
		} else if (value < 0) {
			result = value + 0xff + 1;
		}
		if (result < 0) {
			result = Unsigned((byte)result);
		}
		return result;
	}
	// 为了提高密钥的有效作用,对密钥进行一定的处理,并且返回变换后的系数
	private void ChangeKeyt(int keyt)
	{
		if(keyt < 100000.0 && keyt > 0){
			JIELINCOE = JIELINCOE - (1.0 / ((double)keyt + 100000.0));
		}else if(keyt >= 100000.0){
			JIELINCOE = JIELINCOE - (1.0 / (double)keyt);
		}
	}
	// 统计InBuFF中符号0的概率,并根据符号0的概率以及ByteLength得出杰林码系数,此函数就是根据杰林码理论所得
	private void GetJieLinCoeV(byte[] InBuFF, int ByteLength)
	{
		int i;
		double Count1 = 0, CountAll = 0, H = 0;
		// 首先是判断InBuFFLength 是不是大于等于 3 * ByteLength
		CountAll = (double)InBuFF.length * 8;
		// 统计符号0的个数
		for(i = 0; i < InBuFF.length; ++i){
			Count1 += (double)CntOfOneSymbol[Unsigned(InBuFF[i])];
		}
		// 求出符号0的概率p0和符号1的概率p1
		p1 = Count1 / CountAll;
		p0 = 1.0 - p1;
		// 全0或全1的二进制序列需要进行预处理
		if(p1 == 0.0){
			p1 = 0.001;
			p0 = 1.0 - p1;
		}else if(p0 == 0.0){
			p0 = 0.001;
			p1 = 1.0 - p0;
		}
		// 求出标准熵
		H = -p0 * (Math.log(p0)/Math.log(2.0))- p1 * (Math.log(p1)/Math.log(2.0));
		// 求出能编码出比特长度为ByteLength * 8的杰林码系数,请参看我的理论《杰林码-加权概率模型单向散列函数》中的公式(2-14)
		JIELINCOE = Math.pow( 2.0, H - ( (ByteLength - 4) * 8 ) / CountAll );
	}
	// 输出字节到缓存中
	private void WEOutPutEncode(byte[] EOut_buff, int ucByte)
	{
		if (ucByte > 255) {
			ucByte = ucByte & 0xFF;
		}
		if(EOut_buff_loop < EOut_buff.length) {
			EOut_buff[ EOut_buff_loop] = (byte)ucByte;
			EOut_buff_loop = EOut_buff_loop + 1;
		}
	}
	// 核心编码
	private void WEncode(byte symbol, byte[] EOut_buff)
	{
		
		long High = 0,i = 0;
		// 根据加权概率模型理论,杰林码系数作用于符号0和符号1的概率
		if (1 == symbol){// 符号1
			EFLow += Unsigned((int)(EFRange * p0));
			EFRange = Unsigned((int)(EFRange * p1));
		}else{
			EFRange = Unsigned((int)(EFRange * p0));
		}
		EFRange = Unsigned((int)((double)(EFRange * JIELINCOE)));
		// 根据区间编码方式每次输出一个编码后的字节
		while(EFRange <= RC_MIN_RANGE){
			High = Unsigned((int) (EFLow + EFRange - 1));
			if(EFFollow != 0) {
				if (High <= RC_MAX_RANGE) {
					WEOutPutEncode(EOut_buff, EFDigits);
					for (i = 1; i <= EFFollow - 1; ++i){
						WEOutPutEncode(EOut_buff, 0xFF);
					}
					EFFollow = 0;
					EFLow = EFLow + RC_MAX_RANGE;
				}
				else if (EFLow >= RC_MAX_RANGE) {
					WEOutPutEncode(EOut_buff, EFDigits + 1);
					for (i = 1; i <= EFFollow - 1; ++i){
						WEOutPutEncode(EOut_buff, 0x00);
					}									
					EFFollow = 0;
				} else {				
					EFFollow = EFFollow + 1;
					EFLow = Unsigned((int)((EFLow << 8) & (RC_MAX_RANGE - 1))); 
					EFRange = Unsigned((int) (EFRange << 8));
					continue;
				}
			}
			if  (((Unsigned((int) EFLow) ^ Unsigned((int) High)) & (0xFFL << RC_SHIFT_BITS)) == 0) {
				WEOutPutEncode(EOut_buff, (int) (Unsigned((int) EFLow) >> RC_SHIFT_BITS));
			}else{
				EFLow = Unsigned((int) (EFLow - RC_MAX_RANGE));
				EFDigits = (int) Unsigned((int) (EFLow >> RC_SHIFT_BITS));
				EFFollow = 1;
			}
			EFLow = Unsigned((int) (((EFLow << 8) & (RC_MAX_RANGE - 1)) | (EFLow & RC_MAX_RANGE)));
			EFRange = Unsigned((int) (EFRange << 8));
		}
	}
	private void WFinishEncode(byte[] EOut_buff)
	{
		int n = 0;
		// 输出剩余延迟数字
		if (EFFollow != 0) {
			if (EFLow < RC_MAX_RANGE) {
				// 趋向下沿
				WEOutPutEncode(EOut_buff, EFDigits);
				for (n = 1; n <= EFFollow - 1; n++) {
					WEOutPutEncode(EOut_buff, 0xFF);
				}
			} else {
				// 趋向上沿
				WEOutPutEncode(EOut_buff, EFDigits + 1);
				for (n = 1; n <= EFFollow - 1; n++) {
					WEOutPutEncode(EOut_buff, 0x00);
				}
			}
		}
		// 输出剩余编码
		EFLow = Unsigned((int) (EFLow << 1));
		n = RC_CODE_BITS + 1;// 32位
		do {
			n -= 8;// 每次减少8位
			WEOutPutEncode(EOut_buff, (int) Unsigned((int) (EFLow >> n)));
		} while (!(n <= 0));
	}
	// 杰林码哈希算法函数
	public byte[] WJLHA(byte[] InBuFF,int keyt, byte[] OutBuFF, int ByteLength)
	{
		int i = 0, j = 0;
		if(InBuFF.length <= 1){
			return null;
		}else {
			// 计算当前InBuFF中符号0和符号1的概率
			GetJieLinCoeV(InBuFF, ByteLength);
			// 设定了密钥
			if(keyt > 0){
				ChangeKeyt(keyt);
			}
			// 直接进行编码
			for(i = 0; i < InBuFF.length; ++ i){
				// 每个字节有8比特
				for(j = 0; j < 8; ++ j){
					// 对当前的symbol进行编码
					WEncode(bitOfByteTable[Unsigned(InBuFF[i])][j], OutBuFF);
				}
			}
			// 结束编码
			WFinishEncode(OutBuFF);
			// 不足自定义输出长度ByteLength的补充符号0
			if(EOut_buff_loop < ByteLength){
				for(i = EOut_buff_loop; i < ByteLength; ++i){
					WEOutPutEncode(OutBuFF, 0);
				}
			}
		}
		return OutBuFF;
	}
	
	// 测试
	public static void main(String[] args) {
		int i = 0;
		int In_BUFF_Len = 20000; // 随机生成字节
		int ByteLength = 32; // 自定义输出的字节长度,这里为256位
		int keyt = 0; // 私有密钥
		byte[] In_BUFF = new byte[In_BUFF_Len];
		byte[] Out_BUFF = new byte[ByteLength];
		// 随机生成In_BUFF_Len长度的(int) (Math.random() * 255);
		for(i = 0; i < In_BUFF_Len; ++i){
			In_BUFF[i] = (byte)(Math.random() * 255);
		}
		// 调用WJLHashAlgorithm算法
		WJLHashAlgorithm wha = new WJLHashAlgorithm();
		wha.WJLHA(In_BUFF, keyt, Out_BUFF, ByteLength);
		System.out.print("有符号的WJLHA:");
		for(i = 0; i < ByteLength; ++i){
			System.out.print(Out_BUFF[i]+",");
		}
		System.out.println();
		System.out.print("无符号的WJLHA:");
		for(i = 0; i < ByteLength; ++i){
			System.out.print(wha.Unsigned(Out_BUFF[i])+",");
		}
		System.out.println();
	}

}

标签:ByteLength,JAVA,来测,int,Unsigned,源码,result,byte,buff
来源: https://blog.csdn.net/wjlxueshu/article/details/105976181