算法习题---4-7RAID技术(UV509)

一:题目

(一)基础知识补充(RAID和奇偶校验)

磁盘管理—磁盘阵列(RAID)实例详解(本题目常用RAID 5技术实现)

奇偶校验(同行数据中同位上的1的个数,偶校验时:1的个数为偶数则校验结果为0,否则为1,奇校验时相反:1的个数为偶数则校验结果为1,否则为0)

(二)题目详解

RAID技术用多个磁盘保存数据。每份数据在不止一个磁盘上保存,因此在某个磁盘损 
坏时能通过其他磁盘恢复数据。本题讨论其中一种RAID技术。数据被划分成大小 
为s(1≤s≤64)比特的数据块保存在d(2≤d≤6)个磁盘上,如图所示,每d-1个数据块都 
有一个校验块,使得每d个数据块的异或结果为全0(偶校验)或者全1(奇校验)。

(三)案例解析

例如,d=5,s=2,偶校验,数据6C7A79EDFC(二进制01101100 01111010 01111001 11101101 11111100)的保存方式如图所示

其中加粗块是校验块。输入d、s、b、校验的种类(E表示偶校验,O表示奇校验)以及b(1≤b≤100)个数据块(其中“x”表示损坏的数据),
你的任务是恢复并输出完整的数据。如果校验错或者由于损坏数据过多无法恢复,应报告磁盘非法。

(四)样例输入

注意:

其中样例输入中:第一组数据网站和书籍有所出入。自己检查后发现书上是可以的。所以将原来的
0001011111 
0110111011 
1011011111 
1110101100 
0010010111 
替换为:
0001101100
0110111010
0111011001
1110111101
1111110011

样例输入:

5 2 5  //第一个参数是磁盘块(将一个数据块拆分为多个分别存放在各个磁盘块中)--列数  第二个参数是每个磁盘块存放的数据位数  第三个参数是数据块数(将每一块数据拆分为多个分别存放在各个磁盘中)--行数
E     //E是偶校验 O是奇校验
0001101100
0110111010
0111011001
1110111101
1111110011
3 2 5
E
0001111111
0111111011
xx11011111
3 5 1
O
11111
11xxx
x1111
0  //0代表输入结束

注意点:

1、校验块是不加入十六进制运算的 
2、校验块的顺序是第一行的第一块,第二行的第二个,到了某行最后一个时,下一行就有从第一个开始算做校验块  -----  当前行数%列数
3、十六进制转换是 一个十六进制数字需要四个二进制数字,所以每四位二进制就是一位十六进制 
4校验进行是一行中每个块的相同位进行校验 
5、奇校验就是每个数互相异或下来是1,偶校验就是0 
6、磁盘不合理有三种可能性:一是已知的位校验不符合,二是未知位有多位,无法判断其内容,三是校验位中含有x

数据实际存放样式:

5 2 5
E
0001101100            00 01 10 11 00
0110111010            01 10 11 10 10
0111011001    ----->  01 11 01 10 01
1110111101            11 10 11 11 01
1111110011            11 11 11 00 11
3 2 5
E                    00 01 11
0001111111           11 11 01
0111111011 ---->     11 11 10
xx11011111           11 xx 11
                     01 11 11
3 5 1
O
11111    
11xxx        ----->    11111 11xxx x1111
x1111

(五)样例输出

Disk set 1 is valid, contents are: 6C7A79EDFC 
Disk set 2 is invalid. 
Disk set 3 is valid, contents are: FFC

二:代码实现

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string>

#define ROW 100    //最多100条    100行
#define COL 6    //最多6个磁盘 6列
#define BITS 64    //每个磁盘块最多64位

int col, row, bits;
char DISK[ROW][COL][BITS], ct;    //获取基本数据:磁盘数据和校验类型
char Ddata[COL][BITS], CkCode[BITS];    //获取每行数据和校验值

//-1无效 0结束 1正确

获取磁盘数据,并进行检测和纠错

