ทดสอบว่าค่าทั้งหมดในรายการไม่ซ้ำกันหรือไม่


91

ฉันมีรายการไบต์เล็ก ๆ และต้องการทดสอบว่าเป็นค่าที่ต่างกันทั้งหมด ตัวอย่างเช่นฉันมีสิ่งนี้:

List<byte> theList = new List<byte> { 1,4,3,6,1 };

วิธีใดที่ดีที่สุดในการตรวจสอบว่าค่าทั้งหมดแตกต่างกันหรือไม่?


2
เนื่องจากเป็นคำถามทั่วไปในห้องเรียนฉันจะตอบด้วยคำถาม คุณจะทำอย่างไรถ้ามันถูกจัดเรียง?
ctrl-alt-delor

คำตอบ:


171
bool isUnique = theList.Distinct().Count() == theList.Count();

แค่อยากรู้อยากเห็น: ข้อกำหนดเรื่องพื้นที่และเวลาคืออะไร?
dtb

10
@dtb ควรจะเกี่ยวกับ O (N) แน่นอนว่าการพิจารณาว่านี่คือ "รายการเล็ก ๆ " มันจะรวดเร็วทันใจด้วยอัลกอริทึมเกือบทั้งหมด IMO สิ่งนี้ชนะในเรื่องความสามารถในการอ่านและความกระชับและเนื่องจากความเร็วไม่ใช่ปัญหาจึงทำให้สมบูรณ์แบบ
Tim S.

2
สิ่งนี้มีประสิทธิภาพมากกว่าที่ควรจะเป็น
Jodrell

74

นี่เป็นอีกแนวทางหนึ่งที่มีประสิทธิภาพมากกว่าEnumerable.Distinct+ Enumerable.Count(ยิ่งถ้าลำดับไม่ใช่ประเภทคอลเลกชัน) ใช้HashSet<T>ซึ่งกำจัดรายการที่ซ้ำกันมีประสิทธิภาพมากในการค้นหาและมีคุณสมบัติการนับ:

var distinctBytes = new HashSet<byte>(theList);
bool allDifferent = distinctBytes.Count == theList.Count;

หรือวิธีอื่นที่ละเอียดกว่าและมีประสิทธิภาพ:

var diffChecker = new HashSet<byte>();
bool allDifferent = theList.All(diffChecker.Add);

HashSet<T>.Addส่งคืนfalseหากไม่สามารถเพิ่มองค์ประกอบได้เนื่องจากมีอยู่แล้วในไฟล์HashSet. Enumerable.Allหยุดที่ "เท็จ" ตัวแรก


1
เพื่อให้ง่ายและเห็นได้ชัดไม่ได้ผมคิดว่าเหตุผลที่เกี่ยวกับมันเป็นครั้งแรก :) ผมใช้นี้หนึ่งซับในการทดสอบหน่วยเพื่อยืนยัน 10 Assert.IsTrue(samples.Add(AwesomeClass.GetUnique()));ล้านองค์ประกอบที่สร้างโดยรหัสที่น่ากลัวของฉันจะไม่ซ้ำกันจริงๆ พวกเขาเป็นและเป็น :) +1 สำหรับคุณ Tim :)
grapkulec

1
ฉันได้ลองคำตอบของคุณสำหรับคำถามนี้แล้ว แต่ไม่ได้ผลครับ: stackoverflow.com/questions/34941162/…
Learning-Overthinker-Confused

ควรเป็นสิ่งนี้:bool allDifferent = theList.All(s => diffChecker.Add(s))
mike nelson

2
ไม่ไม่จำเป็น ในกรณีนี้คุณสามารถส่งต่อผู้แทนได้โดยตรง
Tim Schmelter

1
@ AndréReichelt - ฉันเพิ่งเปิดรหัสของคุณและสถานการณ์ที่สาม ( List.All(HashSet.Add)) ดูเหมือนจะเร็วกว่าอีกสองอย่างในเกือบทุกกรณี
Kyle Delaney

7

โอเคนี่เป็นวิธีที่มีประสิทธิภาพที่สุดที่ฉันคิดได้จากการใช้. Net มาตรฐาน

using System;
using System.Collections.Generic;

public static class Extension
{
    public static bool HasDuplicate<T>(
        this IEnumerable<T> source,
        out T firstDuplicate)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var checkBuffer = new HashSet<T>();
        foreach (var t in source)
        {
            if (checkBuffer.Add(t))
            {
                continue;
            }

            firstDuplicate = t;
            return true;
        }

        firstDuplicate = default(T);
        return false;
    }
}

โดยพื้นฐานแล้วอะไรคือจุดในการแจกแจงลำดับทั้งหมดสองครั้งหากสิ่งที่คุณต้องการทำคือค้นหารายการแรกที่ซ้ำกัน

ฉันสามารถปรับสิ่งนี้ให้เหมาะสมได้มากขึ้นโดยการจัดเรียงลำดับองค์ประกอบว่างเปล่าและองค์ประกอบเดี่ยวแบบพิเศษ แต่จะลดลงจากความสามารถในการอ่าน / การบำรุงรักษาโดยได้รับน้อยที่สุด


เป็นการเพิ่มมูลค่าที่ซ้ำกันออกไปซึ่งมีประโยชน์มากสำหรับการตรวจสอบความถูกต้อง
Pac0

ฉันได้ทดสอบ 3 โซลูชันที่นี่และนี่เป็นวิธีที่มีประสิทธิภาพมากที่สุดในหน้านี้ มีการพิมพ์ผิดเล็กน้อย (เช่นsequenceควรจะเป็นsource) แต่ใช้งานได้ดีเมื่อได้รับการแก้ไขแล้ว
ไมค์เนลสัน

