首页 > 解决方案 > 类数组正在破坏数据

问题描述

我正在尝试使用 C++制作类似于NEAT的 AI。但是,我遇到了让人口正常工作的问题。当我使用一组自定义对象时,存储在每个对象属性中的一些数据被损坏了。在分配网络后,将连接中的每个连接内的数据设置为0xDDDDDDDD。当我在构造函数中测试给定属性时,它看起来很好,但是当它在人口数组中分配时(在这种情况下t),所有数据都设置为0xDDDDDDDD类似于如果它被删除。

    // neat network.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include <cmath>

class node
{
public:
    int id;
    double value;
    node()
    {
        id = 0;
        value = 0;
    }
    node(int n)
    {
        id = n;
        value = 0;
    }
    void apply(double v)
    {
        value = v;
    }
    node copy()
    {
        return node(id);
    }
};

class connection
{
public:
    double weight;
    int in;
    int out;
    connection(int i, int o, double w=0)
    {
        in = i;
        out = o;
        weight = w;
        int r = rand();
        weight = ((double)(r % 2000) - 1000.0) / 1000.0;
        srand(r);
    }
    connection()
    {
        in = 0;
        out = 0;
        weight = 0;
    }
    void mutate()
    {
        weight += (double)((rand() % 50 - 2) / 1000);
    }
    connection copy()
    {
        connection ret = connection(in, out);
        ret.weight = weight;
        return ret;
    }
};

double sigmoid(double n)
{
    return 1 / (exp(-n) + 1);
}

class net
{
public:
    double* position;
    int quant;
    node* nodes;
    int* inpernode;
    connection* connections;
    int connlen;
    int inn;
    int ut;
    net(int ins, int outs, int cn=0)
    {
        inn = ins;
        ut = outs;
        quant = ins+outs;
        nodes = new node[quant];
        position = new double[quant];
        connections = new connection[ins*outs];
        inpernode = new int[quant];
        for (int i = 0; i < quant; i++)
        {
            nodes[i] = node(i);
            if (i < ins)
            {
                position[i] = 0;
            }
            else
            {
                position[i] = 1;
            }
            inpernode[i] = 0;
        }
        int c = 0;
        connlen = ins * outs;
        for (int i = 0; i < ins; i++)
        {
            for (int j = ins; j < quant; j++)
            {
                connections[c] = connection(i, j);
                //std::cout << connections[c].weight << " ";
                inpernode[j]++;
                c++;
            }
        }
        //std::cout << connections[0].in << std::endl;
    }
    net()
    {
        connections = new connection();
        connlen = 0;
        inn = 0;
        inpernode = new int();
        quant = 0;
        nodes = new node();
        position = new double();
        ut = 0;
    }
    ~net()
    {
        delete[] position;
        delete[] nodes;
        delete[] connections;
        delete[] inpernode;
    }
    double* apply(double* inputs)
    {
        bool* finished = new bool[quant];
        //std::cout << connections[0].in;
        bool* cfinished = new bool[connlen];
        int* ndone = new int[quant];
        bool alldone=false;
        for (int i = 0; i < connlen; i++)
        {
            cfinished[i] = false;
        }
        for (int i = 0; i < quant; i++)
        {
            finished[i] = i<inn;
            ndone[i] = 0;
            nodes[i].value = 0;
            if (i < inn) {
                nodes[i].value = inputs[i];
            }
        }
        while (!alldone)
        {
            alldone = true;
            for (int i = 0; i < connlen; i++)
            {
                //std::cout << quant;
                if (finished[connections[i].in] && !cfinished[i])
                {
                    alldone = false;
                    nodes[connections[i].out].value += connections[i].weight * sigmoid(nodes[connections[i].in].value);
                    cfinished[i] = true;
                    ndone[connections[i].out]++;
                    if (ndone[connections[i].out] == inpernode[connections[i].out])
                    {
                        finished[connections[i].out] = true;
                    }
                }
            }
        }
        double* outs = new double[ut];
        for (int i = inn; i < inn + ut; i++)
        {
            outs[i - inn] = sigmoid(nodes[i].value);
        }
        return outs;
    }
    net copy()
    {
        net ret = net(inn, ut);
        ret.quant = quant;
        ret.connlen = connlen;
        for (int i = 0; i < quant; i++)
        {
            ret.position[i] = position[i];
            ret.nodes[i] = nodes[i].copy();
            ret.inpernode[i] = inpernode[i];
        }
        for (int i = 0; i < connlen; i++)
        {
            ret.connections[i] = connections[i].copy();
        }
        return ret;
    }
    net mutate()
    {
        net ret = copy();
        for (int i = 0; i < quant; i++)
        {
            if (rand() % 20 == 19)
            {
                connections[i].mutate();
            }
            if (rand() % 333 == 332)
            {
                nodes[quant] = node(quant);
                int temp = connections[i].out;
                connections[i].out = quant;
                connections[connlen] = connection(quant, temp);
                quant++;
                connlen++;
            }
        }
        if (rand() % 33 == 32)
        {
            bool done = false;
            int tries = 200;
            while (!done)
            {
                tries--;
                if (tries < 0)
                    done = true;
                int inc = rand() % quant;
                if (position[inc] == 1)
                    continue;
                int utc = rand() % quant;
                if (position[inc] > position[utc])
                    continue;
                bool found = false;
                for (int i = 0; i < connlen; i++)
                {
                    if (connections[i].in == inc && connections[i].out == utc)
                        found = true;
                }
                if (!found)
                {
                    connections[connlen] = connection(inc, utc);
                    connlen++;
                    done = true;
                }
            }
        }
        return ret;
    }
};