int getDiskData()    //改进:在这里检错
{
    int flag = 1;
    int x_bits[BITS], xNum = 0, OneNum[BITS];    //记录x位置x_bits中内容是对于列数值和x的个数
    scanf("%d", &col);
    if (col == 0) return 0;
    scanf("%d %d", &bits, &row);
    getchar();
    scanf("%c", &ct);
    getchar();

    //获取磁盘数据
    for (int i = 0; i < row;i++)
    {
        memset(x_bits, -1, sizeof(x_bits));
        memset(OneNum, 0, sizeof(OneNum));
        for (int j = 0; j < col; j++)
        {
            for (int k = 0; k < bits; k++)
            {
                scanf("%c", &DISK[i][j][k]);
                if (DISK[i][j][k] == '
')
                {
                    k--;
                    continue;
                }
                if (i%col == j)    //不允许校验位为x
                {
                    if (DISK[i][j][k] == 'x')
                        flag = -1;
                }

                if (DISK[i][j][k] == 'x')
                {
                    if (x_bits[k] != -1)    //不允许在同一位出现多个x
                        flag = -1;
                    x_bits[k] = j;    //最多记录不同位上的x位置
                }
                if (i%col !=j && DISK[i][j][k] == '1')
                    OneNum[k]++;
            }
        }
        //复制校验位
        memcpy(CkCode, DISK[i][i%col], BITS);
        for (int k = 0; k < bits;k++)    //将对应位置上的数据进行纠错
        {
            if (x_bits[k] != -1)    //该行有x数据,需要纠错(纠错就避免了检错)
            {
                if (ct == 'E')    //偶校验
                {
                    if ((CkCode[k] == '0'&&OneNum[k] % 2 == 1) || (CkCode[k] == '1' && OneNum[k] % 2 == 0))
                        DISK[i][x_bits[k]][k] = '1';
                    else
                        DISK[i][x_bits[k]][k] = '0';
                }
                else    //奇校验
                {
                    if ((CkCode[k] == '1'&&OneNum[k] % 2 == 1) || (CkCode[k] == '0' && OneNum[k] % 2 == 0))
                        DISK[i][x_bits[k]][k] = '1';
                    else
                        DISK[i][x_bits[k]][k] = '0';
                }
            }
            else    //该行没有x数据,直接检错
            {
                if (ct == 'E' && ((CkCode[k] == '1'&&OneNum[k] % 2 == 0) || (CkCode[k] == '0' && OneNum[k] % 2 == 1)))
                    flag = -1;
                if (ct == 'O' && ((CkCode[k] == '1'&&OneNum[k] % 2 == 1) || (CkCode[k] == '0' && OneNum[k] % 2 == 0)))
                    flag = -1;
            }
        }
    }
    getchar();
    return flag;
}

将字符串二进制转为16进制输出

//转换16进制输出
void printValidData()
{
    int n = 0;
    int m;
    //获取每一行数据,进行输出
    for (int i = 0; i < row; i++)
    {
        m = 0;
        for (int j = 0; j < col; j++)
        {
            if (i%col==j) continue;    //跳过校验位
            for (int k = 0; k < bits; k++)
            {
                if (DISK[i][j][k] == '1')
                    n |= 0x01;
                else
                    n |= 0x00;
                m++;
                if (m == 4)
                {
                    printf("%X", n);
                    n = 0, m = 0;
                }
                n <<= 1;
            }
        }
        //获取完一行
        if (m > 0 && m != 4)
        {
            m++;
            while (m != 4)
            {
                n |= 0x00,n <<= 1;
                m++;
            }
            printf("%X", n);
        }
    }
    printf("
");
}

主函数

void main()
{
    int c = 1, flag;
    FILE* fp = freopen("data7.in", "r", stdin);
    freopen("data7.out", "w", stdout);

    while (!feof(fp))
    {
        flag = getDiskData();
        if (flag == 0) break;
        if (flag == -1)
            printf("Disk set %d is invalid.
",c++);
        else 
        {
            printf("Disk set %d is valid, contends are: ",c++);
            printValidData();
        }
    }

    freopen("CON", "r", stdin);
    freopen("CON", "w", stdout);
}
原文地址:https://www.cnblogs.com/ssyfj/p/11162287.html