วิธีการเข้าถึงรายการสุ่มในรายการ?


233

ฉันมี ArrayList และฉันต้องสามารถคลิกปุ่มแล้วสุ่มเลือกสตริงจากรายการนั้นและแสดงในกล่องข้อความ

ฉันจะทำสิ่งนี้อย่างไร

คำตอบ:


404
  1. สร้างตัวอย่างของการRandomเรียนที่ไหนสักแห่ง โปรดทราบว่ามันสำคัญมากที่จะไม่สร้างอินสแตนซ์ใหม่ทุกครั้งที่คุณต้องการหมายเลขสุ่ม คุณควรใช้อินสแตนซ์เก่าเพื่อให้เกิดความสม่ำเสมอในตัวเลขที่สร้างขึ้น คุณสามารถมีstaticฟิลด์ที่ใดที่หนึ่ง (ระมัดระวังเกี่ยวกับปัญหาความปลอดภัยของเธรด):

    static Random rnd = new Random();
  2. ขอให้Randomอินสแตนซ์ให้หมายเลขสุ่มกับจำนวนสูงสุดของรายการในArrayList:

    int r = rnd.Next(list.Count);
  3. แสดงสตริง:

    MessageBox.Show((string)list[r]);

มีวิธีที่ดีในการแก้ไขเพื่อไม่ให้หมายเลขซ้ำกันหรือไม่ สมมติว่าฉันต้องการสับไพ่โดยสุ่มเลือกทีละครั้ง แต่เห็นได้ชัดว่าไม่สามารถเลือกไพ่ใบเดียวกันได้สองครั้ง
AdamMc331

7
@ McAdam331 ค้นหาอัลกอริทึมแบบสุ่มของ Fisher-Yates: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
นี่ควรเป็น "rnd.Next (list.Count-1)" แทนที่จะเป็น "rnd.Next (list.Count)" เพื่อหลีกเลี่ยงการเข้าถึงองค์ประกอบสูงสุดซึ่งจะเป็นหนึ่งในดัชนีที่ใช้ 0 หรือไม่
B. Clay Shannon

22
@ B.ClayShannon ไม่ได้หมายเลขบนในการNext(max)โทรเป็นเอกสิทธิ์
Mehrdad Afshari

1
เมื่อรายการว่างเปล่าจะเป็นอย่างไร
tsu1980

137

ฉันมักจะใช้วิธีการขยายขนาดเล็กนี้:

public static class EnumerableExtension
{
    public static T PickRandom<T>(this IEnumerable<T> source)
    {
        return source.PickRandom(1).Single();
    }

    public static IEnumerable<T> PickRandom<T>(this IEnumerable<T> source, int count)
    {
        return source.Shuffle().Take(count);
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.OrderBy(x => Guid.NewGuid());
    }
}

สำหรับรายการที่พิมพ์อย่างมากสิ่งนี้จะอนุญาตให้คุณเขียน:

var strings = new List<string>();
var randomString = strings.PickRandom();

หากสิ่งที่คุณมีคือ ArrayList คุณสามารถส่งได้:

var strings = myArrayList.Cast<string>();

ความซับซ้อนของสิ่งเหล่านั้นคืออะไร? ลักษณะขี้เกียจของ IEnumerable หมายความว่าไม่ใช่ O (N) หรือไม่
Dave Hillier

17
คำตอบนี้จะทำการสุ่มรายการซ้ำทุกครั้งที่คุณเลือกหมายเลขสุ่ม การคืนค่าดัชนีแบบสุ่มมีประสิทธิภาพมากขึ้นโดยเฉพาะอย่างยิ่งสำหรับรายการขนาดใหญ่ ใช้สิ่งนี้ใน PickRandom - return list[rnd.Next(list.Count)];
swax

นี่ไม่ได้สับเปลี่ยนรายการดั้งเดิม แต่จะอยู่ในรายการอื่นที่จริงแล้วซึ่งอาจยังไม่ดีสำหรับประสิทธิภาพหากรายการมีขนาดใหญ่พอ ..
nawfal

.OrderBy (.) ไม่ได้สร้างรายการอื่น - มันสร้างวัตถุประเภท IEnumerable <T> ซึ่งวนซ้ำผ่านรายการดั้งเดิมในวิธีการสั่งซื้อ
Johan Tidén

5
อัลกอริทึมการสร้าง GUID นั้นไม่สามารถคาดเดาได้ แต่ไม่สุ่ม ลองถืออินสแตนซ์ของRandomในสถานะคงที่แทน
ได

90

คุณทำได้:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()

สวย. ใน ASP.NET MVC 4.5 ฉันต้องเปลี่ยนรายการนี้เป็น: list.OrderBy (x => Guid.NewGuid ()) FirstOrDefault ();
แอนดี้บราวน์

3
มันจะไม่สำคัญในกรณีส่วนใหญ่ แต่มันอาจช้ากว่าการใช้ rnd.Next OTOH มันจะทำงานกับ IE จำนวน <T> ไม่ใช่แค่รายการ
solublefish เมื่อ

12
ไม่แน่ใจว่าสุ่มได้ขนาดไหน Guids นั้นมีเอกลักษณ์ไม่สุ่ม
เครื่องบินทิ้งระเบิด