int main()
{
    srand(2002);
    /*net test = net(2, 1);
    net test2 = net(2, 1);
    std::cout << test.connections[0].weight << " " << test.connections[1].weight << std::endl;
    std::cout << test2.connections[0].weight << " " << test2.connections[1].weight << std::endl;
    double ins[] = { 0,0 };
    double* cp = test.apply(ins);
    std::cout << cp[0]<<" ";
    cp = test2.apply(ins);
    std::cout << cp[0];*/
    net t[1];
    t[0] = net(2, 1);
    std::cout << t[0].inn << " " << t[0].ut << std::endl;
    std::cout << t[0].connections[0].in << " " << t[0].connections[0].weight << std::endl;
    /*net pop[100];
    for (int i = 0; i < 100; i++)
    {
        pop[i] = net(2, 1);
        std::cout << pop[i].connections[0].in << " " << pop[i].connections[1].in << " " << std::endl;
    }
    int* scores = new int[100];
    for (int gen = 0; gen < 100; gen++)
    {
        for (int i = 0; i < 100; i++)
        {
            scores[i] = 0;
            double inp[] = { 0, 0 };
            double* t = pop[i].apply(inp);
            if (t[0] < .1)
                scores[i]++;
            inp[1] = 1;
            t = pop[i].apply(inp);
            if (t[0] > .9)
                scores[i]++;
            inp[0] = 1;
            inp[1] = 0;
            t = pop[i].apply(inp);
            if (t[0] > .9)
                scores[i]++;
            inp[1] = 1;
            t = pop[i].apply(inp);
            if (t[0] < .1)
                scores[i]++;
            if (scores[i] == 4)
            {
                std::cout << "Solved" << gen;
                return 0;
            }
            scores[i] -= pop[i].connlen + pop[i].quant;
        }
        int sum = 0;
        for (int i = 0; i < 100; i++)
            sum += scores[i];
        double avg = (double)(sum / 100);
        net newpop[100];
        int nplen = 0;
        for (int i = 0; i < 100; i++)
        {
            if (scores[i] > avg)
            {
                newpop[nplen++] = pop[i];
            }
        }
        for (int i = 0; i < nplen; i++)
        {
            pop[i] = newpop[i];
        }
        for (int i = nplen; i < 100; i++)
        {
            pop[i] = pop[i % nplen].mutate();
        }
    }*/
    return 0;
}

// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
// Debug program: F5 or Debug > Start Debugging menu

// Tips for Getting Started: 
//   1. Use the Solution Explorer window to add/manage files
//   2. Use the Team Explorer window to connect to source control
//   3. Use the Output window to see build output and other messages
//   4. Use the Error List window to view errors
//   5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
//   6. In the future, to open this project again, go to File > Open > Project and select the .sln file

标签: c++visual-c++memory-managementneural-networkartificial-intelligence

解决方案


随机顺序的注释

* 重量被忽略/多次调用srand

connection(int i, int o, double w=0)
{
  in = i;
  out = o;
  weight = w;

  int r = rand();
  weight = ((double)(r % 2000) - 1000.0) / 1000.0;
  srand(r);
}

用户指定的权重总是被随机值覆盖。

您可能应该编写两个不同的构造函数:

connection(int i, int o)
  : weight((static_cast<double>(rand() % 2000) - 1000.0) / 1000.0),
    in(i), out(o)  
{
}

connection(int i, int o, double w) : weight(w), in(i), out(o) {}

考虑到srand 必须避免多次调用。

srand不像你想象的那样工作。初始调用rand返回一个定义明确的值,然后将其用作种子:这个种子根本不是随机的,因此对/的调用链中没有任何随机性srandrand

此外,混合调用srand可能会影响质量rand(这本身就不是很好)。

*mutate总是返回0

void mutate()
{
  weight += (double)((rand() % 50 - 2) / 1000);
}

应该是:

void mutate()
{
  weight += (double)((rand() % 50 - 2)) / 1000;
}

* 重新分配

~net()
{
  delete[] position;
  delete[] nodes;
  delete[] connections;
  delete[] inpernode;
}

这假设 , position, nodes,connectionsipernode对象数组,但它们可以是单个对象,例如:

net()
{
  connections = new connection();
  inpernode = new int();
  nodes = new node();
  position = new double();

  // ...
}

(请参阅C++ 中的 delete vs delete[] 运算符)。

报告的问题

net t[1];

构造单个net对象调用net().

t[0] = net(2, 1);

有(至少)四种效果:

  1. 创建一个新net对象(net(2, 1));
  2. 删除现有t[0]对象;
  3. net(2, 1)里面的副本t[0]
  4. 删除net(2, 1).

第 2 点是有问题的(见上文:释放)。

在第 3 点之后,您有:

t[0].connections ----points to----------> [connection1, connection2]
                                                     ^
                                                     |
net(2, 1).connections ----points to------------------+

第 4 点之后:

t[0].connections ----points to----------> [freed memory area]

`net(2, 1)` destroyed

和 也是position如此inpernode

现在

std::cout << t[0].connections[0].in;

访问一个已释放的内存区域(0xDDDDDDDD值...)。

t[0](在末尾)的删除main涉及释放已释放的内存块(在 tcache 2 中检测到双重释放之类的错误)。

TL;博士

  • 至少在类中添加一个复制赋值运算符和一个复制构造函数(规则三net。也改为net()

    net()
    {
      connections = nullptr;
      connlen = 0;
      inn = 0;
      inpernode = nullptr;
      quant = 0;
      nodes = nullptr;
      position = nullptr;
      ut = 0;
    }
    
  • 所有这些令人头疼的问题都是由于手动内存管理而发生的。您可以使用 s轻松重写代码std::vector,避免编写任何自定义复制/移动构造函数、赋值运算符或析构函数(零规则)......这就是要走的路。

PS我没有检查net::apply, net::mutate,net::copy成员函数。


推荐阅读