【问题标题】:c array error- expect elements to be initialized, but they are not, and my setters appear not to work, sometimesc数组错误-期望元素被初始化,但它们没有被初始化,而且我的setter有时似乎不起作用
【发布时间】:2013-08-02 21:15:30
【问题描述】:

我无法创建二维数组并用值填充它,然后读取数组并获得完全不同的值。奇怪的是我有两个维护两个数组,其中一个是正确存储值,另一个不是。我很确定我也没有覆盖元素。我假设我犯了一些愚蠢的错误,这对于不熟悉 C 的人来说是显而易见的。

请注意我正在实现viterbi algorithm,但我理解的一般算法并且有一个有效的python实现,只是c中的数组让我很伤心。

我在做什么:

1) malloc 两个数组,它们被用作二维数组,但我分配了一个连续的内存块。我没有显式地初始化数组,因为我应该填写其中的每个条目,就像通过维特比算法的前进步骤一样。

double *viterbi_table = malloc(sizeof(double) * n_samples * n_states);
int *best_path_table = malloc(sizeof(int) * n_samples * n_states);

2) 对于维特比算法的前向部分,我遍历观察到的数据,并计算最可能的状态和每个状态的概率。

for (t = 1; t < n_samples; t++) // for each piece of observed data
{
    for (i = 0; i < n_states; i++)
    {
        max_state_index = 0;
        max_p = -DBL_MAX;
        // calculate the max state and probability by looping through all the states
        // yada yada...

        // IMPORTANT PART: We set the array values here
        viterbi_table[t * n_samples + i] = max_p;
        best_path_table[t * n_samples + i] = max_state_index;
        printf("\tbest_path_table[%d][%d] or [%d] = %d => %d\n", 
            i, t, t * n_samples + i, best_path_table[t * n_samples + i], max_state_index);
    }

    // IMPORTANT PART: print out rows of best path table to see if what we thought we inserted is in there
    if (debug)
    {
        printf("best_path, [ ", t);
        for (i = 0; i < n_states; i++)
        {
            printf("[%d], %d ", t * n_samples + i, best_path_table[t * n_samples + i]);
        }
        printf("]\n");
    }
}

3) 我运行代码,而不是让我设置的数组元素与我认为我设置的元素匹配,而是得到看起来像未初始化元素的大负数或正数。是什么赋予了?我为这些块分配了一个值。这是显示问题的输出的选定部分。

t=36 => sample=X
    best_path_table[0][36] or [1404] = 0 => 0
    best_path_table[1][36] or [1405] = 0 => 0
    best_path_table[2][36] or [1406] = 0 => 0
    best_path_table[3][36] or [1407] = 0 => 0
    ...
best_path, [ [1404], 1399607453 [1405], -1070347604 [1406], 1399607453 [1407], 0 ... ]

通过对比,下面这个是正确的。

t=37 => sample=X
    best_path_table[0][37] or [1443] = 3 => 3
    best_path_table[1][37] or [1444] = 3 => 3
    best_path_table[2][37] or [1445] = 3 => 3
    ...
best_path, [ [1443], 3 [1444], 3 [1445], ... ]

当我为一小段数据运行代码时,比如

observation#
1) correct
2-3) garbage
4) correct
4-5) garbage
and so on

代码

见this gist。它不依赖于 3rd 方库。

编辑:

维特比表的第一行在算法前向部分之前的一步中初始化。

for (i = 0; i < n_states; i++)
{
    state_i = states[i];
    sample_t = samples[0];
    viterbi_table[i*n_samples]
        = prior(state_i, 0, true) + emission(sample_t, state_i, true);
}

EDIT2:

在之前的代码版本中,我正在执行更标准的二维数组初始化(在非连续块中)和相应的数组访问。对于更大的输入数据,这给了我bus error,这完全有道理。

double **viterbi_table = malloc(sizeof * viterbi_table * n_states);
int **best_path_table = malloc(sizeof * best_path_table * n_states);
...
viterbi_table[j][t - 1] = ...

EDIT3,解决方案评论:

原来这是一个愚蠢的下标错误。维特比和最佳路径数组的大小为 n_samples * n_states,即 17 * 39 = 663。这排除了我的示例中的任何 1404 索引。

