.NET으로 문자열 배열을 무작위 화하는 가장 좋은 방법은 무엇입니까? 내 배열에는 약 500 개의 문자열이 포함되어 Array
있으며 동일한 문자열을 사용하지만 임의의 순서 로 새 문자열 을 만들고 싶습니다 .
답변에 C # 예를 포함하십시오.
myArray.Shuffled().ToArray()
(또는 myArray.Shuffle()
현재 배열을 변경하려는 경우)
.NET으로 문자열 배열을 무작위 화하는 가장 좋은 방법은 무엇입니까? 내 배열에는 약 500 개의 문자열이 포함되어 Array
있으며 동일한 문자열을 사용하지만 임의의 순서 로 새 문자열 을 만들고 싶습니다 .
답변에 C # 예를 포함하십시오.
myArray.Shuffled().ToArray()
(또는 myArray.Shuffle()
현재 배열을 변경하려는 경우)
답변:
.NET 3.5를 사용하는 경우 다음 IEnumerable coolness (C #이 아닌 VB.NET을 사용할 수 있지만 아이디어는 명확해야합니다 ...)를 사용할 수 있습니다.
Random rnd=new Random();
string[] MyRandomArray = MyArray.OrderBy(x => rnd.Next()).ToArray();
편집 : 확인 및 해당 VB.NET 코드는 다음과 같습니다.
Dim rnd As New System.Random
Dim MyRandomArray = MyArray.OrderBy(Function() rnd.Next()).ToArray()
두 번째 편집은 시간 기반 시퀀스를 반환하여 System.Random이 "스레드 안전하지 않고" "장난감 앱에만 적합"하다는 설명에 응답합니다. 배열을 무작위 화하는 루틴을 다시 입력하도록 허용합니다.이 경우 lock (MyRandomArray)
데이터를 손상시키지 않기 위해 어쨌든 같은 것이 필요 rnd
합니다.
또한 엔트로피의 원천 인 System.Random이 그다지 강력하지 않다는 것을 잘 이해하고 있어야합니다. MSDN 문서에 언급 된대로 System.Security.Cryptography.RandomNumberGenerator
보안 관련 작업을 수행하는 경우 파생 된 항목을 사용해야합니다. 예를 들면 다음과 같습니다.
using System.Security.Cryptography;
...
RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
string[] MyRandomArray = MyArray.OrderBy(x => GetNextInt32(rnd)).ToArray();
...
static int GetNextInt32(RNGCryptoServiceProvider rnd)
{
byte[] randomInt = new byte[4];
rnd.GetBytes(randomInt);
return Convert.ToInt32(randomInt[0]);
}
OrderBy
정렬 키를 내부적으로 캐시 하지 않으면 정렬 된 비교의 전이 속성을 위반하는 문제도 있습니다. OrderBy
올바른 결과 를 생성 한 디버그 모드 확인이 있으면 이론상 예외가 발생할 수 있습니다.
다음 구현은 Fisher-Yates 알고리즘 ( 일명 Knuth Shuffle)을 사용합니다. 그것은 O (n) 시간에 실행되고 그 뒤를 뒤섞습니다. 그래서 더 많은 코드 줄이지 만 '랜덤 정렬'기법보다 성능이 좋습니다. 비교 성능 측정에 대해서는 여기 를 참조 하십시오 . 암호화 이외의 용도로는 괜찮은 System.Random을 사용했습니다. *
static class RandomExtensions
{
public static void Shuffle<T> (this Random rng, T[] array)
{
int n = array.Length;
while (n > 1)
{
int k = rng.Next(n--);
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
}
용법:
var array = new int[] {1, 2, 3, 4};
var rng = new Random();
rng.Shuffle(array);
rng.Shuffle(array); // different order from first call to Shuffle
* 배열이 길수록 (매우 큰) 순열 수를 동일하게 만들려면 각 스왑마다 많은 반복을 통해 의사 난수 생성기 (PRNG)를 실행하여 충분한 엔트로피를 생성해야합니다. 500 원소 배열의 경우 가능한 500의 아주 작은 부분 만! PRNG를 사용하여 순열을 얻을 수 있습니다. 그럼에도 불구하고 Fisher-Yates 알고리즘은 편견이 없으므로 셔플은 사용하는 RNG만큼 좋습니다.
array.Shuffle(new Random());
.. 와 같은 사용법을 만드는 것이 낫지 않습니까?
new Random()
현재 시스템 시간을 기준으로 시드 값으로 초기화되어 ~ 16ms마다 업데이트되기 때문입니다.
셔플 링 알고리즘을 찾고 있습니까?
이 작업을 수행하는 두 가지 방법이 있습니다. 영리하지만 항상 사람들은 항상 잘못 이해하고 잘못 이해하고 잘못 이해하면 그다지 영리하지 않습니다. 방법, 그리고 바위처럼 멍청하지만 누가 돌봐주는 방식으로 작동합니다.
- 첫 번째 배열의 복제본을 생성하지만 각 문자열에 임의의 숫자로 태그를 지정하십시오.
- 난수를 기준으로 중복 배열을 정렬합니다.
이 알고리즘은 잘 작동하지만 난수 생성기가 두 개의 문자열에 같은 숫자를 태그하지 않도록하십시오. 소위 Birthday Paradox로 인해 예상보다 자주 발생합니다. 시간 복잡도는 O ( n log n )입니다.
이것을 재귀 알고리즘으로 설명하겠습니다.
크기가 n 인 배열을 섞으려면 ([0 .. n -1] 범위에 있음 ) :
만약 N = 0만약 N > 0
- 아무것도하지 마세요
- (재귀 단계) 배열 의 첫 번째 n -1 요소를 섞습니다.
- [0 .. n -1] 범위에서 랜덤 인덱스 x를 선택하십시오 .
- 인덱스 n -1의 요소를 인덱스 x 의 요소와 교체
반복되는 동등 물은 반복자를 배열을 통해 걷는 것으로, 임의의 요소로 바꾸면서 반복자가 가리키는 요소 뒤에는 요소를 바꿀 수 없다는 점에 유의하십시오. 이것은 매우 일반적인 실수이며 편향된 셔플로 이어집니다.
시간 복잡도는 O ( n )입니다.
이 알고리즘은 간단하지만 효율적이지 않습니다. O (N 2 ). 모든 "순서 순"알고리즘은 일반적으로 O (N log N)입니다. 아마도 수십만 요소 이하에서 차이를 만들지 않지만 큰 목록에는 영향을 미칩니다.
var stringlist = ... // add your values to stringlist
var r = new Random();
var res = new List<string>(stringlist.Count);
while (stringlist.Count >0)
{
var i = r.Next(stringlist.Count);
res.Add(stringlist[i]);
stringlist.RemoveAt(i);
}
그것이 O (N 2 ) 인 이유 는 미묘합니다. List.RemoveAt () 는 끝에서 순서대로 제거하지 않는 한 O (N) 연산입니다.
Matt Howells에서 확장 방법을 만들 수도 있습니다. 예.
namespace System
{
public static class MSSystemExtenstions
{
private static Random rng = new Random();
public static void Shuffle<T>(this T[] array)
{
rng = new Random();
int n = array.Length;
while (n > 1)
{
int k = rng.Next(n);
n--;
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
}
}
그런 다음 다음과 같이 사용할 수 있습니다.
string[] names = new string[] {
"Aaron Moline1",
"Aaron Moline2",
"Aaron Moline3",
"Aaron Moline4",
"Aaron Moline5",
"Aaron Moline6",
"Aaron Moline7",
"Aaron Moline8",
"Aaron Moline9",
};
names.Shuffle<string>();
많은 문자열 주위를 이동해야하므로 배열을 무작위로 지정하는 것이 중요합니다. 왜 배열에서 무작위로 읽지 않습니까? 최악의 경우 getNextString ()을 사용하여 래퍼 클래스를 만들 수도 있습니다. 실제로 임의의 배열을 만들어야하는 경우 다음과 같은 작업을 수행 할 수 있습니다
for i = 0 -> i= array.length * 5
swap two strings in random places
* 5는 임의입니다.
내 머리 꼭대기에서 생각하면 다음과 같이 할 수 있습니다.
public string[] Randomize(string[] input)
{
List<string> inputList = input.ToList();
string[] output = new string[input.Length];
Random randomizer = new Random();
int i = 0;
while (inputList.Count > 0)
{
int index = r.Next(inputList.Count);
output[i++] = inputList[index];
inputList.RemoveAt(index);
}
return (output);
}
Jacco, 맞춤형 IComparer를 사용하는 솔루션은 안전하지 않습니다. 정렬 루틴을 제대로 수행하려면 비교기가 여러 요구 사항을 준수해야합니다. 그중 첫 번째는 일관성입니다. 비교기가 동일한 쌍의 객체에서 호출되면 항상 동일한 결과를 반환해야합니다. (비교도 전 이적이어야 함).
이러한 요구 사항을 충족하지 않으면 무한 루프 가능성을 포함하여 정렬 루틴에 많은 문제가 발생할 수 있습니다.
임의의 숫자 값을 각 항목과 연결 한 다음 해당 값을 기준으로 정렬하는 솔루션과 관련하여 두 항목에 동일한 숫자 값이 할당 될 때마다 출력의 임의성이 손상됩니다. "안정한"정렬 루틴에서, 입력의 첫 번째 중 하나가 출력에서 첫 번째입니다. Array.Sort는 안정적이지 않지만 Quicksort 알고리즘에 의해 수행 된 분할에 기반한 편향이 여전히 있습니다.
어느 수준의 무작위성이 필요한지 생각해야합니다. 결정된 공격자로부터 보호하기 위해 암호화 수준의 임의성이 필요한 포커 사이트를 운영하는 경우 노래 재생 목록을 무작위 화하려는 사람과는 매우 다른 요구 사항이 있습니다.
노래 목록 셔플 링의 경우 시드 된 PRNG (System.Random과 같은)를 사용하는 데 아무런 문제가 없습니다. 포커 사이트의 경우 옵션이 아니며 스택 오버 플로우에서 누군가 당신을 위해 할 것보다 훨씬 더 어려운 문제에 대해 생각해야합니다. (암호화 RNG를 사용하는 것은 시작에 불과하며, 알고리즘에 편향이 발생하지 않도록하고, 엔트로피가 충분하고, 후속 임의성을 손상시키는 내부 상태를 노출시키지 않아야합니다.)
이 게시물은 이미 꽤 잘 대답되어 있습니다. Fisher-Yates 셔플의 Durstenfeld 구현을 사용하여 빠르고 편견없는 결과를 얻으십시오. 일부 구현이 실제로 잘못 되었음에도 불구하고 일부 구현이 게시되었습니다.
나는 게시물에 대해 잠시 뒤로 몇 쓴 이 기술을 사용하여 전체 및 부분 셔플을 구현 도 (I 값을 추가 바라고 곳이 두 번째 링크입니다) 등을 구현 편견 여부를 확인하는 방법에 대한 후속 포스트를 , 셔플 알고리즘을 확인하는 데 사용할 수 있습니다. 두 번째 게시물의 끝 부분에서 난수 선택에서 간단한 실수의 영향을 볼 수 있습니다.
좋아, 이것은 분명히 내 편과 충돌하지만 (사죄 ...), 나는 종종 매우 일반적이고 암호로 강력한 방법을 사용합니다.
public static class EnumerableExtensions
{
static readonly RNGCryptoServiceProvider RngCryptoServiceProvider = new RNGCryptoServiceProvider();
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable)
{
var randomIntegerBuffer = new byte[4];
Func<int> rand = () =>
{
RngCryptoServiceProvider.GetBytes(randomIntegerBuffer);
return BitConverter.ToInt32(randomIntegerBuffer, 0);
};
return from item in enumerable
let rec = new {item, rnd = rand()}
orderby rec.rnd
select rec.item;
}
}
Shuffle ()은 IEnumerable의 확장이므로 목록에서 임의의 순서로 0에서 1000까지의 숫자를 얻을 수 있습니다.
Enumerable.Range(0,1000).Shuffle().ToList()
정렬 방법은 정렬 값이 생성되고 시퀀스의 요소 당 정확히 한 번만 기억되므로 정렬과 관련하여 놀라지 않을 것입니다.
이것은 다음에 제공된 예제를 기반으로 한 완전한 콘솔 솔루션입니다 .
class Program
{
static string[] words1 = new string[] { "brown", "jumped", "the", "fox", "quick" };
static void Main()
{
var result = Shuffle(words1);
foreach (var i in result)
{
Console.Write(i + " ");
}
Console.ReadKey();
}
static string[] Shuffle(string[] wordArray) {
Random random = new Random();
for (int i = wordArray.Length - 1; i > 0; i--)
{
int swapIndex = random.Next(i + 1);
string temp = wordArray[i];
wordArray[i] = wordArray[swapIndex];
wordArray[swapIndex] = temp;
}
return wordArray;
}
}
int[] numbers = {0,1,2,3,4,5,6,7,8,9};
List<int> numList = new List<int>();
numList.AddRange(numbers);
Console.WriteLine("Original Order");
for (int i = 0; i < numList.Count; i++)
{
Console.Write(String.Format("{0} ",numList[i]));
}
Random random = new Random();
Console.WriteLine("\n\nRandom Order");
for (int i = 0; i < numList.Capacity; i++)
{
int randomIndex = random.Next(numList.Count);
Console.Write(String.Format("{0} ", numList[randomIndex]));
numList.RemoveAt(randomIndex);
}
Console.ReadLine();
OLINQ를 사용하는 간단한 방법은 다음과 같습니다.
// Input array
List<String> lst = new List<string>();
for (int i = 0; i < 500; i += 1) lst.Add(i.ToString());
// Output array
List<String> lstRandom = new List<string>();
// Randomize
Random rnd = new Random();
lstRandom.AddRange(from s in lst orderby rnd.Next(100) select s);
private ArrayList ShuffleArrayList(ArrayList source)
{
ArrayList sortedList = new ArrayList();
Random generator = new Random();
while (source.Count > 0)
{
int position = generator.Next(source.Count);
sortedList.Add(source[position]);
source.RemoveAt(position);
}
return sortedList;
}
sortedList = source.ToList().OrderBy(x => generator.Next()).ToArray();