PHP usort() 不准确?
PHP usort() not accurate?
我有一个多维数组。在每个数组中,它们是一个名为 "elo-rating" 的子值。我想根据这个子值对我的数组进行排序。
所以我通过以下方式获取名为 $newRanks
和 运行 的数组:
uasort($newRanks, create_function(
'$b, $a', 'return $a["elo_rating"] - $b["elo_rating"];'
));
运行通过那段代码后,我的数组returns如下:
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
下方。
关于如何解决这个问题有什么想法吗?
您正在 return 从您的函数中获取十进制值,documentation 对此发出警告。
The comparison function must return an integer less than, equal to, or
greater than zero if the first argument is considered to be
respectively less than, equal to, or greater than the second. Note
that before PHP 7.0.0 this integer had to be in the range from
-2147483648 to 2147483647.
Caution Returning non-integer values from the comparison function,
such as float, will result in an internal cast to integer of the
callback's return value. So values such as 0.99 and 0.1 will both be
cast to an integer value of 0, which will compare such values as
equal.
将函数更改为 return 整数以解决问题。
uasort($newRanks, create_function(
'$b, $a', 'return ($a["elo_rating"] > $b["elo_rating"])?-1:1;'
));
两个问题:
- 您使用的
create_function()
在内部使用 eval()
,这是一个巨大的安全漏洞,不需要启动。只需使用实际的匿名函数即可。
- 你的函数参数是反的。
- Willem Renzema 就 return 值的隐式转换提出了一个有效的观点,但我不喜欢这个解决方案。
所以:
$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
都是等价的,也就是 float/rounding 错误。
参见:What is the most effective way for float and double comparison?
我有一个多维数组。在每个数组中,它们是一个名为 "elo-rating" 的子值。我想根据这个子值对我的数组进行排序。
所以我通过以下方式获取名为 $newRanks
和 运行 的数组:
uasort($newRanks, create_function(
'$b, $a', 'return $a["elo_rating"] - $b["elo_rating"];'
));
运行通过那段代码后,我的数组returns如下:
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
下方。
关于如何解决这个问题有什么想法吗?
您正在 return 从您的函数中获取十进制值,documentation 对此发出警告。
The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second. Note that before PHP 7.0.0 this integer had to be in the range from -2147483648 to 2147483647.
Caution Returning non-integer values from the comparison function, such as float, will result in an internal cast to integer of the callback's return value. So values such as 0.99 and 0.1 will both be cast to an integer value of 0, which will compare such values as equal.
将函数更改为 return 整数以解决问题。
uasort($newRanks, create_function(
'$b, $a', 'return ($a["elo_rating"] > $b["elo_rating"])?-1:1;'
));
两个问题:
- 您使用的
create_function()
在内部使用eval()
,这是一个巨大的安全漏洞,不需要启动。只需使用实际的匿名函数即可。 - 你的函数参数是反的。
- Willem Renzema 就 return 值的隐式转换提出了一个有效的观点,但我不喜欢这个解决方案。
所以:
$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
都是等价的,也就是 float/rounding 错误。
参见:What is the most effective way for float and double comparison?