信息论实验-二元对称信道仿真(C++实现)

来源:互联网 发布:网络直播摄像头哪种好 编辑:程序博客网 时间:2024/06/06 23:16

二元对称信道模拟器

实验目的

加深理解二进制对称信道的工作原理,掌握通过高级编程语言生成伪随机数的方法。允许使用编程语言:C,C++等

实验要求

输入:BSC信道的错误概率,任意的二进制序列
输出:经BSC信道传输后的二进制序列

实验内容

二元对称信道定义

2元对称信道信道的转移概率为

P(j|k)={1p,  k=jp,   kj, k,j=0,1,...
p表示错误概率。
如下图所示
二元对称信道
对于二元对称信道DMC,其信道容量为C=1H(p)

C、C++产生伪随机数原理

C++中常用rand()函数生成随机数,但严格意义上来讲生成的只是伪随机数(pseudo-random integral number)。生成随机数时需要我们指定一个种子,如果在程序内循环,那么下一次生成随机数时调用上一次的结果作为种子。但如果分两次执行程序,那么由于种子相同,生成的“随机数”也是相同的。在工程应用时,我们一般将系统当前时间(Unix时间)作为种子,这样生成的随机数更接近于实际意义上的随机数。
rand函数不是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子相同的数调用 rand()会导致相同的随机数序列被生成。
srand((unsigned)time(NULL))则使用系统定时/计数器的值作为随机种子。每个种子对应一组根据算法预先生成的随机数,所以,在相同的平台环境下,不同时间产生的随机数会是不同的,相应的,若将srand(unsigned)time(NULL)改为srand(TP)(TP为任一常量),则无论何时运行、运行多少次得到的“随机数”都会是一组固定的序列,因此srand生成的随机数是伪随机数。
标准库中的两个用于产生随机数的函数srand()和rand().

函数原型:int rand(void)返回值:返回一个[0, RAND_MAX]间的随机整数函数原型:void srand(unsigned seed)输入参数:seedrand()的种子,用来初始化rand()的起始值。

系统在调用rand()之前都会自动调用srand(),如果用户在rand()之前曾调用过srand()给参数seed指定了一个值,那么 rand()就会将seed的值作为产生伪随机数的初始值;而如果用户在rand()前没有调用过srand(),那么系统默认将1作为伪随机数的初始 值。如果给了一个定值,那么每次rand()产生的随机数序列都是一样的
所以为了避免上述情况的发生我们通常用srand((unsigned)time(0))或者srand((unsigned)time(NULL))来 产生种子。如果仍然觉得时间间隔太小,可以在(unsigned)time(0)或者(unsigned)time(NULL)后面乘上某个合适的整数。

二元对称信道的仿真实现

程序主函数

int main(){    print();    float P_error;  //错误概率    Info *root = new Info;    cout << "!------请输入二元信道的错误概率:-----!" << endl <<"P_error =";    cin >> P_error;    getchar();    getBinaryString(root, P_error);    putBinaryString(root);    getchar();}

程序流程很简单,首先输入二元对称信道的错误概率,然后输入二元序列,最后将通过通道的二元对称序列输出。在输入二元序列的过程我采用链表的形式存储每一个二元码。对应的结构为:

struct Info{    unsigned char source;    unsigned char output;    struct Info *next;};

通过链表进行存储,有这样一个好处就是你不需要提前分配空间,程序会判断输入流中是否含有0或1的码,然后对给每一个码动态的分配存储空间,这样既不会造成提前分配很多空间造成空间浪费,也不会因为码字过长造成缓冲区溢出。
输入函数的代码如下

void getBinaryString(Info *root, float P){    unsigned char num;    Info *node = root;    Info *before;    cout << "!-----请输入二元信号序列:------!" << endl << endl;    num = cin.get();    srand((unsigned)time(NULL));    do     {        node->source = num;        float x;        x = rand();        x = x / float(RAND_MAX);        //cout << x << ' ';        if (x < P)        {            if (num == '0')            {                node->output = '1';            }            if (num == '1')            {                node->output = '0';            }        }        else        {            node->output = num;        }        num = cin.get();        node->next = new Info;        before = node;        node = node->next;    } while (num == '0' || num == '1');    before->next = NULL;}

输入函数的参数包括错误概率和根节点的地址,程序运行结束将会在根节点后链接读入的二元码信息,并完成通过信道的操作。
输出函数的代码如下

void putBinaryString(Info *root){    Info *node = root;    cout << endl << "!-------输出的二元序列为:-------!" << endl << endl;    while (node != NULL)    {        cout.put(node->output);        node = node->next;    }    cout << endl << "!-------仿真程序运行结束-------!" << endl << endl;}

输出函数执行的功能就是将链表中的通过信道后的数据输出。
程序仿真结果如下图所示
error = 0.3
可以看出此次仿真输入的错误概率为0.3,输出序列和输入序列有两个位是不同的,也就是通过信道时受到了干扰。下面我们看两种极端的情况,就能更高的理解二元对称信道的仿真过程了。
error=0
上图是错误概率为0时的仿真结果,可以看出输入的二元字符串和输出的字符串是完全一样的,即通过信道的过程不会发生错误。
error=1
上图是错误概率为1时的结果,可以看出,输入的二元字符串和输入的二元字符正好对应位置相反,及每一位的码字经过信道时都发生了错误。