算法習題---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] == '\n')
                {
                    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("\n");
}

主函數

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.\n",c++);
        else 
        {
            printf("Disk set %d is valid, contends are: ",c++);
            printValidData();
        }
    }

    freopen("CON", "r", stdin);
    freopen("CON", "w", stdout);
}
相關文章
相關標籤/搜索