【问题标题】:What is the idiomatic way to get the index of a maximum or minimum floating point value in a slice or Vec in Rust?在 Rust 中获取 slice 或 Vec 中最大或最小浮点值的索引的惯用方法是什么?
【发布时间】:2019-05-23 00:28:50
【问题描述】:

假设 -- Vec<f32> 确实具有任何 NaN 值或表现出任何 NaN 行为。

采取以下样本集:

0.28  
0.3102
0.9856
0.3679
0.3697
0.46  
0.4311
0.9781
0.9891
0.5052
0.9173
0.932 
0.8365
0.5822
0.9981
0.9977

获取上述列表中最高值的索引(值可以是负数)的最简洁和最稳定的方法是什么?

我最初的尝试是这样的:

let _tmp = *nets.iter().max_by(|i, j| i.partial_cmp(j).unwrap()).unwrap();    
let _i = nets.iter().position(|&element| element == _tmp).unwrap();

其中nets&Vec<f32>。在我看来,这显然是不正确的。

与此等效的 Python(考虑到上述假设):

_i = nets.index(max(nets))

【问题讨论】:

标签: algorithm floating-point rust idioms


【解决方案1】:

这是否行不通的原因?

use std::cmp::Ordering;

fn example(nets: &Vec<f32>) {
    let index_of_max: Option<usize> = nets
        .iter()
        .enumerate()
        .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal))
        .map(|(index, _)| index);
}

【讨论】:

【解决方案2】:

之所以棘手是因为f32 没有实现Ord。那是因为NaN的值阻止了浮点数形成一个全序,这违反了Ord的约定。

有 3rd 方 crate 通过定义一个不允许包含 NaN 的数字类型包装器来解决此问题。一个例子是ordered-float。如果您使用此 crate 来首先准备集合以包含 NotNan 值,那么您可以编写非常接近您最初想法的代码:

use ordered_float::NotNan;

let non_nan_floats: Vec<_> = nets.iter()
    .cloned()
    .map(NotNan::new)       // Attempt to convert each f32 to a NotNan
    .filter_map(Result::ok) // Unwrap the `NotNan`s and filter out the `NaN` values 
    .collect();

let max = non_nan_floats.iter().max().unwrap();
let index = non_nan_floats.iter().position(|element| element == max).unwrap();

将此添加到Cargo.toml:

[dependencies]
ordered-float = "1.0.1"

奖励材料:类型转换可以真正做到零成本(假设你真的确定没有NaN值!),利用 NotNan 具有透明表示的事实:

let non_nan_floats: Vec<NotNan<f32>> = unsafe { mem::transmute(nets) };

【讨论】:

  • 谢谢彼得,我有这样的印象。事实上,这就是我在第一行中陈述假设的原因。然而,由于这是一个
  • @Juxhin 请记住,Rust 标准库的设计理念之一是慢慢采用新的 API,而是鼓励功能在社区中出现。因此,您将很难找到一个没有外部依赖项的 Rust 项目,而了解流行的 crates 确实是在 Rust 中提高生产力的重要部分。
  • 不创建集合:let result = nets .iter() .cloned() .map(NotNan::new) .enumerate() .filter_map(|(i, nan)| { let nan = nan.ok()?; Some((i, nan))}) .max_by(|a, b| a.1.cmp(&amp;b.1));
  • .map(NotNan::new).filter_map(Result::ok) -> .flat_map(NotNan::new)
【解决方案3】:

我可能会这样做:

fn main() -> Result<(), Box<std::error::Error>> {
    let samples = vec![
        0.28, 0.3102, 0.9856, 0.3679, 0.3697, 0.46, 0.4311, 0.9781, 0.9891, 0.5052, 0.9173, 0.932,
        0.8365, 0.5822, 0.9981, 0.9977,
    ];

    // Use enumerate to get the index
    let mut iter = samples.iter().enumerate();
    // we get the first entry
    let init = iter.next().ok_or("Need at least one input")?;
    // we process the rest
    let result = iter.try_fold(init, |acc, x| {
        // return None if x is NaN
        let cmp = x.1.partial_cmp(acc.1)?;
        // if x is greater the acc
        let max = if let std::cmp::Ordering::Greater = cmp {
            x
        } else {
            acc
        };
        Some(max)
    });
    println!("{:?}", result);

    Ok(())
}

这可以通过在迭代器上添加一个特征来实现,例如函数try_max_by

【讨论】:

    【解决方案4】:

    您可以通过以下方式找到最大值:

    let mut max_value = my_vec.iter().fold(0.0f32, |max, &val| if val > max{ val } else{ max });
    

    找到max_value后,你可以追踪它在向量中的位置:

    let index = my_vec.iter().position(|&r| r == max_value).unwrap();
    

    要获得此结果,您需要在同一个向量上迭代两次。为了提高性能,您可以在fold 迭代中将具有最大值的索引值作为元组返回。

    Playground

    【讨论】:

    • 你可以通过my_vec.iter().fold(0.0f32, |max, &amp;val| if val &gt; max{ val } else{ max });找到单行的最大值,解包会使代码不稳定
    • @Ömer 在我看来,当你得到你不知道如何处理的数据时恐慌通常比处理坏数据更可取。如果 NaN 应该是不可能的,unwrap 不是处理它的错误方法
    • @trentcl 感谢您指出,我认为它可能会在空向量上出现恐慌,那么这段代码可以被认为是整洁和稳定的,假设是关于 NAN
    • 哦,嗯。我误解了代码一点。它似乎对空的 Vec 感到恐慌,我同意这是一个问题。
    • 是的,我将其编写为伪代码,是的,我假设在 vec 中存在最大值并在 vec 中找到之后调用索引检查。所以,如果你要在不同的代码块中调用检查索引,那么你需要先检查它的最大值存在性
    【解决方案5】:

    我从@Akiner Alkan 那里得到了答案,并对其进行了一些调整,这是一个简单的单线,没有任何展开,可以完成这项工作:

    let maxi = my_vec.iter().enumerate().fold((0, 0.0), |max, (ind, &val)| if val > max.1 {(ind, val)} else {max});
    

    (PS: 新手 rust 和 StackOverflow 的第一篇文章,如果我做错了,请不要评判我 :D)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-10
      • 1970-01-01
      • 2013-06-01
      • 2017-12-21
      • 2019-07-31
      • 1970-01-01
      • 1970-01-01
      • 2021-10-20
      相关资源
      最近更新 更多