排序插入到具有重复项的固定大小数组中
sorted insert into a fixed sized array with duplication
我试图找到最有效的 C 程序来存储传入数据流中的 N 个最大值。例如。假设每个传入数据为 32 字节,并且是来自传感器的连续流,我需要存储流中的 N 个最大值(允许重复)。
简单的方法是迭代并找到位置,然后将下面的所有元素移动一个(可能丢弃当前最小值)。有更好的方法吗?
//MAX_KEEP 32
typedef struct accel_sys
{
FILE *infile;
/* Data for largest and last */
u32 largest[MAX_KEEP]; /* largest in highest index, smallest in lowest index */
u32 last[MAX_KEEP]; /* circular buffer */
u8 last_start; /* points to the oldest value */
/* Data for reading and processing the file */
u8 last_byte;
Bool even;
int num_read;
} accel_t;
typedef accel_t * accel_h;
static void store_max(accel_h accel, u32 cur_value)
{
int i = MAX_KEEP-1;
int j = 0;
while(i >= 0)
{
if( cur_value > accel->largest[i] )
{
/* found it */
break;
}
i--;
}
/* i < 0 if the value doesn't belong in the array, do nothing in that case */
if( i >= 0 )
{
/* Move everything lower than cur_value down, losing the last value,
* then store our new value in our found spot */
j = 0;
while( j < i )
{
accel->largest[j] = accel->largest[j+1];
j++;
}
accel->largest[i] = cur_value;
}
}
第一个优化是用 memmove
替换用于移动数组的显式循环。当然无论哪种方式都是线性时间,但在大多数平台上,memmove
是线性的,常数乘数更快。
接下来,N有多大?因为您显然已经按排序顺序保持值,所以,为什么不进行平分搜索而不是线性搜索?这意味着您的摊销平均时间变为 O(log N) 而不是 O(N)。*
所以(未经测试;我保证某处至少有一个差一错误……):
static void store_max(accel_h accel, uint16_t cur_value) {
size_t first = 0, last = N, middle;
while (first < last) {
middle = (first + last)/2;
if (accel->largest[middle] < cur_value)
first = middle + 1;
else if (accel->largest[middle] == cur_value)
break;
else
last = middle - 1;
}
if (middle > 0) {
memmove(accel->largest, accel->largest+1, middle);
accel->largest[middle] = cur_value;
}
}
如果你想改善最坏情况时间,你需要一个堆,因为你可以在对数时间内推入弹出。**你可以将堆存储在N 值的普通旧数组就像您的排序数组一样,并在线性时间内按排序顺序读出这些值。但这增加了一些复杂性,我不想尝试在我的 phone 上编写代码。 :)
* 你最坏的情况还是O(N);想象一个病理情况,其中值不断增加。但即使在那种情况下,非常快的 O(N) + 慢速 O(log N) 也可能比非常快的 O(N) + 慢速 O(N) 值得改进。
** 尽管在实践中,对于您可能关心的 N
的值,O(log N) 交换可能比 memmove
慢……
我试图找到最有效的 C 程序来存储传入数据流中的 N 个最大值。例如。假设每个传入数据为 32 字节,并且是来自传感器的连续流,我需要存储流中的 N 个最大值(允许重复)。 简单的方法是迭代并找到位置,然后将下面的所有元素移动一个(可能丢弃当前最小值)。有更好的方法吗?
//MAX_KEEP 32
typedef struct accel_sys
{
FILE *infile;
/* Data for largest and last */
u32 largest[MAX_KEEP]; /* largest in highest index, smallest in lowest index */
u32 last[MAX_KEEP]; /* circular buffer */
u8 last_start; /* points to the oldest value */
/* Data for reading and processing the file */
u8 last_byte;
Bool even;
int num_read;
} accel_t;
typedef accel_t * accel_h;
static void store_max(accel_h accel, u32 cur_value)
{
int i = MAX_KEEP-1;
int j = 0;
while(i >= 0)
{
if( cur_value > accel->largest[i] )
{
/* found it */
break;
}
i--;
}
/* i < 0 if the value doesn't belong in the array, do nothing in that case */
if( i >= 0 )
{
/* Move everything lower than cur_value down, losing the last value,
* then store our new value in our found spot */
j = 0;
while( j < i )
{
accel->largest[j] = accel->largest[j+1];
j++;
}
accel->largest[i] = cur_value;
}
}
第一个优化是用 memmove
替换用于移动数组的显式循环。当然无论哪种方式都是线性时间,但在大多数平台上,memmove
是线性的,常数乘数更快。
接下来,N有多大?因为您显然已经按排序顺序保持值,所以,为什么不进行平分搜索而不是线性搜索?这意味着您的摊销平均时间变为 O(log N) 而不是 O(N)。*
所以(未经测试;我保证某处至少有一个差一错误……):
static void store_max(accel_h accel, uint16_t cur_value) {
size_t first = 0, last = N, middle;
while (first < last) {
middle = (first + last)/2;
if (accel->largest[middle] < cur_value)
first = middle + 1;
else if (accel->largest[middle] == cur_value)
break;
else
last = middle - 1;
}
if (middle > 0) {
memmove(accel->largest, accel->largest+1, middle);
accel->largest[middle] = cur_value;
}
}
如果你想改善最坏情况时间,你需要一个堆,因为你可以在对数时间内推入弹出。**你可以将堆存储在N 值的普通旧数组就像您的排序数组一样,并在线性时间内按排序顺序读出这些值。但这增加了一些复杂性,我不想尝试在我的 phone 上编写代码。 :)
* 你最坏的情况还是O(N);想象一个病理情况,其中值不断增加。但即使在那种情况下,非常快的 O(N) + 慢速 O(log N) 也可能比非常快的 O(N) + 慢速 O(N) 值得改进。
** 尽管在实践中,对于您可能关心的 N
的值,O(log N) 交换可能比 memmove
慢……