简体   繁体   English

qsort函数在C意外行为?

[英]qsort function in C unexpected behaviour?

I am experiencing weird and unexpected behaviour of the qsort() function. 我遇到了qsort()函数的奇怪和意外行为。 I have my list of nodes, and every of them contains two values based on which I want to sort my list (technically, an array). 我有我的节点列表,每个节点包含两个值,我想根据这些值对列表进行排序(技术上,数组)。

For example: 例如:

If I have an original array that looks like this (1st element is p , 2nd element is t ): 如果我有一个看起来像这样的原始数组(第一个元素是p ,第二个元素是t ):

[0,10|1], [0.05|0], [0,10|0], [0,05|2], [0,10|2], [0,15|1], [0,05|1] [0,10 | 1],[0.05 | 0],[0,10 | 0],[0,05 | 2],[0,10 | 2],[0,15 | 1],[0,05 | 1]

After it is sorted, it should look like this: 排序后,它应如下所示:

[0,05|0], [0,05|1], [0,05|2], [0,10|0], [0,10|1], [0,10|2], [0,15|1]. [0,05 | 0],[0,05 | 1],[0,05 | 2],[0,10 | 0],[0,10 | 1],[0,10 | 2],[0 ,15 | 1]。

Struct node: 结构节点:

typedef struct node{
    int t;
    double p;
}node;

Compare function , called like qsort(nodes, num_of_node, sizeof(node), compare_pairs); 比较函数 ,称为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;

Problem 问题

The unwanted behaviour starts at 3. STEP which looks like this: 不需要的行为从3.步骤开始,如下所示:

List: [0.10 | 清单:[0.10 | 2] [0.10 | 2] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 1] [0.25 | 1] [0.25 | 999] 999]

And should look like this: 应该是这样的:

List: [0.10 | 清单:[0.10 | 2] [0.10 | 2] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 1] [0.15 | 1] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.25 | 999] [0.25 | 999] 999]

Initial list: [0.25 | 初始清单:[0.25 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.05 | 999] [0.05 | 999] [0.05| 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] 999]

  1. STEP: 步:

Erasing (min) node 0.050000... 擦除(分钟)节点0.050000 ...

Erasing (min) node 0.050000... 擦除(分钟)节点0.050000 ...

Creating (new) node 0.100000... 创建(新)节点0.100000 ...

List: [0.05 | 清单:[0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.05 | 999] [0.10 | 999] [0.10 | 1] [0.10 | 1] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.25 | 999] [0.25 | 999] 999]

  1. STEP: 步:

Erasing (min) node 0.050000... 擦除(分钟)节点0.050000 ...

Erasing (min) node 0.050000... 擦除(分钟)节点0.050000 ...

Creating (new) node 0.100000... 创建(新)节点0.100000 ...

List: [0.05 | 清单:[0.05 | 999] [0.10 | 999] [0.10 | 1] [0.10 | 1] [0.10 | 2] [0.10 | 2] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.25 | 999] [0.25 | 999] 999]

  1. STEP: 步:

Erasing (min) node 0.050000... 擦除(分钟)节点0.050000 ...

Erasing (min) node 0.100000... 擦除(最小)节点0.100000 ...

Creating (new) node 0.150000... 创建(新)节点0.150000 ...

List: [0.10 | 清单:[0.10 | 2] [0.10 | 2] [0.10 | 999] [0.10 | 999] [0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 1] [0.25 | 1] [0.25 | 999] 999]

  1. STEP: 步:

Erasing (min) node 0.100000... 擦除(最小)节点0.100000 ...

Erasing (min) node 0.100000... 擦除(最小)节点0.100000 ...

Erasing (new) node 0.200000... 擦除(新)节点0.200000 ...

List: [0.10 | 清单:[0.10 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 999] [0.15 | 1] [0.20 | 1] [0.20 | 1] [0.25 | 1] [0.25 | 999] 999]

  1. STEP: 步:

Erasing (min) node 0.100000... 擦除(最小)节点0.100000 ...

Erasing (min) node 0.150000... 擦除(分钟)节点0.150000 ...

Creating (new) node 0.250000... 创建(新)节点0.250000 ...

List: [0.15 | 清单:[0.15 | 999] [0.15 | 999] [0.15 | 1] [0.20 | 1] [0.20 | 1] [0.25 | 1] [0.25 | 1] [0.25 | 1] [0.25 | 999] 999]

  1. STEP: 步:

Erasing (min) node 0.150000... 擦除(分钟)节点0.150000 ...