具体的问题是我的数组索引是一团糟,因为我错误地使用了 n_samples 而不是 n_states。对于给定的观测指数 t (30) 和给定的状态指数 i (14),计算如下:

// Original, wrong
t * n_samples + i = 30 * 39 + 14 = 1184

// New, correct
t * n_states + i = 30 * 17 + 14 = 524

变量t 已经编码了我们所处的样本数,所以我们只需要将它乘以状态数。

EDIT4,固定代码: 固定代码可以找到here。我还为我的用例调整了发射和转换概率。

【问题讨论】:

  • 如果有的话,this 问题值得this one 投赞成票,因为它为研究解决方案所做的努力。 +1。
  • (t = 1; t &lt; n_samples; t++)这可能是错误的,如果你有n_samples,循环应该从0开始。
  • @KarolyHorvath t = 1 是正确的(根据我对算法的理解)。
  • max_state_index 始终为 0。
  • 即使 t = 1 是正确的,数组中仍然存在垃圾值。他们需要被清除。

标签: c arrays memory


【解决方案1】:

我添加了一个宏 CHECK_SUBSCRIPT 来检查下标是否在范围内,使用 assert。它触发了——下标失控了。

来源

评论已删除。

#include <assert.h>
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CHECK_SUBSCRIPT(x, n_samples)   assert((x) >= 0 && (x) < (n_samples) * n_states)

typedef enum { false, true } bool;
typedef double (*prob_function_def)(char, char, bool);

