【问题标题】:Integer not updating整数不更新
【发布时间】:2016-01-23 14:34:58
【问题描述】:

您好,我似乎无法弄清楚为什么我的程序在运行时会在 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),它们充当集群均值。然后程序调用(聚类)函数 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 循环中,当 (clustering) 函数在重新增加 (in1) 和 (in2) 之前, (updatemean) 函数会给出错误。

感谢阅读。

【问题讨论】:

  • 你需要初始化你的变量。
  • 嗯,一个很好的案例,开始学习如何使用你的工具链调试器。
  • 建议减少对全局变量的依赖。通过引用阅读,函数不必总是返回 int。

标签: c++ k-means


【解决方案1】:

考虑以下情况:对于每个 d1 和 d2,语句:d1

m1 = sum1 / in1;

你可以通过设置一个简单的 if 来解决这个问题,它检查 in1 是否真的为零:

if( in1 == 1) m1 = 0; else m1 = sum1 / in1;

祝你好运。

【讨论】:

  • 使用这种方法确实可以让程序运行,但是 in1 和 in2 永远不能为 0。
【解决方案2】:

据我所知,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() 返回一个双精度值,而您将它分配给一个整数。因此,您的值有可能被四舍五入,因此d1d2 相同。也就是说,在查看这条线时,我有一些问题。

你到底为什么要说sqrt(pow(m#-x, 2)); 在这个语句中,你本质上是在说“平方然后取平方根。” 从数学上讲,这个语句应该总是返回@987654338 的值@。 逻辑: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}; 之间缺少一个等号 (=)。

以下是您的代码的修改版本。我添加了 cmets,更改了一些变量类型,修改了几行,并包含了一些调试语句,这些语句在整个代码中打印出变量的值(当 _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 是没有意义的。

【讨论】:

  • 我自己也想过,不知道为什么会这样表达。我使用 sqrt(pow(m#-x, 2)) 的原因是为了计算欧几里得空间中的距离,我们使用: d=√(x_2 - x_1 ) 当我们有多个输入时,它变成: d= √((x_2 - x_1 )^2 + (y_2 - y_1 )^2 )
  • 也感谢您的建议,我现在就开始研究它。
  • @user3178205,计算欧几里得距离时的判别式是(DeltaX)^2 + (DeltaY)^2。您不能将它们分开并分别平方根。您必须将它们加在一起,然后平方根。这是因为a^2 + b^2 = c^2 因此c = sqrt(a^2 + b^2)
【解决方案3】:

您对均值的计算是错误的,因为您将全局变量用于 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;
}

不过,我只更正了您的除零错误。正如其他人所建议的那样,正如这个错误所证明的那样,在任何情况下都建议使用更多的局部变量和更少的全局变量。

顺便说一句,我不知道算法本身,但我很惊讶地意识到你正在做整数除法?虽然输入数据是整数,但总和和质心不应该是双精度浮点数吗?

【讨论】:

  • 感谢您的回复。我还是这个算法的新手,你说得对,我应该为这类事情使用更准确的数据类型。
  • 好的,那么你应该做一些改进:
  • (1) 将您的均值和变量总和更改为双精度数。 (2) 将 sqrt(pow(m-x,2)) 替换为 fabs(m-x),这样计算欧几里得距离的效率更高,可读性更高。那么你的手段将是准确的。否则,您的均值将被整数截断:例如 {0,3} 的均值将截断为 (0+3)/2 = 1 而不是正确的均值 (0.+3.)/2 = 1.5
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多