代码之家  ›  专栏  ›  技术社区  ›  Jason Axelrod

PHP usort()不准确?

  •  0
  • Jason Axelrod  · 技术社区  · 8 年前

    我有一个多维数组。在每个数组中,它们是一个子值,称为“elo rating”。我想根据此子值对数组排序。

    所以我取我的数组,叫做 $newRanks 并运行以下操作:

    uasort($newRanks, create_function(
        '$b, $a', 'return $a["elo_rating"] - $b["elo_rating"];'
    ));
    

    运行完该代码后,我的数组返回如下:

    array:52 [
      "3-1154" => array:4 [
        "league_id" => 3
        "user_id" => 1154
        "elo_matches" => 8
        "elo_rating" => 1224.47797881
      ]
      "3-205" => array:4 [
        "league_id" => 3
        "user_id" => 205
        "elo_matches" => 11
        "elo_rating" => 1207.86593741
      ]
      "3-1" => array:4 [
        "league_id" => 3
        "user_id" => 1
        "elo_matches" => 17
        "elo_rating" => 1206.60264689
      ]
      "3-285" => array:4 [
        "league_id" => 3
        "user_id" => 285
        "elo_matches" => 4
        "elo_rating" => 1187.31524255
      ]
      "3-259" => array:4 [
        "league_id" => 3
        "user_id" => 259
        "elo_matches" => 4
        "elo_rating" => 1173.02391767
      ]
      "3-12689" => array:4 [
        "league_id" => 3
        "user_id" => 12689
        "elo_matches" => 4
        "elo_rating" => 1167.46830619
      ]
      "3-1603" => array:4 [
        "league_id" => 3
        "user_id" => 1603
        "elo_matches" => 13
        "elo_rating" => 1153.3060092
      ]
      "3-16" => array:4 [
        "league_id" => 3
        "user_id" => 16
        "elo_matches" => 7
        "elo_rating" => 1146.65083202
      ]
      "3-1609" => array:4 [
        "league_id" => 3
        "user_id" => 1609
        "elo_matches" => 3
        "elo_rating" => 1122.679103
      ]
      "3-333" => array:4 [
        "league_id" => 3
        "user_id" => 333
        "elo_matches" => 5
        "elo_rating" => 1112.56694511
      ]
      "3-10030" => array:4 [
        "league_id" => 3
        "user_id" => 10030
        "elo_matches" => 4
        "elo_rating" => 1091.27782914
      ]
      "3-378" => array:4 [
        "league_id" => 3
        "user_id" => 378
        "elo_matches" => 9
        "elo_rating" => 1082.0354022
      ]
      "3-6107" => array:4 [
        "league_id" => 3
        "user_id" => 6107
        "elo_matches" => 5
        "elo_rating" => 1059.74850166
      ]
      "3-5179" => array:4 [
        "league_id" => 3
        "user_id" => 5179
        "elo_matches" => 3
        "elo_rating" => 1046.60181418
      ]
      "3-1476" => array:4 [
        "league_id" => 3
        "user_id" => 1476
        "elo_matches" => 9
        "elo_rating" => 1038.88789903
      ]
      "3-70" => array:4 [
        "league_id" => 3
        "user_id" => 70
        "elo_matches" => 8
        "elo_rating" => 1038.63959146
      ]
      "3-303" => array:4 [
        "league_id" => 3
        "user_id" => 303
        "elo_matches" => 7
        "elo_rating" => 1039.26666217
      ]
      "3-59" => array:4 [
        "league_id" => 3
        "user_id" => 59
        "elo_matches" => 1
        "elo_rating" => 1033.78309445
      ]
      "3-1017" => array:4 [
        "league_id" => 3
        "user_id" => 1017
        "elo_matches" => 4
        "elo_rating" => 1002.79264647
      ]
      "3-632" => array:4 [
        "league_id" => 3
        "user_id" => 632
        "elo_matches" => 3
        "elo_rating" => 1002.2039368
      ]
      "3-177" => array:4 [
        "league_id" => 3
        "user_id" => 177
        "elo_matches" => 4
        "elo_rating" => 994.838857477
      ]
      "3-12466" => array:4 [
        "league_id" => 3
        "user_id" => 12466
        "elo_matches" => 4
        "elo_rating" => 994.761652125
      ]
      "3-9725" => array:4 [
        "league_id" => 3
        "user_id" => 9725
        "elo_matches" => 7
        "elo_rating" => 994.520367143
      ]
      "3-1593" => array:4 [
        "league_id" => 3
        "user_id" => 1593
        "elo_matches" => 4
        "elo_rating" => 987.448354356
      ]
      "3-78" => array:4 [
        "league_id" => 3
        "user_id" => 78
        "elo_matches" => 16
        "elo_rating" => 984.927509938
      ]
      "3-20837" => array:4 [
        "league_id" => 3
        "user_id" => 20837
        "elo_matches" => 4
        "elo_rating" => 981.533602402
      ]
      "3-25" => array:4 [
        "league_id" => 3
        "user_id" => 25
        "elo_matches" => 3
        "elo_rating" => 977.651701927
      ]
      "3-2056" => array:4 [
        "league_id" => 3
        "user_id" => 2056
        "elo_matches" => 8
        "elo_rating" => 978.374247502
      ]
      "3-14300" => array:4 [
        "league_id" => 3
        "user_id" => 14300
        "elo_matches" => 9
        "elo_rating" => 958.218292232
      ]
      "3-16900" => array:4 [
        "league_id" => 3
        "user_id" => 16900
        "elo_matches" => 3
        "elo_rating" => 957.66758785
      ]
      "3-5" => array:4 [
        "league_id" => 3
        "user_id" => 5
        "elo_matches" => 3
        "elo_rating" => 955.441682773
      ]
      "3-11793" => array:4 [
        "league_id" => 3
        "user_id" => 11793
        "elo_matches" => 3
        "elo_rating" => 956.118019821
      ]
      "3-23" => array:4 [
        "league_id" => 3
        "user_id" => 23
        "elo_matches" => 1
        "elo_rating" => 950.0
      ]
      "3-160" => array:4 [
        "league_id" => 3
        "user_id" => 160
        "elo_matches" => 6
        "elo_rating" => 946.346810828
      ]
      "3-11882" => array:4 [
        "league_id" => 3
        "user_id" => 11882
        "elo_matches" => 3
        "elo_rating" => 943.113557791
      ]
      "3-178" => array:4 [
        "league_id" => 3
        "user_id" => 178
        "elo_matches" => 3
        "elo_rating" => 940.38037017
      ]
      "3-2113" => array:4 [
        "league_id" => 3
        "user_id" => 2113
        "elo_matches" => 3
        "elo_rating" => 940.343382565
      ]
      "3-1334" => array:4 [
        "league_id" => 3
        "user_id" => 1334
        "elo_matches" => 2
        "elo_rating" => 923.336202927
      ]
      "3-184" => array:4 [
        "league_id" => 3
        "user_id" => 184
        "elo_matches" => 2
        "elo_rating" => 920.326252901
      ]
      "3-2162" => array:4 [
        "league_id" => 3
        "user_id" => 2162
        "elo_matches" => 2
        "elo_rating" => 917.932985501
      ]
      "3-2058" => array:4 [
        "league_id" => 3
        "user_id" => 2058
        "elo_matches" => 6
        "elo_rating" => 905.641833006
      ]
      "3-1951" => array:4 [
        "league_id" => 3
        "user_id" => 1951
        "elo_matches" => 2
        "elo_rating" => 906.136056131
      ]
      "3-1749" => array:4 [
        "league_id" => 3
        "user_id" => 1749
        "elo_matches" => 2
        "elo_rating" => 905.570092295
      ]
      "3-15296" => array:4 [
        "league_id" => 3
        "user_id" => 15296
        "elo_matches" => 2
        "elo_rating" => 901.02829192
      ]
      "3-11684" => array:4 [
        "league_id" => 3
        "user_id" => 11684
        "elo_matches" => 2
        "elo_rating" => 901.02829192
      ]
      "3-940" => array:4 [
        "league_id" => 3
        "user_id" => 940
        "elo_matches" => 2
        "elo_rating" => 899.735074733
      ]
      "3-12235" => array:4 [
        "league_id" => 3
        "user_id" => 12235
        "elo_matches" => 2
        "elo_rating" => 900.0
      ]
      "3-2957" => array:4 [
        "league_id" => 3
        "user_id" => 2957
        "elo_matches" => 2
        "elo_rating" => 900.0
      ]
      "3-14959" => array:4 [
        "league_id" => 3
        "user_id" => 14959
        "elo_matches" => 2
        "elo_rating" => 894.798068073
      ]
      "3-779" => array:4 [
        "league_id" => 3
        "user_id" => 779
        "elo_matches" => 5
        "elo_rating" => 874.675970857
      ]
      "3-110" => array:4 [
        "league_id" => 3
        "user_id" => 110
        "elo_matches" => 4
        "elo_rating" => 849.309123925
      ]
      "3-5837" => array:4 [
        "league_id" => 3
        "user_id" => 5837
        "elo_matches" => 4
        "elo_rating" => 821.601462523
      ]
    ]
    

    如果仔细观察这个结果数组,您会注意到它的排序不完全正确。例如 1039.26666217 低于两者 1038.63959146 和 1038.88789903 .

    有没有办法解决这个问题?

    2 回复  |  直到 8 年前
        1
  •  3
  •   Willem Renzema    8 年前

    您正在从函数返回十进制值 documentation 警告不要。

    比较函数必须返回小于、等于或的整数 如果第一个参数被认为是 分别小于、等于或大于第二个。笔记 在PHP 7.0.0之前,此整数必须在以下范围内 -2147483648至2147483647。

    警告:从比较函数返回非整数值, 例如float,将导致内部强制转换为 回调的返回值。因此,0.99和0.1等值都将 强制转换为整数值0,这将比较以下值: 相同的

    更改函数以返回一个整数来修复此问题。

    uasort($newRanks, create_function(
        '$b, $a', 'return ($a["elo_rating"] > $b["elo_rating"])?-1:1;'
    ));
    
        2
  •  1
  •   Sammitch    8 年前

    两个问题:

    1. 您正在使用 create_function() 其中使用 eval() 在内部,这是一个巨大的安全漏洞,不需要启动。只需使用实际的匿名函数。
    2. 您的函数参数是向后的。
    3. WillemRenzema就返回值的隐式转换提出了一个有效的观点,但我不喜欢这个解决方案。

    因此:

    $epsilon = 0.000000001;
    uasort(
      $newRanks,
      function($a,$b)use($epsilon}{
        $diff = $a["elo_rating"] - $b["elo_rating"];
        if( abs($diff) < $epsilon ) { return 0; }
        else if( $diff > 0 ) { return 1; }
        else { return -1; }
      )
    );
    

    哪里 $epsilon 是否专门为该比较选择了一个值 $diff 小于该值即为等效值,即浮点/舍入误差。

    请参见: What is the most effective way for float and double comparison?

    推荐文章