实验代码一:用来链表实现单向图

Posted niubidexiebiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实验代码一:用来链表实现单向图相关的知识,希望对你有一定的参考价值。

#include <iostream>
#include <algorithm>
#include "MnistFile.cpp"
#include <cmath>

using namespace std;
const int synapseNums = 800;

class Node {
public:
    //int weight = 1;//权重
    double value = 0;//保持计算和
    Node *pre[synapseNums];//pre[0]指向前一层节点
    Node *next;//链接下一个节点
};
class Chain {//循环链表
public:
    // start->input->others
    Node *start;//指向输出层的开始节点
    Node *input;//指向输入层的开始节点
    Node *others;//指向中间层的节点,动态增删
    Node *end;//整个链的最后节点,下一个节点指向start
    Node null;//删除节点造成的空指针集中指向的节点
    Node **outputLink;//指向输出层的节点
};
class Network {
public:
    Chain chain;
    const int inputNodeNums;//输入节点维度
    const int outputNodeNums;//输出节点维度
    int othersNodeNums = 0;//中间层节点数目
    int inputIndexValue = -1;//当前标签值

public:
    Network(int in, int out);
    ~Network();
    void init();//建立网络结构
    void setNull(Node *p);
    void setNodes(Node **p, int nums);
    void inputValue(vector<double> input, double index);
    double activate(double x);
    int softMax(double **x, int n);
    int retIndexOf(Node *p, Node *q);
    int retNullIndex(Node *p);
    void forward();
    double getIndexValue(int index);//得到标签的输出值
    int getIndex();//得到正确的标签值
    double getChange();//得到改变边后输出的变化
    void newNode();
    void deleteNode(Node *p);
    void deleteNode2(Node *p);
    void newEdge(Node **p);
    void newEdge2(Node **ip, vector<double> &labels, vector<vector<double> >&images);
    void newOutputEdge();
    //void deleteedge();
    void train(int trainSampleNums, vector<double>&labels, vector<vector<double> > &images);
    void train2(int trainSampleNums, vector<double>&labels, vector<vector<double> > &images);
    int predictIndex();//预测的标签值
    //void writeDate();
    //void readData(string s);
    double evalStudyRate(int testSampleNums, vector<double> &labels, vector<vector<double> >&images);
};
void Network::setNull(Node *p) {
    for (int i = 0; i < synapseNums; ++i)
        p->pre[i] = &chain.null;
}
Network::Network(int in, int out) : inputNodeNums(in), outputNodeNums(out) {
    //设置null节点
    chain.null.next = nullptr;
    chain.null.value = 0;
    setNull(&chain.null);
    //初始化
    init();
    //设置outputLink
    chain.outputLink = new Node*[outputNodeNums];
    Node *p = chain.start;
    for (int i = 0; i < outputNodeNums; ++i) {
        chain.outputLink[i] = p;
        p = p->next;
    }
}
Network::~Network() {
    cout << "~Network ..." << endl;
    Node *p = nullptr;
    Node *q = nullptr;
    p = chain.start;
    chain.start = nullptr;
    while (p != chain.input) {
        //cout << "p isn‘t null" << endl;
        q = p->next;
        delete p;
        p = q;
    }

    delete  [] chain.outputLink;
}
void Network::setNodes(Node **p, int nums) {
    for (int i = 0; i < nums; i++) {
        Node *q = new Node;
        setNull(q);
        (*p)->next = q;
        *p = q;
    }
}
void Network::init() {
    cout << "init ..." << endl;
    //将所有节点拉成链
    //建立输出层
    Node *p = new Node;
    setNull(p);
    chain.start = p;//p前节点
    setNodes(&p, outputNodeNums - 1);

    //建立输入层
    setNodes(&p, 1);
    chain.input = p;//p前节点
    setNodes(&p, inputNodeNums - 1);

    //连接others
    setNodes(&p, 1);
    othersNodeNums = 1;

    //连接最后节点指针
    chain.others = chain.end = p;
    chain.end->next = chain.start;//循环链表
}
double Network::activate(double x) {
    return 1.0 / (1.0 + exp(-x));
}
int Network::softMax(double **x, int n) {
    double *p = *x;
    int k = 0;
    for (int i = 0; i < n; ++i) {
        if (p[k] < p[i])
            k = i;
    }
    return k;
}
void Network::inputValue(vector<double> input, double index) {
    Node *p = chain.input;
    double s = 0;
    for (int j = 0; j < inputNodeNums; ++j) {
        s += input[j];
    }
    for (int i = 0; i < inputNodeNums; ++i) {
        p->value = input[i];// / s;//正规化
        p = p->next;
    }

    inputIndexValue = index;
    //cout << "Network::inputValue(vector<double> input, double index); end!" << endl;
}
void Network::forward() {
    Node *p = chain.others;
    double sum = 0;
    while (p != chain.input) {
        for (int i = 0; i < synapseNums; ++i) {
            sum = sum + p->pre[i]->value;
        }
        p->value = activate(sum);
        //cout << p->value << " ";
        p = p->next;
    }
}
void Network::newNode() {
    cout << "newNode ..." << endl;
    Node *p = new Node;
    setNull(p);
    othersNodeNums++;
    chain.end->next = p;
    chain.end = p;
    chain.end->next = chain.start;
}
void Network::deleteNode(Node *p) { // 
    //删除一个节点,需要找到指向该节点的指针,然后再删除该节点
    //找到p的前驱
    Node *q = chain.others;
    while (q) {
        if (q->next == p || q->next == chain.start) {
            break;
        } else {
            q = q->next;
        }
    }
    if (q->next != p)
        return;//没找到这个节点

    //找到指向p的边修改为null
    Node *t = chain.others;
    while (t) {
        for (int i = 0; i < synapseNums; ++i) {
            if (t->pre[i] == p) {
                t->pre[i] = &chain.null;//t->pre[i] == nullptr;
            }
        }
        t = t->next;
        if (t == chain.input)
            break;
    }
    //删除节点
    q->next = p->next;
    delete p;
    othersNodeNums--;
}
void Network::deleteNode2(Node *p) {
    p->value = 0;
}

