[英]qsort function in C unexpected behaviour?
我遇到了qsort()函数的奇怪和意外行为。 我有我的节点列表,每个节点包含两个值,我想根据这些值对列表进行排序(技术上,数组)。
例如:
如果我有一个看起来像这样的原始数组(第一个元素是p ,第二个元素是t ):
[0,10 | 1],[0.05 | 0],[0,10 | 0],[0,05 | 2],[0,10 | 2],[0,15 | 1],[0,05 | 1]
排序后,它应如下所示:
[0,05 | 0],[0,05 | 1],[0,05 | 2],[0,10 | 0],[0,10 | 1],[0,10 | 2],[0 ,15 | 1]。
结构节点:
typedef struct node{
int t;
double p;
}node;
比较函数 ,称为qsort(nodes, num_of_node, sizeof(node), compare_pairs);
static int compare_pairs(const void *n1, const void *n2){
const node *na1= n1;
const node *na2= n2;
if(na1->p < na2->p) return -1;
if(na1->p > na2->p) return 1;
// At this point, value p is equal, so I am sorting based on t
if(na1->t < na2->t) return -1;
if(na1->t > na2->t) return 1;
return 0;
问题
不需要的行为从3.步骤开始,如下所示:
清单:[0.10 | 2] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 1] [0.25 | 999]
应该是这样的:
清单:[0.10 | 2] [0.10 | 999] [0.10 | 999] [0.15 | 1] [0.15 | 999] [0.15 | 999] [0.25 | 999]
初始清单:[0.25 | 999] [0.15 | 999] [0.15 | 999] [0.10 | 999] [0.10 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999]
擦除(分钟)节点0.050000 ...
擦除(分钟)节点0.050000 ...
创建(新)节点0.100000 ...
清单:[0.05 | 999] [0.05 | 999] [0.05 | 999] [0.10 | 1] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.25 | 999]
擦除(分钟)节点0.050000 ...
擦除(分钟)节点0.050000 ...
创建(新)节点0.100000 ...
清单:[0.05 | 999] [0.10 | 1] [0.10 | 2] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.25 | 999]
擦除(分钟)节点0.050000 ...
擦除(最小)节点0.100000 ...
创建(新)节点0.150000 ...
清单:[0.10 | 2] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 1] [0.25 | 999]
擦除(最小)节点0.100000 ...
擦除(最小)节点0.100000 ...
擦除(新)节点0.200000 ...
清单:[0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 1] [0.20 | 1] [0.25 | 999]
擦除(最小)节点0.100000 ...
擦除(分钟)节点0.150000 ...
创建(新)节点0.250000 ...
清单:[0.15 | 999] [0.15 | 1] [0.20 | 1] [0.25 | 1] [0.25 | 999]
擦除(分钟)节点0.150000 ...
擦除(分钟)节点0.150000 ...
擦除(新)节点0.300000 ...
清单:[0.20 | 1] [0.25 | 1] [0.25 | 999] [0.30 | 1]
擦除(分钟)节点0.200000 ...
擦除(最小)节点0.250000 ...
擦除(新)节点0.450000 ...
清单:[0.25 | 999] [0.30 | 1] [0.45 | 1]
擦除(最小)节点0.250000 ...
擦除(分钟)节点0.300000 ...
创建(新)节点0.550000 ...
清单:[0.45 | 1] [0.55 | 1]
擦除(分钟)节点0.450000 ...
擦除(分钟)节点0.550000 ...
创建(新)节点1.000000 ...
清单:[1.00 | 1]
总体思路 *
在每个步骤中,从列表中删除两个最小节点,并将一个新节点插入到列表中。 插入的节点的值为t ,大于列表中的最大值,但它不会将自身与t值999进行比较。如果列表中的最大值为t = 999,则插入的节点将为有1。
找到最大的t :
int max_t(node *nodes, int num, double p){
int max_t= 0;
int i;
for(i=0; i<num; i+=1){
if(nodes[i].p== p && nodes[i].t != 999){
if(nodes[i].t > max_t){
max_t = nodes[i].t;
}
}
}
return max_t;
主要代码:
node *nodes = malloc(num_of_nodes*sizeof(node));
int i;
for(i=0; i<num_of_nodes; i+=1){
node n;
n.t = 999;
n.p = *(probabs+ i);
*(nodes+i) = n;
}
qsort(nodes, num_of_nodes, sizeof(node), compare_pairs);
while(num_of_nodes> 1){
printf("\n%d. STEP:\n", z);
z += 1;
// 2) Find two min nodes
node *min_n1 = malloc(sizeof(node));
node *min_n2 = malloc(sizeof(node));
*min_n1 = nodes[0];
printf("Erasing (min) node %lf...\n", min_n1->p);
nodes= erase_node(nodes, min_n1, num_of_nodes);
num_of_nodes -= 1;
*min_n2 = nodes[0];
printf("Erasing (min) node %lf...\n", min_n2->p);
nodes= erase_node(nodes, min_n2, num_of_nodes);
num_of_nodes-= 1;
// 3) Create new node, add it to the list
node *new_node = malloc(sizeof(node));
new_node->p= min_n1->p + min_n2->p;
double p = new_node->p;
int max_t = max_t(nodes, num_of_nodes, p);
new_node->t = max_t + 1;
printf("Creating (new) node %lf...\n", new_node->p);
nodes = add_node(nodes, new_node, num_of_nodes);
num_of_nodes += 1;
qsort(nodes, num_of_nodes, sizeof(node), compare_pairs);
printf("List: ");
int k;
for(k=0; k<num_of_nodes; k+=1){
printf("[%.2lf | %d] ", nodes[k].p, nodes[k].t);
}
printf("\n");
添加/删除节点...
node *add_node(node *nodes, node *n, int num){
nodes = realloc(nodes, (num+1)*sizeof(node));
nodes[num] = *n;
return nodes;
node *erase_node(node *nodes, node *n, int num){
int i;
int index = 0;
for(i=0; i<num; i+=1){
if(nodes_equal(&nodes[i], n)){
index = i;
break;
}
}
for(i=index; i<num-1; i+=1){
nodes[i] = nodes[i+1];
}
nodes= realloc(nodes, (num-1)*sizeof(node));
return nodes;
}
int nodes_equal(node *n1, node *n2){
return !memcmp(n1, n2, sizeof(node));
}
你遇到的问题是浮点不精确。 精确的十进制数0.1,0.05和0.15都没有二进制浮点的精确表示。
使用IEEE 64位double
表示,最接近的可表示值0.15略小于0.15,并且最接近的可表示值0.05和0.10分别略大于0.05和0.10。 使用舍入到最接近的实现,这意味着如果你加起来0.05和0.10,你最终会得到一个略大于0.15的数字,如果你直接设置一个double
到0.15,你最终会得到一个稍微少一点的数字比0.15。 这些不会相等。
这是您在步骤3中显然发生的事情。您删除了值为0.05和0.10的两个节点(实际上,正如所讨论的那样,它们的实际值略大于这些数字)并将它们加在一起,最后得到的数字略大于0.15 。 这比实际值略小于 0.15的现有节点要大,因此在它们之后进行排序。
不过,目前尚不清楚这对你的算法是否真的重要吗? 最终它不会以同样的最终状态结束吗? 如果它确实很重要,因为你显然存储的概率范围从0.0到1.0(包括0.0和1.0),你可以使用小数点定点表示(例如,将概率乘以10000乘以long int
而不是double
,然后除以10000用于显示)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.