rust - ¿Cómo hacer una búsqueda binaria en un Vec de carrozas?
(4)
Si tiene un
Vec<u32>
usaría el método
slice::binary_search
.
Por razones que no entiendo,
f32
y
f32
no implementan
Ord
.
Dado que los tipos primitivos son de la biblioteca estándar, no puede implementar
Ord
en ellos, por lo que no parece que pueda usar este método.
¿Cómo puedes hacer esto de manera efectiva?
¿Realmente tengo que envolver
f64
en una estructura de envoltura e implementar
Ord
en él?
Parece extremadamente doloroso tener que hacer esto, e implica una gran cantidad de
transmute
para enviar bloques de datos de un lado a otro de manera insegura sin ningún motivo efectivo.
por razones que no entiendo, f32 y f64 no implementan Ord.
¡Porque el
punto flotante es difícil
!
La versión corta es que los números de coma flotante tienen un valor especial NaN - No es un número.
La especificación IEEE para números de coma flotante establece que
1 < NaN
,
1 > NaN
y
NaN == NaN
son todos
false
.
Ord
dice:
Rasgo para los tipos que forman un orden total .
Esto significa que las comparaciones deben tener totalidad :
a ≤ b o b ≤ a
pero acabamos de ver que los puntos flotantes no tienen esta propiedad.
Entonces, sí, tendrá que crear un tipo de contenedor que de alguna manera trate de comparar la
gran cantidad de valores de NaN
.
Tal vez su caso puede simplemente afirmar que el valor flotante nunca es NaN y luego llamar al rasgo
PartialOrd
normal.
Aquí hay un ejemplo:
use std::cmp::Ordering;
#[derive(PartialEq,PartialOrd)]
struct NonNan(f64);
impl NonNan {
fn new(val: f64) -> Option<NonNan> {
if val.is_nan() {
None
} else {
Some(NonNan(val))
}
}
}
impl Eq for NonNan {}
impl Ord for NonNan {
fn cmp(&self, other: &NonNan) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
fn main() {
let mut v: Vec<_> = [2.0, 1.0, 3.0].iter().map(|v| NonNan::new(*v).unwrap()).collect();
v.sort();
let r = v.binary_search(&NonNan::new(2.0).unwrap());
println!("{:?}", r);
}
Si está seguro de que sus valores de coma flotante nunca serán NaN, puede expresar esta semántica con los envoltorios en
decorum
.
Específicamente, el tipo
Ordered
implementa
Ord
y entra en pánico cada vez que el programa intenta hacer algo no válido:
use decorum::Ordered;
fn foo() {
let ordered = Ordered<f32>::from_inner(10.);
let normal = ordered.into()
}
Uno de los métodos de
binary_search_by
es
binary_search_by
, que podría usar.
f32
/
f32
implementa
PartialOrd
, por lo que si sabe que
nunca
pueden ser
NaN
, puede desenvolver el resultado de
partial_cmp
:
fn main() {
let values = [1.0, 2.0, 3.0, 4.0, 5.0];
let location = values.binary_search_by(|v| {
v.partial_cmp(&3.14).expect("Couldn''t compare values")
});
match location {
Ok(i) => println!("Found at {}", i),
Err(i) => println!("Not found, could be inserted at {}", i),
}
}
https://github.com/emerentius/ord_subset
implementa un método
ord_subset_binary_search()
que puede usar para esto.
de su README:
let mut s = [5.0, std::f64::NAN, 3.0, 2.0];
s.ord_subset_sort();
assert_eq!(&s[0..3], &[2.0, 3.0, 5.0]);
assert_eq!(s.ord_subset_binary_search(&5.0), Ok(2));
assert_eq!(s.iter().ord_subset_max(), Some(&5.0));
assert_eq!(s.iter().ord_subset_min(), Some(&2.0));