목록에서 임의의 항목에 액세스하는 방법은 무엇입니까?


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 Shuffle 알고리즘을 찾아보십시오 : en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mehrdad Afshari

2
아마도 0 기반 인덱스보다 큰 요소 max에 액세스하지 않으려면 "rnd.Next (list.Count)"대신 "rnd.Next (list.Count-1)"이어야합니까?
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 2016 년

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 ();
Andy Brown

3
대부분의 경우 중요하지 않지만 rnd.Next를 사용하는 것보다 속도가 느릴 수 있습니다. OTOH는 목록뿐만 아니라 IEnumerable <T>에서도 작동합니다.
solublefish

12
그게 얼마나 무작위인지 잘 모르겠습니다. 길드는 무작위가 아니라 독특합니다.
Pomber

1
더 좋은 생각이 답변의 확장 버전 @ solublefish의 의견 멋지게로 요약되는 이 답변 (플러스 내 댓글 비슷한 질문에).
Neo

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되어 있으므로 두 번째 오버로드가 필요하지 않습니다.
다이

3

왜 안되 겠어요 :

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}

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는 임의의 숫자 배타적 상한 "이기 때문에 maxValuemethod 의 매개 변수 Next는 목록에서 하나의 요소가 아닌 여러 요소 여야 한다고 말하고 싶습니다 .
David Ferenczy Rogožan

1

이 ExtensionMethod를 한동안 사용했습니다.

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

A는 대신 다음, 목록 내에서 항목의 순서가 추출에서 중요하지 않은 경우 나, 다른 접근 방식을 제안합니다 (각 항목은 한 번만 선택해야합니다) 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.