嗨我似乎无法理解为什么我的程序在运行时会在visual studio中崩溃。调试时我收到错误“整数除以0”。我已经在代码中注释了发生错误的行。该程序的目的是演示一个基本的k-means聚类算法。
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;
int m1, o1;
int mem1[10];
int sum1;
int in1;
int m2, o2;
int mem2[10];
int sum2;
int in2;
int arr[10] {21, 135, 45, 174, 10, 64, 85, 44, 96, 125};
int random_mean()
{
m1 = rand() % 255;
m2 = rand() % 255;
return 0;
}
int clustering(int x)
{
int d1;
int d2;
d1 = sqrt((pow(m1 - x, 2)));
d2 = sqrt((pow(m2 - x, 2)));
if (d1 < d2)
{
mem1[in1] = x;
in1++;
}
else if (d2 < d1)
{
mem2[in2] = x;
in2++;
}
return 0;
}
int updatemean()
{
o1 = m1;
o2 = m2;
for (size_t i = 0; i < in1; i++)
{
sum1 += mem1[i];
}
for (size_t i = 0; i < in2; i++)
{
sum2 += mem2[i];
}
m1 = sum1 / in1; //error is taking place on this line
m2 = sum2 / in2;
in1 = 0;
in2 = 0;
return 0;
}
int main()
{
random_mean();
do
{
for (size_t i = 0; i < 10; i++)
{
clustering(arr[i]);
}
updatemean();
} while (o1 != m1 && o2 !=m2);
return 0;
}
程序首先将值随机分配给(m1)和(m2),作为集群均值。然后程序调用(clustering)函数10次,每次调用时,数组(arr)中的值传递给函数。
在聚类函数中,计算通过值与每个均值之间的距离。然后将该值添加到(mem1)或(mem2),具体取决于具有最短距离的值。添加后,(in1)或(in2)递增。
在传递了所有10个值并将其添加到各自的数组之后,程序会调用(updatemean)函数,这就是问题发生的地方。该函数将值从(m1)和(m2)复制到变量(o1)和(o2)。然后计算并保存(mem1)和(mem2)的总和。然后更新(m1)和(m2)。每个都使用数组(mem1)或(mem2)之和除以数组中的元素数(in1)或(in2)进行更新。例如,m1 = sum1 / in1。然后重置变量(in1)和(in2)。
程序重复步骤2和3,直到满足do / while循环的条件。我似乎无法弄清楚为什么在第二个do / while循环中,(updatemean)函数在它之前的(cluster)函数再次递增(in1)和(in2)时会发出错误。
感谢阅读。
答案 0 :(得分:1)
考虑每个d1和d2的情况,语句:d1&lt; d2为真,则in2的值永远不会递增。同样,如果陈述d1&lt; d2,总是为false,in1的值永远不会递增,它们的值将保持为0,从而导致divis为零:
m1 = sum1 / in1;
你可以通过设置一个简单的if来解决这个问题,它检查in1是否实际为零:
if( in1 == 1) m1 = 0; else m1 = sum1 / in1;
祝你好运。
答案 1 :(得分:1)
你对均值的计算是错误的,因为你正在为sum1和sum2使用全局变量,并且永远不会将这些全局变量重置为零。
因此,您的计算会在之前的平均值之上累积新值,而sum1和sum2会向无穷大方向偏移。经过几次迭代后,所有点落入一个簇中,另一个簇的点数降至零,因此在该阶段得到的是零除错误。
只需将sum1和sum2定义为本地(初始化)变量,就可以了:
int updatemean()
{
o1 = m1;
o2 = m2;
int sum1 = 0;
for (size_t i = 0; i < in1; i++)
sum1 += mem1[i];
m1 = sum1 / in1;
in1 = 0;
int sum2 = 0;
for (size_t i = 0; i < in2; i++)
sum2 += mem2[i];
m2 = sum2 / in2;
in2 = 0;
return 0;
}
我只纠正了你被零除错误。正如其他人所告知的那样,并且正如此错误所证明的那样,在任何情况下都建议使用更多局部变量和更少的全局变量。
顺便说一下,我不知道算法本身,但我很惊讶地发现你正在进行整数除法?虽然输入数据是整数,但不应该是和,质心是双精度浮点数吗?答案 2 :(得分:1)
从我所看到的,错误发生在updatemean()
,因为in1
或in2
仍为0.我立即看到两个可能导致这种情况的案例。
案例1 :如果在每次致电clustering(int x)
期间,d1
或d2
始终大于另一次,则in1
或in2
可能从未超过0。
案例2 :如果在每次运行期间d1==d2
,则不执行任何操作,in1
和in2
都不会超过0。
我认为案例2更有可能。在原始代码中,您说的是int d1 = sqrt((pow(m1 - x, 2)));
和int d2 = sqrt((pow(m2 - x, 2)));
。这里的问题是sqrt()
返回一个double,你将它分配给一个整数。因此,您可能会对值进行四舍五入,因此d1
与d2
相同。也就是说,在回顾这一行时,我有一些问题。
为什么你在说sqrt(pow(m#-x, 2));
在这个陈述中,你基本上是在说“将它平方然后取平方根。”在数学上,这个陈述应该总是返回值m#-x
。 逻辑:sqrt(10^2) = sqrt(100) = 10
。
另外,在原始代码中,您说int arr[10] {21, 135, 45, 174, 10, 64, 85, 44, 96, 125};
。您在=
和int arr[10]
之间缺少等号({21, 135, 45, 174, 10, 64, 85, 44, 96, 125};
)。
以下是您的代码的修改版本。我添加了注释,更改了一些变量类型,修改了几行,并且我包含了一些调试语句,这些语句在整个代码中打印出变量的值(当_DEBUG_ == 1
时)。希望这可以帮助您修改,改进和进一步开发代码:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <cmath>
/* just used a preprocessor way to check if debug statements should be compiled */
#define _DEBUG_ 1 // 1=debug mode; 0=non-debug mode
using namespace std;
int m1, o1;
int mem1[10] = {0};
int sum1;
int in1;
int m2, o2;
int mem2[10] = {0};
int sum2;
int in2;
// Added an equal sign between "arr[10]" and "{21, 135, ...}"
// Previously you had:
// int arr[10] {21, 135, 45, 174, 10, 64, 85, 44, 96, 125};
int arr[10] = {21, 135, 45, 174, 10, 64, 85, 44, 96, 125};
void random_mean()
{
m1 = rand() % 255;
m2 = rand() % 255;
}
void clustering(int x)
{
// Changed from int to double because sqrt() returns a double:
double d1;
double d2;
// What is this? You square it just to square root it?
d1 = sqrt((pow(m1 - x, 2)));
d2 = sqrt((pow(m2 - x, 2)));
if (d1 < d2)
{
mem1[in1] = x;
in1++;
}
else if (d1 > d2)
{
mem2[in2] = x;
in2++;
} // Perhaps you should add a condition in case d1 == d2 (although it is unlikely to occur)
#if _DEBUG_ == 1
// Show what the in1 and in2 values are:
std::cout << "Debug: At the end of clustering() function.\nin1=" << in1 << "; in2=" << in2 << std::endl;
#endif
}
void updatemean()
{
o1 = m1;
o2 = m2;
for (int i = 0; i < in1; i++)
{
sum1 += mem1[i];
}
for (int i = 0; i < in2; i++)
{
sum2 += mem2[i];
}
#if _DEBUG_ == 1
// if _DEBUG_ == 1, then the statements within this preprocessor block
// will be compiled. Here we will include print statements to show us
// some of the variable values:
std::cout << "Debug: In updatemean() before division.\nin1=" << in1 << "; in2=" << in2 << std::endl;
#endif
m1 = sum1 / in1; //error is taking place on this line
m2 = sum2 / in2;
in1 = 0;
in2 = 0;
}
int main()
{
random_mean();
// Get the size of the array for the for-loop below:
// (This allows you to adjust the size of the arr above without having to manually
// change the for loop below.)
int iArrayLen = sizeof(arr)/sizeof(arr[0]);
do
{
for (int i = 0; i < iArrayLen; i++)
{
#if _DEBUG_ == 1
// Show which iteration count we are at:
std::cout << "Debug: in1=" << in1 << "; in2=" << in2 << std::endl;
#endif
clustering(arr[i]);
}
#if _DEBUG_ == 1
// Show which in1 and in2 values
std::cout << "Debug: In do-while loop. Outside for loop.\nin1=" << in1 << "; in2=" << in2 << std::endl;
#endif
updatemean();
} while (o1 != m1 && o2 !=m2);
return 0;
}
另外,我改变了所有刚刚返回0的函数,现在返回void(当然除了main)。将函数声明为每次返回0时返回 int 而不是某些有用的值是没有意义的。