2013-04-14 24 views
5

tôi muốn tạo một HashSet cho số thực (hiện nay Double s) sử dụng một khoan dung định nghĩa (epsilon), (cf Assert.assertEquals(double, double, double)
Kể từ khi sử dụng Double.equals() chỉ hoạt động cho bình đẳng chính xác và Double là một lớp học chính thức Ý tưởng ban đầu của tôi là mở rộng HashSet (ví dụ: DoubleHashSet), với phương thức setEpsilon(double) và tạo một lớp mới ComparableDouble trong đó equals() sử dụng giá trị này từ DoubleHashSet. Tuy nhiên, tôi muốn kiểm tra xem có các giải pháp hiện tại hay không đã có và các thư viện F/OSS hiện có.Tạo một HashSet cho đôi

(thứ hai trong tương lai, tôi sẽ muốn mở rộng điều này thành các số thực - ví dụ: hình chữ nhật và hình khối - do đó, cách tiếp cận chung là thích hợp hơn

LƯU Ý: @NPE đã đề xuất không thể. Thật không may tôi nghi ngờ đây là chính thức chính xác :-) Vì vậy, tôi tự hỏi nếu có phương pháp gần đúng ... Những người khác phải có vấn đề này và giải quyết nó khoảng. (Tôi đã thường xuyên sử dụng một công cụ Real.isEqual(a, b, epsilon) và nó rất hữu ích.) Tôi đang chuẩn bị để chấp nhận một số lỗi không thường xuyên của transitivity.

LƯU Ý: Tôi sẽ sử dụng TreeSet để giải quyết vấn đề "gần bằng()". Sau đó tôi sẽ so sánh complexNumbers, hình chữ nhật (và các đối tượng phức tạp hơn) và nó thực sự hữu ích để có thể thiết lập một giới hạn trong đó 2 điều là bằng nhau. Không có đơn đặt hàng tự nhiên đơn giản của complexNumbers (có lẽ một cách tiếp cận Cantor sẽ làm việc), nhưng chúng ta có thể biết liệu chúng gần như bằng nhau hay không.

+0

Bạn dường như đang đi đúng hướng ở đây. Mở rộng Double và cung cấp việc thực hiện equals của bạn có vẻ là cách tiếp cận đúng. – anubhava

+1

@anubhava OK - Tôi sẽ thêm một số mã giả để nhận xét –

+0

@anubhava đã xóa mã vì các câu trả lời khác thay thế cho nó –

Trả lời

4

Có một số sai sót cơ bản trong cách tiếp cận này.

HashSet sử dụng equals() để kiểm tra hai phần tử để bình đẳng. The contract on equals() has the following among its requirements:

Đó là bắc cầu: đối với bất kỳ giá trị tham chiếu không null x, y, và z, nếu x.equals(y) lợi nhuận truey.equals(z) lợi nhuận true, sau đó x.equals(z) nên trở true.

Bây giờ xem xét ví dụ sau:

x = 0.0 
y = 0.9 * epsilon 
z = 1.8 * epsilon 

Rõ ràng là chương trình so sánh đề xuất của bạn sẽ phá vỡ các yêu cầu transitivity (x bằng yy bằng z, nhưng x không bằng z). Trong những trường hợp này, HashSet không thể hoạt động chính xác.

Hơn nữa, hashCode() sẽ tạo ra những thách thức bổ sung, vì lý do sau requirement:

Nếu hai vật đều bình đẳng theo phương pháp equals(Object), sau đó gọi phương thức hashCode trên mỗi của hai đối tượng phải xuất trình cùng kết quả số nguyên.

Yêu cầu hashCode() có thể được sideste bằng cách sử dụng TreeSet thay vì HashSet.

+0

Tôi có cảm giác chìm bạn đúng. Nhưng tôi đã sẵn sàng để có một mức độ thất bại nhỏ trong việc này. Ví dụ tôi có thể tạo số nguyên gần nhất thành 10 * (- log (epsilon)) làm hashCode(). Nếu nó đôi khi thất bại nó sẽ không phải là kết thúc của thế giới. –

+0

Vấn đề 'hashCode()' có thể được phá vỡ bằng cách sử dụng một 'TreeSet' thay vào đó, nhưng yêu cầu transitivity vẫn giữ nguyên. –

+0

@Philipp Âm thanh đầy hứa hẹn. Bạn có thể cho tôi một ví dụ? –

1

gì tôi sẽ làm là quanh đôi trước khi sử dụng chúng (giả định này là phù hợp)

ví dụ

public static double roundByFactor(double d, long factor) { 
    return (double) Math.round(d * factor)/factor; 
} 

TDoubleHashSet set = new TDoubleHashSet(); // more efficient than HashSet<Double> 
set.add(roundByFactor(1.001, 100)); 
set.add(roundByFactor(1.005, 100)); 
set.add(roundByFactor(1.01, 100)); 
// set has two elements. 

Bạn có thể bao bọc hành vi này trong DoubleHashSet của riêng bạn. Nếu bạn muốn đặt trước giá trị ban đầu, bạn có thể sử dụng HashMap hoặc TDoubleDoubleHashMap trong đó khóa là giá trị được làm tròn và giá trị là giá trị ban đầu.

+0

Rất cám ơn. Tôi có giả định đây là 'gnu.trove.set.hash.TDoubleHashSet'? Nếu có vẻ như vậy (thoạt nhìn) những gì tôi đã tìm kiếm miễn là nó không có giấy phép GPL. [xem http://trove4j.sourceforge.net/javadocs/gnu/trove/set/hash/TDoubleHashSet.html] –

+1

Đó là LGPL tốt cho cộng đồng của tôi và hầu hết các chiến lược phân phối –

0

tôi đã thực hiện cách tiếp cận @ NPE của (Tôi đã chấp nhận/câu trả lời của mình để s/anh nhận được những điểm :-) và cung cấp cho các mã ở đây

//Create a comparator: 
public class RealComparator implements Comparator<Double> { 

    private double epsilon = 0.0d; 

    public RealComparator(double eps) { 
     this.setEpsilon(eps); 
    } 

    /** 
    * if Math.abs(d0-d1) <= epsilon 
    * return -1 if either arg is null 
    */ 
    public int compare(Double d0, Double d1) { 
     if (d0 == null || d1 == null) { 
      return -1; 
     } 
     double delta = Math.abs(d0 - d1); 
     if (delta <= epsilon) { 
      return 0; 
     } 
     return (d0 < d1) ? -1 : 1; 
    } 

    /** set the tolerance 
    * negative values are converted to positive 
    * @param epsilon 
    */ 
    public void setEpsilon(double epsilon) { 
     this.epsilon = Math.abs(epsilon); 
    } 

và thử nghiệm nó

public final static Double ONE = 1.0; 
public final static Double THREE = 3.0; 

@Test 
public void testTreeSet(){ 
    RealComparator comparator = new RealComparator(0.0); 
    Set<Double> set = new TreeSet<Double>(comparator); 
    set.add(ONE); 
    set.add(ONE); 
    set.add(THREE); 
    Assert.assertEquals(2, set.size()); 
} 
@Test 
public void testTreeSet1(){ 
    RealComparator comparator = new RealComparator(0.0); 
    Set<Double> set = new TreeSet<Double>(comparator); 
    set.add(ONE); 
    set.add(ONE-0.001); 
    set.add(THREE); 
    Assert.assertEquals(3, set.size()); 
} 
@Test 
public void testTreeSet2(){ 
    RealComparator comparator = new RealComparator(0.01); 
    Set<Double> set = new TreeSet<Double>(comparator); 
    set.add(ONE); 
    set.add(ONE - 0.001); 
    set.add(THREE); 
    Assert.assertEquals(2, set.size()); 
} 
@Test 
public void testTreeSet3(){ 
    RealComparator comparator = new RealComparator(0.01); 
    Set<Double> set = new TreeSet<Double>(comparator); 
    set.add(ONE - 0.001); 
    set.add(ONE); 
    set.add(THREE); 
    Assert.assertEquals(2, set.size()); 
} 
+0

LƯU Ý: Tôi đã triển khai điều này cho các bộ dữ liệu . Vì không có trật tự tự nhiên nó "cư xử kỳ lạ" - nó có thể tạo ra một số tập hợp con của tập hợp dự định. –