1
ฉันคิดว่าคำตอบนี้มีเวอร์ชันที่ดีขึ้นและขยายออกไปและความคิดเห็นของ @ solublefish นั้นได้ถูกรวมไว้ในคำตอบนี้ (รวมถึงความคิดเห็นของฉัน ) ด้วยคำถามที่คล้ายกัน
นีโอ

23

สร้างRandomตัวอย่าง:

Random rnd = new Random();

ดึงสตริงแบบสุ่ม:

string s = arraylist[rnd.Next(arraylist.Count)];

โปรดจำไว้ว่าหากคุณทำเช่นนี้บ่อยครั้งคุณควรใช้Randomวัตถุนั้นอีกครั้ง วางไว้เป็นเขตข้อมูลแบบคงที่ในชั้นเรียนเพื่อให้เริ่มต้นได้เพียงครั้งเดียวแล้วเข้าถึง


20

หรือคลาสเสริมส่วนขยายแบบนี้:

public static class CollectionExtension
{
    private static Random rng = new Random();

    public static T RandomElement<T>(this IList<T> list)
    {
        return list[rng.Next(list.Count)];
    }

    public static T RandomElement<T>(this T[] array)
    {
        return array[rng.Next(array.Length)];
    }
}

จากนั้นเพียงแค่โทร:

myList.RandomElement();

ทำงานสำหรับอาร์เรย์เช่นกัน

ฉันจะหลีกเลี่ยงการโทรOrderBy()เพราะอาจมีราคาแพงสำหรับคอลเลกชันขนาดใหญ่ ใช้คอลเลกชันที่มีการจัดทำดัชนีเช่นList<T>หรืออาร์เรย์เพื่อจุดประสงค์นี้


3
อาร์เรย์ใน. NET มีการใช้งานแล้วIListดังนั้นการโอเวอร์โหลดครั้งที่สองจึงไม่จำเป็น
ได


2
ArrayList ar = new ArrayList();
        ar.Add(1);
        ar.Add(5);
        ar.Add(25);
        ar.Add(37);
        ar.Add(6);
        ar.Add(11);
        ar.Add(35);
        Random r = new Random();
        int index = r.Next(0,ar.Count-1);
        MessageBox.Show(ar[index].ToString());

3
ในขณะที่ข้อมูลโค้ดนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายช่วยปรับปรุงคุณภาพของโพสต์ของคุณ จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและคนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ
gunr2171

3
ฉันจะบอกว่าmaxValueพารามิเตอร์ของวิธีการNextควรเป็นเพียงจำนวนขององค์ประกอบในรายการไม่ลบหนึ่งเพราะตามเอกสาร " maxValue เป็นขอบเขตเฉพาะพิเศษของหมายเลขสุ่ม "
David Ferenczy Rogožan

1

ฉันใช้ส่วนขยายนี้มาระยะหนึ่งแล้ว:

public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int count)
{
    if (count <= 0)
      yield break;
    var r = new Random();
    int limit = (count * 10);
    foreach (var item in list.OrderBy(x => r.Next(0, limit)).Take(count))
      yield return item;
}

คุณลืมเพิ่มอินสแตนซ์ของชั้นเรียนแบบสุ่ม
bafsar

1

ฉันจะแนะนำวิธีการที่แตกต่างกันหากลำดับของรายการในรายการไม่สำคัญในการแยก (และแต่ละรายการควรเลือกเพียงครั้งเดียว) จากนั้นแทนที่จะใช้Listคุณสามารถใช้ชุดConcurrentBagที่ปลอดภัยต่อการเรียงลำดับ วัตถุ:

var bag = new ConcurrentBag<string>();
bag.Add("Foo");
bag.Add("Boo");
bag.Add("Zoo");

EventHandler:

string result;
if (bag.TryTake(out result))
{
    MessageBox.Show(result);
}

TryTakeจะพยายามที่จะดึงวัตถุ "สุ่ม" จากการเก็บเรียงลำดับ


0

ฉันต้องการไอเท็มเพิ่มเติมมากกว่าหนึ่งอัน ดังนั้นฉันจึงเขียนสิ่งนี้:

public static TList GetSelectedRandom<TList>(this TList list, int count)
       where TList : IList, new()
{
    var r = new Random();
    var rList = new TList();
    while (count > 0 && list.Count > 0)
    {
        var n = r.Next(0, list.Count);
        var e = list[n];
        rList.Add(e);
        list.RemoveAt(n);
        count--;
    }

    return rList;
}

ด้วยสิ่งนี้คุณจะได้รับองค์ประกอบที่คุณต้องการแบบสุ่มดังนี้

var _allItems = new List<TModel>()
{
    // ...
    // ...
    // ...
}

var randomItemList = _allItems.GetSelectedRandom(10); 

0

การพิมพ์ชื่อประเทศแบบสุ่มจากไฟล์ JSON
รุ่น:

public class Country
    {
        public string Name { get; set; }
        public string Code { get; set; }
    }

Implementaton:

string filePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\")) + @"Data\Country.json";
            string _countryJson = File.ReadAllText(filePath);
            var _country = JsonConvert.DeserializeObject<List<Country>>(_countryJson);


            int index = random.Next(_country.Count);
            Console.WriteLine(_country[index].Name);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.