尝试取数组开始和结束的特定部分的平均值

时间:2019-02-06 15:14:26

标签: c arrays compression

以下问题正在完全用C语言编写的PLC上运行。此功能是我尝试添加的库的一部分。

我有一个任意长度的数据浮点数组。当数组长度大于等于25个条目时,将触发以下代码。该函数应该确定条目的前x个和最后x个百分比(即,数组99中的33%将是前33个条目)。然后将第一部分和最后一部分取平均值,并压缩为两半(对于奇数输入数组,则压缩为一半+/- 1)。最终输出是这两个数组的组合。

为此,我正在尝试使用以下形式的功能:

plcbit arrayCompressFloatExtremities(float* arrayToCopyResultTo,
        float* arrayToDecrease, unsigned long numberOfDesiredElements,
        unsigned long numberOfElementsInOriginal, float inclusionZonePercent)

其中

  • float* arrayToCopyResultTo是函数完成后的目标数组
  • float* arrayToDecrease是输入数组指针
  • unsigned long numberofDesiredElements是输出数组的长度,应为25
  • unsigned long numberofElementsInOriginal是原始数组的长度,> = 25
  • float inclusionZonePercent是我要压缩的数组的前端和后端的百分比。例如。输入值.25会将条目的前25%和后25%压缩为长度为25个条目的数组

到目前为止,该代码似乎可以正常工作。 usedInterval的值似乎在调试器中被零除,但我不确定为什么。无论如何,我不确定我对此设置是否正确。

/* compresses and compies the array while neglecting a certain percentage of the board */
plcbit arrayCompressFloatExtremities(float* arrayToCopyResultTo,
        float* arrayToDecrease, unsigned long numberOfDesiredElements,
        unsigned long numberOfElementsInOriginal, float inclusionZonePercent) {
    int usedInterval = 0, i = 0, j = 0, k = 0, numberOfElementsLeft = 0;
    double temp = 0;
    float zone = 0;

    if ((numberOfElementsInOriginal == 0) || (numberOfDesiredElements == 0)) return 0;

    // determine zone size
    numberOfElementsInOriginal = sizeof(arrayToDecrease);
    numberOfElementsLeft = numberOfElementsInOriginal * inclusionZonePercent;

    // compress zone A into first half of output array using modulo operator
    // for odd number arrays

    for (i = 0;
            i < ((numberOfDesiredElements - (numberOfDesiredElements % 2)) / 2);
            i++) // i starts at 0 for the beginning part of the board and continues until the halfway point or halfway - 1
    {
        usedInterval = numberOfElementsLeft /
                (((numberOfDesiredElements - (numberOfDesiredElements % 2)) / 2) - i); 
        temp = 0;
        for (j = 0;
                j < (usedInterval + numberOfElementsInOriginal - numberOfElementsLeft);
                j++) {
            temp += arrayToDecrease[j];
        }

        arrayToCopyResultTo[i] = temp / (float) usedInterval;

        numberOfElementsLeft -= usedInterval;
    }
    // compress zone B
    numberOfElementsLeft = numberOfElementsInOriginal * inclusionZonePercent;   
    for (i = (numberOfElementsInOriginal - numberOfElementsLeft);
            i < (numberOfDesiredElements + (numberOfDesiredElements % 2));
            i++) // i starts at the end of the board minus the appropriate percentile and fills array with half of desired point or halfwat + 1
    {
        usedInterval = numberOfElementsLeft /
                (((numberOfDesiredElements + (numberOfDesiredElements % 2)) / 2) - i);
        temp = 0;
        for (j = (numberOfElementsInOriginal - numberOfElementsLeft);
                j < (usedInterval + numberOfElementsInOriginal - numberOfElementsLeft);
                j++) {
            temp += arrayToDecrease[j];
        }

        arrayToCopyResultTo[i] = temp / (float)usedInterval;

        numberOfElementsLeft -= usedInterval;
    }
    return 1;
}

我希望该算法将能够获取数组的开头和结尾的百分位数,压缩值(通过对条目进行平均),并在数组中输出值,而忽略数组的中间值。

1 个答案:

