【问题标题】:Take element with lowest value from a HashSet?从 HashSet 中获取具有最低值的元素?
【发布时间】:2018-05-22 16:42:45
【问题描述】:

我正在尝试使用一种方法创建一个堆,该方法返回具有最小f-value 的节点,同时将其从堆本身中删除。

堆之后应该仍然可以使用,只是没有删除的值:

Node 结构及其实现:

use std::hash::{Hash, Hasher};

#[derive(Debug)]
struct Node {
    x: f64,
    y: f64,
    f: f64,
}

impl Node {
    fn to_bits(&self) -> u128 {
        let xb = self.x.to_bits() as u128;
        let yb = self.y.to_bits() as u128;
        (xb << 64) + yb
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Node) -> bool {
        self.x == other.x && self.y == other.y
    }
}

impl Eq for Node {}

impl Hash for Node {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.to_bits().hash(state)
    }
}

Heap 结构:

use std::f64;
use std::collections::HashSet;

#[derive(Debug)]
struct Heap {
    pool: HashSet<Node>,
}

impl Heap {
    fn add(mut self, node: Node) -> Heap {
        self.pool.insert(node);
        self
    }

    fn consume(mut self) -> Node {
      // find the node with minimum f-value in self.pool
      // and "take" it, aka remove it from the pool
      // and then return it
      Node { x: 0.0, y: 0.0, f: 0.0 } // dummy node so that the code compiles
    }
}

还有main 函数:

fn main() {
    let n1 = Node { x: 10.0, y: 11.0, f: 5.0 };
    let n2 = Node { x: 11.0, y: 12.0, f: 7.0 };
    let n3 = Node { x: 12.0, y: 13.0, f: 3.0 };
    let n4 = Node { x: 14.0, y: 14.0, f: 4.0 };

    let mut heap = Heap { pool: HashSet::new() };
    heap = heap.add(n1);
    heap = heap.add(n2);
    heap = heap.add(n3);
    heap = heap.add(n4);

    let minimal_n1 = heap.consume();
    println!("{:?}", minimal_n1);
    // should print
    // Node { x: 12.0, y: 13.0, f: 3.0 }

    let minimal_n2 = heap.consume();
    println!("{:?}", minimal_n2);
    // should print
    // Node { x: 14.0, y: 14.0, f: 4.0 }

    println!("Heap has {} nodes", heap.pool.len());
    // should print
    // Heap has 2 nodes
}

这是我迄今为止对consume所做的尝试:

fn consume(mut self) -> Node {
    let mut min_f = f64::MAX;
    let mut min_node: Option<&Node> = None;

    for n in self.pool.iter() {
        if n.f < min_f {
            min_f = n.f;
            min_node = Some(n);
        }
    }

    self.pool.take(&min_node.unwrap()).unwrap()
}

问题是self.pooliter() 方法不可变地借用,因此self.pool.take() 不能同时可变地借用它。

使consume 方法获取并返回f-value 在pool 中最小的节点的最佳方法是什么?

注意事项:

  • 需要一个 Set(或 Map),因为其他方法需要在 O(1) 中检索任何节点
  • 我不使用有序集(这很容易解决上述问题),因为添加/更新操作必须保持 O(1)
  • heap需要在移除minimum-f节点后才能访问,如示例所示

【问题讨论】:

    标签: rust hashset


    【解决方案1】:

    幸运的是,由于您按价值获取 self,因此这是一个很容易解决的问题。扔掉所有不是最低要求的东西Node

    fn consume(self) -> Node {
        self.pool
            .into_iter()
            .min_by(|a, b| a.f.partial_cmp(&b.f).expect("Found a NaN"))
            .expect("There was no minimum")
    }
    

    如果之后需要保留Heap,则需要在删除之前将找到的值与堆解除关联。克隆是最简单的解决方案:

    fn consume(&mut self) -> Node {
        let min = self.pool
            .iter()
            .min_by(|a, b| a.f.partial_cmp(&b.f).expect("Found a NaN"))
            .cloned()
            .expect("There was no minimum");
    
        self.pool.remove(&min);
    
        min
    }
    

    这确实需要您执行“额外的”哈希查找。由于您正在遍历整个HashSet,这似乎是一个相对较小的成本。


    如果您无法轻松克隆元素,请系好安全带。使用来自How to implement HashMap with two keys? 的想法,我们可以构造一个trait 对象,该对象可用于基于并行但等效的散列/相等实现来查找键:

    use std::borrow::Borrow;
    
    trait Key {
        fn as_bits(&self) -> u128;
    }
    
    impl Key for Node {
        fn as_bits(&self) -> u128 {
            let xb = self.x.to_bits() as u128;
            let yb = self.y.to_bits() as u128;
            (xb << 64) + yb
        }
    }
    
    impl Key for u128 {
        fn as_bits(&self) -> u128 { *self }
    }
    
    impl<'a> Hash for Key + 'a {
        fn hash<H: Hasher>(&self, h: &mut H) {
            self.as_bits().hash(h)
        }
    }
    
    impl<'a> PartialEq for Key + 'a {
        fn eq(&self, other: &Self) -> bool {
            self.as_bits() == other.as_bits()        
        }
    }
    
    impl<'a> Eq for Key + 'a {}
    
    impl<'a> Borrow<Key + 'a> for Node {
        fn borrow(&self) -> &(Key + 'a) {
            self
        }
    }
    
    impl<'a> Borrow<Key + 'a> for u128 {
        fn borrow(&self) -> &(Key + 'a) {
            self
        }
    }
    

    有了这个支持,我们可以将找到的元素转换为轻量级拥有的键,然后使用它再次查找:

    fn consume(&mut self) -> Node {
        let min_key = self.pool
            .iter()
            .min_by(|a, b| a.f.partial_cmp(&b.f).expect("Found a NaN"))
            .map(Node::as_bits)
            .expect("There was no minimum");
    
        let min_key: &Key = min_key.borrow();
        self.pool.take(min_key).unwrap()
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 1970-01-01
      • 2013-05-30
      • 2020-04-06
      • 2013-04-07
      • 2016-07-22
      • 1970-01-01
      • 2014-01-02
      • 2014-06-09
      相关资源
      最近更新 更多