int n_states = 17;
int n_observations = 17;
char states[17] =
    { 'X', '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char observations[17] =
    { 'X', '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

void viterbi_log (
                char *samples,
                int n_samples,
                char *best_path,
                prob_function_def prior,
                prob_function_def transition,
                prob_function_def emission,
                bool debug
             )
{
    printf("\nviterbi...\n");

    int i, j, t, max_state_index;
    char state_i, state_j, sample_t;
    double trans_p, max_p;

    double *viterbi_table = malloc(sizeof(double) * n_samples * n_states);
    int *best_path_table = malloc(sizeof(int) * n_samples * n_states);

    for (int n33 = 0; n33 < n_samples * n_states; n33++)
    {
        CHECK_SUBSCRIPT(n33, n_samples);
        viterbi_table[n33] = 3.14159;
        best_path_table[n33] = 314159;
    }

    if (debug) printf("\nInitialization:\n");
    for (i = 0; i < n_states; i++)
    {
        state_i = states[i];
        sample_t = samples[0];
        CHECK_SUBSCRIPT(i*n_samples, n_samples);
        viterbi_table[i*n_samples]
            = prior(state_i, 0, true) + emission(sample_t, state_i, true);
        if (debug)
        {
            printf("\t");
            printf("log(prior[%c]) + log(emission[%c][%c]) = %e\n",
                state_i, sample_t, state_i, viterbi_table[i*n_samples]);
        }
    }

    if (debug) printf("\nForward:\n");
    for (t = 1; t < n_samples; t++)
    {
        sample_t = samples[t];
        if (debug) printf("t=%d => sample=%c\n", t, sample_t);
        for (i = 0; i < n_states; i++)
        {
            state_i = states[i];
            max_state_index = 0;
            max_p = -DBL_MAX;

            for (j = 0; j < n_states; j++)
            {
                state_j = states[j];
                CHECK_SUBSCRIPT(((t-1)*n_samples)+j, n_samples);
                trans_p = viterbi_table[((t - 1) * n_samples) + j]
                    + transition(state_i, state_j, true)
                    + emission(sample_t, state_j, true);
                if (trans_p > max_p)
                {
                    max_state_index = j;
                    max_p = trans_p;
                }
            }

            CHECK_SUBSCRIPT(t*n_samples+i, n_samples);
            viterbi_table[t * n_samples + i] = max_p;
            best_path_table[t * n_samples + i] = max_state_index;
            printf("\tbest_path_table[%d][%d] or [%d] = %d => %d\n",
                    i, t, t * n_samples + i, best_path_table[t * n_samples + i], max_state_index);
        }

        if (debug)
        {
            printf("best_path, [ ");
            for (i = 0; i < n_states; i++)
            {
                CHECK_SUBSCRIPT(t*n_samples+i, n_samples);
                printf("[%d], %d ", t * n_samples + i, best_path_table[t * n_samples + i]);
            }
            printf("]\n");
        }
    }

    if (debug)
    {
        printf("\nbest path table:\n");
        for (t = n_samples - 1; t > 0; t--)
        {
            printf("t=%d, [ ", t);
            for (i = 0; i < n_states; i++)
            {
                CHECK_SUBSCRIPT(t*n_samples+i, n_samples);
                printf("[%d], %d ", t * n_samples + i, best_path_table[t * n_samples + i]);
            }
            printf("]\n");
        }
    }

    free(viterbi_table);
    free(best_path_table);
}

double prior_prob (char state_i, char state_j, bool log_prob)
{
    if (!log_prob)
        return 0.25;
    else
        return -1.3862943611198906;
}

double transition_prob (char state_i, char state_j, bool log_prob)
{
    if (!log_prob)
    {
        if (state_i == 0 && state_j == 0)
            return 0.9;
        else if (state_i == 0 || state_j == 0)
            return 0.1;
        else if (state_i == state_j)
            return 0.9;
        else
            return 0.1;
    }
    else
    {
        if (state_i == 0 && state_j == 0)
            return -0.10536051565782628;
        else if (state_i == 0 || state_j == 0)
            return -2.3025850929940455;
        else if (state_i == state_j)
            return -0.10536051565782628;
        else
            return -2.3025850929940455;
    }
}

double emission_prob (char observation, char state, bool log_prob)
{
    if (!log_prob)
    {
        if (state == observation)
            return 0.8;
        else
            return 0.2;
    }
    else
    {
        if (state == observation)
            return -0.2231435513142097;
        else
            return -1.6094379124341003;
    }
}

void dtmf_example()
{
    bool debug = true;
    char *samples = "XXXXXXXXXXXXXX6X66XXXXXXXXXXXXXXXXXXXXX";
    int n_samples = strlen(samples);
    char *best_path = malloc(sizeof(int) * n_samples);

    viterbi_log(samples, n_samples, best_path,
        &prior_prob, &transition_prob, &emission_prob, debug);

    printf("\nbest path = { ");
    for (int t = 0; t < n_samples; t++)
        printf("%d ", best_path[t]);
    printf("}\n");
}

int main(void)
{
    dtmf_example();
    return 0;
}

断言被触发:

best_path_table[13][16] or [637] = 0 => 0
best_path_table[14][16] or [638] = 0 => 0
best_path_table[15][16] or [639] = 0 => 0
best_path_table[16][16] or [640] = 0 => 0
best_path, [ [624], 0 [625], 0 [626], 0 [627], 0 [628], 0 [629], 0 [630], 0 [631], 7 [632], 0 [633], 0 [634], 0 [635], 0 [636], 0 [637], 0 [638], 0 [639], 0 [640], 0 ]
t=17 => sample=6
Assertion failed: ((t*n_samples+i) >= 0 && (t*n_samples+i) < (n_samples) * n_states), function viterbi_log, file viterbi3.c, line 89.
Abort trap: 6

这是 CHECK_SUBSCRIPT 在:

        CHECK_SUBSCRIPT(t*n_samples+i, n_samples);
        viterbi_table[t * n_samples + i] = max_p;
        best_path_table[t * n_samples + i] = max_state_index;

【讨论】:

  • 似乎它正在访问未分配的内存,因此是未定义的行为。如果您为越界访问多次打印同一元素的值,则每次访问都会给出不同的值。
  • 如果您访问超出范围的值,您将得到任何结果。这就是调用未定义行为的美妙之处——如果你这样做,编译器永远不会出错。它看起来像t == 17,但绝对不能超过 16;我还没有验证。
  • 绝对是下标问题,当我得到像 1404 这样的索引时,我没有做n_states * n_samples = 39 * 17 = 663 的基本常识检查。
【解决方案2】:

似乎因为t 以1 开头,所以第一行从未初始化。我猜每一行都取决于前一行,所以垃圾值会传播。一个特定案例起作用的事实纯粹是偶然的(未初始化的数据发生是有效的)。

【讨论】:

  • 即使最佳路径表的第一行未初始化 1) 代码可以正常处理较短的数据 2) 最佳路径数组值仅取决于已初始化的维特比表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多