答案 0 :(得分:0)

正如我在评论中所写,您的确存在错误

numberOfElementsInOriginal = sizeof(arrayToDecrease);

因为sizeof(arrayToDecrease)为您提供了指针的大小(以字节为单位),与指针所指向的数组中的元素数量没有任何关系。由于numberOfElementsInOriginal是一个函数参数,我倾向于猜测这是该函数先前版本的多余部分,因此可以将其删除。

就样式和方法而言,似乎对目标数组的前半部分和后半部分使用的方法完全相同。如果确实是您想要的,那么

  • 通过将其分解为一个单独的函数,可以缩短和简化您的代码

您打过两次电话。此外,尽管我很少在SO答案中这么说,

  • 您的变量名太长。

那些长名称非常清楚,但是拥有如此长的可变名称如此之多,会使您的代码更难读取和理解,而不是那么容易,尤其是当其中几个彼此相似时。您可以通过以短短语开头,省略介词,使用缩写词(尤其是传统的缩写词),甚至只是以不同的方式表达想法,来创建仍具有足够描述性的较短名称。例如,

plcbit arrayCompressFloatExtremities(float *dest,
        float *src, unsigned long dest_len,
        unsigned long src_len, float inclusion_fraction)

我也切换到了snake_style而不是camelStyle。前者在C代码中更常见,在这种情况下,我更喜欢它,但后者也可以。

对于实际平均,如果您在遵循代码方面有足够的困难来确信其正确性,那么正确性并不是其最大的问题。我不了解您,但是即使知道(我认为)您正在尝试做什么,对我来说也很难分析您的代码以进行验证。除了我已经提到的问题之外,此问题部分是由于使用

  • 复杂的内联表达式

  • 此类表达式是重复的,并且
  • 有些不必要复杂。

尤其要使用多次重复的表达式:

numberOfDesiredElements - (numberOfDesiredElements % 2)) / 2

假设numberOnumberOfDesiredElements具有无符号整数类型,那么该表达式将始终求值与相同的结果

numberOfDesiredElements / 2

,它更清晰并且小于长度的一半。但是,即使进行替换也无法清除代码,不足以使我对它是否正确充满信心。

以一种函数的形式考虑此替代方案,您可以为每个函数调用一次:

/*
 * Partitions the source array evenly into dest_len bins, and records the average
 * of the elements in each bin in the corresponding element of the destination
 * array.  When dest_len does not evenly divide src_len, each of the src_len % dest_len
 * initial bins is one element longer than the trailing bins.
 *
 * dest:     a pointer to the array wherein the results are to be stored
 * dest_len: the number of leading elements of dest to fill; must not exceed src_len
 * src:      a pointer to the array containing the source elements
 * src_len:  the number of initial elements of src to process
 */
void compress_array(float *dest, size_t dest_len, float *src, size_t src_len) {
    // This implementation depends on these assumptions to be true:
    assert(0 < dest_len && dest_len <= src_len && dest_len <= ULONG_MAX / 2 + 1);

    size_t base_bin_size = src_len / dest_len;
    size_t num_extras = src_len % dest_len;

    // Iterate over the bins
    for (size_t dest_inx = 0; dest_inx < dest_len; dest_inx++) {
        // This is a concise (but perhaps too clever) way of distributing
        // the left-over source elements to bins, one element to each bin
        // until they are used up.  It depends on the fact that relational
        // expressions evaluate to either 0 or 1, and on the fact that
        // unsigned arithmetic wraps around:
        unsigned long bin_size = base_bin_size + (--num_extras < dest_len);

        // Average the elements in this bin
        float temp = 0;
        for (size_t bin_inx = 0; bin_inx < bin_size; bin_inx++) {
            // keeps track of our position in the source array by incrementing src
            temp += *(src++);
        }

        dest[dest_inx] = temp / bin_size;
    }
}

对我来说,这很简单明了,并且显然可以完成所记录的工作。 (并且请注意:它的作用是正确且清楚地记录在案。)我不确定文件所记录的工作正是您需要该函数执行的工作,但重点实际上是演示一些更清晰地编写方法,更易于维护的代码。