double Network::getIndexValue(int index) {
    return chain.outputLink[index]->value;
}
double Network::getChange() {
    forward();
    int index = getIndex();
    return getIndexValue(index);
}
int Network::getIndex() {
    return this->inputIndexValue;
}
int Network::predictIndex() {
    double x[outputNodeNums];
    memset(x, 0, outputNodeNums);
    Node *p = chain.start;
    for (int i = 0; i < outputNodeNums; ++i) {
        x[i] = p->value;
        p = p->next;
    }
    double *q = x;
    return softMax(&q, outputNodeNums);
}
//返回p指向q的下标
int Network::retIndexOf(Node *p, Node *q) {
    //p->q?
    for (int i = 0; i < synapseNums; ++i) {
        if (p->pre[i] == q) {
            return i;//找到了,p的第i个指针指向q
        }
    }
    return -1;//没找到
}
//寻找空闲指针
int Network::retNullIndex(Node *p){
    Node *q = &chain.null;
    for (int i = 0; i < synapseNums; ++i) {
        if (p->pre[i] == q)
            return i;
    }
    return -1;//说明满了,没有空闲的
}
void Network::newEdge(Node **ip) {
    cout << "newEdge ..." << endl;
    Node *p = *ip;
    Node *t = chain.input;
    double old = 0;
    double now = 0;
    for (int i = 0; i < synapseNums;) {
        //测试当前right index的输出值
        old = getChange();

        //连接边
//        i = retNullIndex(p);
//        if (i == -1)
//            break;//这里不对,与下面的i++冲突
        p->pre[i] = t;//当前节点指向t

        //修改output层的正确输出的指针指向p
        int re = retIndexOf(chain.outputLink[getIndex()], p);
        if (re == -1){
            int rn = retNullIndex(chain.outputLink[getIndex()]);
            if (rn == -1)
                cerr << "retNullIndex is overfill!!
";
            chain.outputLink[getIndex()]->pre[rn] = p;
        }

        //测试当前连接后的right index的输出值
        now = getChange();

        //比较,若大于,则连接,否则连接下一个节点
        if (now > old)
            ++i;
        t = t->next;

        if (t == chain.start)
            break;
        if(predictIndex() == getIndex())
            break;//如果得到正确的输出就跳出

       // cout << i << " ";
    }
}
//void
//void Network::newOutputEdge() {
//    Node *p = chain.start;//输出层节点指针
//    double old = 0;
//    double now = 0;
//    Node *q = chain.input;//输入层和中间层节点指针
//
//    while (p != chain.input){
//        int r = retNullIndex(p);//空闲指针
//
//        p = p->next;
//
//    }
//}
double Network::evalStudyRate(int testSampleNums, vector<double> &labels, vector<vector<double> >&images) {
    cout << "evalStudyRate : " ;
    //根据输入的测试集数目来评估正确率
    int rightNums = 0;
    for (int i = 0; i < testSampleNums; ++i) {
        inputValue(images[50000+i], labels[50000+i]);
        forward();//前向传播
        if (predictIndex() == inputIndexValue) //预测
            rightNums++;
    }
    return (double)rightNums / testSampleNums * 100.0;
}
void Network::newEdge2(Node **ip, vector<double> &labels, vector<vector<double> >&images) {
    cout << "newEdge ..." << endl;
    Node *p = *ip;
    Node *t = chain.input;
    double old = 0;
    double now = 0;
    for (int i = 0; i < synapseNums;) {
        //测试当前right index的输出值
        old = evalStudyRate(100, labels, images);

        //连接边
        p->pre[i] = t;//当前节点指向t

//        //修改output层的正确输出的指针指向p
//        int re = retIndexOf(chain.outputLink[getIndex()], p);
//        if (re == -1){
//            int rn = retNullIndex(chain.outputLink[getIndex()]);
//            if (rn == -1)
//                cerr << "retNullIndex is overfill!!
";
//            chain.outputLink[getIndex()]->pre[rn] = p;
//        }

        //测试当前连接后的right index的输出值
        now = evalStudyRate(100, labels, images);

        //比较,若大于,则连接,否则连接下一个节点
        if (now > old)
            ++i;
        t = t->next;

        if (t == chain.start)
            break;
        if(predictIndex() == getIndex())
            break;//如果得到正确的输出就跳出

        //cout << i << " ";
    }
}
//训练这个网络,得到网络结构
void Network::train(int trainSampleNums, vector<double>&labels, vector<vector<double> > &images) {
    cout << "train ... " << endl;
    //训练
    for (int i = 0; i < trainSampleNums; ++i) {
        //输入数据进入网络
        inputValue(images[i], labels[i]);

        //连接边
        newEdge(&chain.end);

        //新建节点
        newNode();

        cout << "othersNodeNums = " << othersNodeNums << endl;

        if (othersNodeNums >= 50) {
            cout << "if (othersNodeNums >= 8) stop" << endl;
            break;
        }
    }


    //评估学习率
    cout << evalStudyRate(9000, labels, images) << "%" << endl;
}
//训练这个网络,得到网络结构
void Network::train2(int trainSampleNums, vector<double>&labels, vector<vector<double> > &images) {
    cout << "train ... " << endl;
    //训练
    for (int i = 0; i < trainSampleNums; ++i) {
        //输入数据进入网络
        inputValue(images[i], labels[i]);

        //连接边
        newEdge(&chain.end);

        //新建节点
        newNode();

        cout << "othersNodeNums = " << othersNodeNums << endl;

        if (othersNodeNums >= 4) {
            cout << "if (othersNodeNums >= 8) stop" << endl;
            break;
        }
    }

//    Node *p = chain.others;
//    while (p != chain.input) {
//        newEdge2(&p, labels, images);
//    }

    //评估学习率
    cout << evalStudyRate(9000, labels, images) << "%" << endl;
}

int main(){
    //读数据
    cout << "read data..." << endl;
    vector<double>labels;
    read_Mnist_Label("train-labels.idx1-ubyte", labels);
    vector<vector<double>> images;
    read_Mnist_Images("train-images.idx3-ubyte", images);

    Network network(784, 10);
    network.train2(10, labels, images);
}

 

以上是关于实验代码一:用来链表实现单向图的主要内容,如果未能解决你的问题,请参考以下文章

C语言反转单向链表的代码

数据结构与算法什么是链表?并用代码手动实现一个单向链表

php实现数据结构单向链表

数据结构单向链表及其Java代码实现

链表的java实现(单向双向链表,单向链表的反转)

链表的java实现(单向双向链表,单向链表的反转)