@mikenelson ที่ควรจะดีกว่า
Jodrell

2
เพื่อความอ่านง่ายฉันคิดว่ามันควรจะif (!checkBuffer.Add(t)) { firstDuplicate = t; return true }อยู่ในวง
เทีย

2

ตรรกะที่คล้ายกันในการDistinctใช้GroupBy:

var isUnique = theList.GroupBy(i => i).Count() == theList.Count;

สิ่งนี้มีประโยชน์หากคุณต้องการตรวจสอบความเป็นเอกลักษณ์เกี่ยวกับคุณสมบัติtheList.GroupBy(o => o.SomeProperty).Count() == theList.Count;ในขณะที่ Distinct () ไม่อนุญาต
Rev1.0

1

นอกจากนี้ยังสามารถทำได้: ใช้แฮชเซ็ต

var uniqueIds = new HashSet<long>(originalList.Select(item => item.Id));

            if (uniqueIds.Count != originalList.Count)
            {
            }

0

มีวิธีแก้มากมาย

และไม่ต้องสงสัยเลยว่าสวยขึ้นด้วยการใช้ LINQ ตามที่ "juergen d" และ "Tim Schmelter" กล่าวไว้

แต่ถ้าคุณเปิดเผย "ความซับซ้อน" และความเร็วทางออกที่ดีที่สุดคือการนำไปใช้ด้วยตัวเอง หนึ่งในวิธีแก้ปัญหาคือการสร้างอาร์เรย์ขนาด N (สำหรับไบต์คือ 256) และวนรอบอาร์เรย์และในการวนซ้ำทุกครั้งจะทดสอบดัชนีตัวเลขที่ตรงกันว่าค่าเป็น 1 ถ้าเป็นเช่นนั้นหมายความว่าฉันเพิ่มดัชนีอาร์เรย์แล้วดังนั้นอาร์เรย์จึงไม่แตกต่างกันมิฉะนั้นฉันจะเพิ่มเซลล์อาร์เรย์และทำการตรวจสอบต่อไป .


2
คุณสามารถใช้เวกเตอร์บิตที่มี 256 บิต = 32 ไบต์ = 8 จำนวนเต็ม แต่ Big O = O (n) ของคุณจะยังคงเหมือนกับการใช้ Hashet ที่เสนอในคำตอบอื่น ๆ
BrokenGlass

นี่คือ O (n) ดังนั้นอาจจะเร็วที่สุด (ทดสอบ) การตรวจสอบจะนับว่าคุณไปหรือในตอนท้ายจะเร็วที่สุด? ฉันสงสัยว่าในตอนท้ายจะปรับปรุงกรณีที่เลวร้ายที่สุด แต่ในขณะที่คุณไปอาจปรับปรุงค่าเฉลี่ยและกรณีที่ดีที่สุด) หากไม่มีรายการที่ซ้ำกันนี่จะเป็นผลงานที่แย่ที่สุด นอกจากนี้สำหรับประเภทข้อมูลที่ใหญ่กว่านี้จะทำงานได้ไม่ดีสำหรับประเภท 16 บิตคุณจะต้องใช้การนับ 64k บิต 64k (8k ไบต์) แต่สำหรับสิ่งที่ใหญ่กว่าการใช้หน่วยความจำจะเริ่มงี่เง่า อย่างไรก็ตามฉันชอบคำตอบนี้สำหรับค่า 8 บิต
ctrl-alt-delor

1
@TamusJRoyce ถ้าคุณต้องการเก็บ 4294967296 ความเป็นไปได้คุณต้อง 4GB ไม่ใช่ 42MB (หรือ 512MB ของคุณใช้การปิดบังบิต)
tigrou

ไม่แน่ใจว่าฉันคิดอะไรอยู่ "จัดสรรหน่วยความจำ 42MB + เพื่อเก็บความเป็นไปได้ทั้งหมด 4294967296 และใช้ตัวนับที่เก็บข้อมูลแบบง่าย ๆ หรือแม้แต่ใช้ bit masking xor และตรวจสอบว่ามีการเปลี่ยนแปลงบิตใดจากจริงเป็นเท็จหรือไม่ 42MB + / 8 = 5MB + ค่าใช้จ่ายดูเหมือนจะมากเกินไปสำหรับฮาร์ดแวร์ในปัจจุบัน แต่ วันหนึ่งอาจได้บุญ " ไม่ใช่ความคิดเห็นที่เกี่ยวข้องจริงๆ แฮชเซ็ตจะดีที่สุด หากคุณกำลังจัดการกับอาร์เรย์ขนาดใหญ่มากคุณคาดว่าจะมีหน่วยความจำขนาดใหญ่มาก แต่ในกรณีที่แปลกประหลาดเช่นนี้การสืบเชื้อด้วยอัลกอริทึม CRC จะดีกว่า แมปกับพหุนาม ถ้าปิดให้ประเมิน ขอบคุณ @tigrou!
TamusJRoyce

0

และอีกวิธีหนึ่งหากคุณต้องการค้นหาค่าที่ซ้ำกัน

var values = new [] { 9, 7, 2, 6, 7, 3, 8, 2 };

var sorted = values.ToList();
sorted.Sort();
for (var index = 1; index < sorted.Count; index++)
{
    var previous = sorted[index - 1];
    var current = sorted[index];
    if (current == previous)
        Console.WriteLine(string.Format("duplicated value: {0}", current));
}

เอาท์พุต:

duplicated value: 2
duplicated value: 7

http://rextester.com/SIDG48202


0

ฉันตรวจสอบว่า IEnumerable (aray, list, ฯลฯ ) ไม่ซ้ำกันเช่นนี้:

var isUnique = someObjectsEnum.GroupBy(o => o.SomeProperty).Max(g => g.Count()) == 1;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.