整数不更新

时间:2015-10-23 23:20:23

标签: c++ k-means

嗨我似乎无法理解为什么我的程序在运行时会在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;
}
  1. 程序首先将值随机分配给(m1)和(m2),作为集群均值。然后程序调用(clustering)函数10次,每次调用时,数组(arr)中的值传递给函数。

  2. 在聚类函数中,计算通过值与每个均值之间的距离。然后将该值添加到(mem1)或(mem2),具体取决于具有最短距离的值。添加后,(in1)或(in2)递增。

  3. 在传递了所有10个值并将其添加到各自的数组之后,程序会调用(updatemean)函数,这就是问题发生的地方。该函数将值从(m1)和(m2)复制到变量(o1)和(o2)。然后计算并保存(mem1)和(mem2)的总和。然后更新(m1)和(m2)。每个都使用数组(mem1)或(mem2)之和除以数组中的元素数(in1)或(in2)进行更新。例如,m1 = sum1 / in1。然后重置变量(in1)和(in2)。

  4. 程序重复步骤2和3,直到满足do / while循环的条件。我似乎无法弄清楚为什么在第二个do / while循环中,(updatemean)函数在它之前的(cluster)函数再次递增(in1)和(in2)时会发出错误。

  5. 感谢阅读。

3 个答案:

答案 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(),因为in1in2仍为0.我立即看到两个可能导致这种情况的案例。

案例1 :如果在每次致电clustering(int x)期间,d1d2始终大于另一次,则in1in2可能从未超过0。

案例2 :如果在每次运行期间d1==d2,则不执行任何操作,in1in2都不会超过0。

我认为案例2更有可能。在原始代码中,您说的是int d1 = sqrt((pow(m1 - x, 2)));int d2 = sqrt((pow(m2 - x, 2)));。这里的问题是sqrt()返回一个double,你将它分配给一个整数。因此,您可能会对值进行四舍五入,因此d1d2相同。也就是说,在回顾这一行时,我有一些问题。

为什么你在说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 而不是某些有用的值是没有意义的。