海明码简介
汉明码(Hamming Code),是在电信领域的一种线性调试码,以发明者理查德·卫斯里·汉明的名字命名。汉明码在传输的消息流中插入验证码,当计算机存储或移动数据时,可能会产生数据位错误,以侦测并更正单一比特错误。由于汉明编码简单,它们被广泛应用于内存(RAM)。(摘自百度百科)
海明码是一种可以纠正一位差错的编码。它是利用在信息位为k位,增加r位冗余位,构成一个n=k+r位的码字,然后用r个监督关系式产生的r个校正因子来区分无错和在码字中的n个不同位置的一位错。它必需满足以下关系式: 2^r ≥ k+r+1 或 2r ≥ n+1 海明码的编码效率为: R=k/(k+r) 式中 k为信息位位数 r为增加冗余位位数。(摘自搜狗百科)
海明码冗余位的位置
海明码的编码规则如下:
可以看到,p1算作二进制中的第一位,p2算作二进制中的第二位,以此类推
但是,冗余位并不一定是在上图的那些位置,也可以放在别的地方(可以自行验证),但是为了方便一般都会这么放。1、2、4、8、16 等下标对应的二进制数只有一位是1,其他位置都是0。这样编码和译码做奇偶校验的时候,只用异或所有下标的二进制数对应位置为1的数。
Java实现
海明码只能纠错一位。下述实现,译码过程实现了自动纠错一位,但如果有多个错误,可能译码出错误的结果。
public class HammingCode {
/**
* 偶校验
*/
public static final int EVEN_PARITY = 0;
/**
* 奇校验
*/
public static final int ODD_PARITY = 1;
/**
* 编码译码时使用的校验模式,默认为偶校验
*/
private int checkMode = EVEN_PARITY;
/**
* 译码是否成功
*/
private boolean decodeSuccess = false;
/**
* 译码的时候,是否检测到输入错误
*/
private boolean errorSrc = false;
/**
* 译码的时候,检测到输入错误在第几位
*/
private Integer errorIndex;
// 无参构造
public HammingCode() {}
// 有参构造, 用与选择是奇校验还是偶校验
public HammingCode(int checkMode) {
if (checkMode == ODD_PARITY) {
this.checkMode = ODD_PARITY;
} else {
this.checkMode = EVEN_PARITY;
}
}
// 编码
public String code(String src) {
// 校验输入数据
if (src == null || src.equals("")) {
return "输入为空";
}
String[] srcArray = src.trim().split("");
for (String srcChar : srcArray) {
if (!srcChar.equals("0") && !srcChar.equals("1")) {
return "输入格式不正确";
}
}
int n = src.length();
int x = 0;
while ((1 << x) - 1 < n + x) x++;
// 将原数据填充到扩增的数组中的响应位置
int[] resultArray = new int[n + x];
for (int i = 0, j = 0, checkIndex = 1; i < resultArray.length; i++) {
if (checkIndex == i + 1) {
checkIndex = checkIndex << 1;
} else {
resultArray[i] = Integer.parseInt(srcArray[j++]);
}
}
// 填充数组中校验位的数据
for (int i = 1; i < resultArray.length; i = i << 1) {
int verification = checkMode;
for (int j = i + 1; j <= resultArray.length; j++) {
if ((i & j) != 0) {
verification = verification ^ resultArray[j - 1];
}
}
resultArray[i - 1] = verification;
}
// 获取返回字符串
StringBuilder result = new StringBuilder();
for (int i : resultArray) {
result.append(i);
}
return result.toString();
}
// 译码
public String decode(String src) {
// 校验输入数据
if (src == null || src.equals("")) {
return "输入为空";
}
String[] srcArray = src.trim().split("");
for (String srcChar : srcArray) {
if (!srcChar.equals("0") && !srcChar.equals("1")) {
return "输入格式不正确";
}
}
// 初始化译码相关的结果数据
initParam();
int n = src.length();
int x = 0;
while ((1 << x) - 1 < n) x++;
// 从左到右,获取通过校验位校验后的结果
int[] checkInts = new int[x];
for (int i = 1, index = 0; i < n; i = i << 1) {
int verification = Integer.parseInt(srcArray[i - 1]);
for (int j = i + 1; j <= n; j++) {
if ((i & j) != 0) {
verification = verification ^ Integer.parseInt(srcArray[j - 1]);
}
}
// 奇偶校验对应不同的值
checkInts[index++] = verification == checkMode ? 0 : 1;
}
// 通过校验位,获取出错在第几位
int errorIndexNum = 0;
for (int i = 0; i < x; i++) {
if (checkInts[i] != 0) {
errorIndexNum += (1 << i);
}
}
// 如果errorIndex大于输入的长度,代表输入数据错误肯定不止一位
if (errorIndexNum-1 > n) {
decodeSuccess = false;
errorSrc = true;
return "输入数据有误,可能包含不止一位错误,无法解码";
}
// 如果出错,纠正错误
if (errorIndexNum > 0) {
String indexStr = srcArray[errorIndexNum - 1];
if (indexStr.equals("0")) {
srcArray[errorIndexNum - 1] = "1";
} else {
srcArray[errorIndexNum - 1] = "0";
}
errorSrc = true;
errorIndex = errorIndexNum;
}
// 译码成功,获取并返回原数据
decodeSuccess = true;
StringBuilder result = new StringBuilder();
for (int i = 0, checkIndex = 1; i < n; i++) {
if (checkIndex == i + 1) {
checkIndex = checkIndex << 1;
} else {
result.append(srcArray[i]);
}
}
return result.toString();
}
/**
* 初始化译码相关的结果数据
*/
private void initParam() {
decodeSuccess = false;
errorSrc = false;
errorIndex = null;
}
public boolean isDecodeSuccess() {
return decodeSuccess;
}
public boolean isErrorSrc() {
return errorSrc;
}
public Integer getErrorIndex() {
return errorIndex;
}
}