c++ - 类数组正在破坏数据
问题描述
我正在尝试使用 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
解决方案
随机顺序的注释
* 重量被忽略/多次调用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
返回一个定义明确的值,然后将其用作种子:这个种子根本不是随机的,因此对/的调用链中没有任何随机性。srand
rand
此外,混合调用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
,connections
是ipernode
对象数组,但它们可以是单个对象,例如:
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);
有(至少)四种效果:
- 创建一个新
net
对象(net(2, 1)
); - 删除现有
t[0]
对象; net(2, 1)
里面的副本t[0]
;- 删除
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
成员函数。
推荐阅读
- admob - Admob 提供低填充率
- python - 从多个主题读取的python Kafka消费者的低速率
- c++ - 如何在 Visual Studio 2019 中启用编译器警告?
- swift - CLGeocoder() 意外返回 nil
- html - 对齐标签和文本字段
- package - 使用自定义包的 VHDL
- api - 注册失败时如何显示自定义错误消息?(Express、Vue、Axios)
- python - 如何在 Python 中按字符数对列表进行排序?
- python - 在 PyGame 中清除屏幕的最快方法是什么?
- wpf - 如何将数据网格列 ActualWidth 绑定到文本框 MaxLength?