Erasing (min) node 0.150000... 擦除(分钟)节点0.150000 ...

Erasing (new) node 0.300000... 擦除(新)节点0.300000 ...

List: [0.20 | 清单:[0.20 | 1] [0.25 | 1] [0.25 | 1] [0.25 | 1] [0.25 | 999] [0.30 | 999] [0.30 | 1] 1]

  1. STEP: 步:

Erasing (min) node 0.200000... 擦除(分钟)节点0.200000 ...

Erasing (min) node 0.250000... 擦除(最小)节点0.250000 ...

Erasing (new) node 0.450000... 擦除(新)节点0.450000 ...

List: [0.25 | 清单:[0.25 | 999] [0.30 | 999] [0.30 | 1] [0.45 | 1] [0.45 | 1] 1]

  1. STEP: 步:

Erasing (min) node 0.250000... 擦除(最小)节点0.250000 ...

Erasing (min) node 0.300000... 擦除(分钟)节点0.300000 ...

Creating (new) node 0.550000... 创建(新)节点0.550000 ...

List: [0.45 | 清单:[0.45 | 1] [0.55 | 1] [0.55 | 1] 1]

  1. STEP: 步:

Erasing (min) node 0.450000... 擦除(分钟)节点0.450000 ...

Erasing (min) node 0.550000... 擦除(分钟)节点0.550000 ...

Creating (new) node 1.000000... 创建(新)节点1.000000 ...

List: [1.00 | 清单:[1.00 | 1] 1]

General idea * 总体思路 *

In every step two minimal nodes get deleted from list, and one new node gets inserted into the list. 在每个步骤中,从列表中删除两个最小节点,并将一个新节点插入到列表中。 The node that gets inserted has the value of t for 1 bigger then the greatest in the list, except that it does not compare itself to the t value of 999. If the greatest in the list has t = 999, then the one inserted will have 1. 插入的节点的值为t ,大于列表中的最大值,但它不会将自身与t值999进行比较。如果列表中的最大值为t = 999,则插入的节点将为有1。

Find greatest t : 找到最大的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;

Main code: 主要代码:

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");

Add / Remove node... 添加/删除节点...

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));
 }

The problem you are having is with floating point imprecision. 你遇到的问题是浮点不精确。 None of the exact decimal numbers 0.1, 0.05 and 0.15 have exact representations in binary floating point. 精确的十进制数0.1,0.05和0.15都没有二进制浮点的精确表示。

Using the IEEE 64-bit double representation, the closest representable value to 0.15 is slightly less than 0.15, and the closest representable values to 0.05 and 0.10 are slightly greater than 0.05 and 0.10 respectively. 使用IEEE 64位double表示,最接近的可表示值0.15略小于0.15,并且最接近的可表示值0.05和0.10分别略大于0.05和0.10。 With an implementation using round-to-nearest, this means that if you add up 0.05 and 0.10 you will end up with a number slightly greater than 0.15, and if you set a double to 0.15 directly you will end up with a number slightly less than 0.15. 使用舍入到最接近的实现,这意味着如果你加起来0.05和0.10,你最终会得到一个略大于0.15的数字,如果你直接设置一个double到0.15,你最终会得到一个稍微少一点的数字比0.15。 These will not compare equal. 这些不会相等。

This is what is apparently happening in your step 3. You remove two nodes with the values 0.05 and 0.10 (actually, as discussed their real values are slightly greater than those numbers) and add them together, ending up with a number slightly greater than 0.15. 这是您在步骤3中显然发生的事情。您删除了值为0.05和0.10的两个节点(实际上,正如所讨论的那样,它们的实际值略大于这些数字)并将它们加在一起,最后得到的数字略大于0.15 。 This compares greater than the existing nodes whose real values are slightly less than 0.15, so it sorts after them. 这比实际值略小于 0.15的现有节点要大,因此在它们之后进行排序。

It's not clear if this really matters for your algorithm, though? 不过,目前尚不清楚这对你的算法是否真的重要吗? Doesn't it end up at the same end state anyway? 最终它不会以同样的最终状态结束吗? If it does matter, since you are apparently storing probabilities which range from 0.0 to 1.0 inclusive, you could use a decimal fixed point representation instead (eg store the probability multiplied by 10000 in a long int rather than a double , then just divide by 10000 for display). 如果它确实很重要,因为你显然存储的概率范围从0.0到1.0(包括0.0和1.0),你可以使用小数点定点表示(例如,将概率乘以10000乘以long int而不是double ,然后除以10000用于显示)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM