Рекомендуется в качестве ответа:
Вот решение, использующее генераторы es2015 :
function* subsetSum(numbers, target, partial = [], partialSum = 0) {
if(partialSum === target) yield partial
if(partialSum >= target) return
for(let i = 0; i < numbers.length; i++){
const remaining = numbers.slice(i + 1)
, n = numbers[i]
yield* subsetSum(remaining, target, [...partial, n], partialSum + n)
}
}
Использование генераторов действительно может быть очень полезным, поскольку оно позволяет вам чтобы приостановить выполнение скрипта сразу после нахождения действительного подмножества. Это контрастирует с решениями без генераторов (т. Е. С отсутствием состояния), которым приходится проходить через каждое подмножество numbers
Перемешать любой (I)List
с использованием метода расширения, основанного на Shisherle Fisher-Yates :
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Использование:
List<Product> products = GetProducts();
products.Shuffle();
код выше использует сильно критикуемый метод System.Random для выбора кандидатов подкачки. Это быстро, но не так произвольно, как должно быть. Если вам нужно лучшее качество случайности в ваших тасованиях, используйте генератор случайных чисел в System.Security.Cryptography следующим образом:
using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
int n = list.Count;
while (n > 1)
{
byte[] box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (Byte.MaxValue / n)));
int k = (box[0] % n);
n--;
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
В этом блоге доступно простое сравнение (WayBack Machine).
Редактирование: поскольку я пишу этот ответ пару лет назад, многие люди комментировали или писали мне, чтобы указать на большой глупый недостаток в моем сравнении. Они, конечно, правы. Нет ничего плохого в System.Random, если он используется так, как он был предназначен. В моем первом примере выше я создаю экземпляр rng-переменной внутри метода Shuffle, который запрашивает проблемы, если метод будет вызываться повторно. Ниже приведен фиксированный полный пример, основанный на действительно полезном комментарии, полученном сегодня от @weston здесь, на SO.
Program.cs:
using System;
using System.Collections.Generic;
using System.Threading;
namespace SimpleLottery
{
class Program
{
private static void Main(string[] args)
{
var numbers = new List<int>(Enumerable.Range(1, 75));
numbers.Shuffle();
Console.WriteLine("The winning numbers are: {0}", string.Join(", ", numbers.GetRange(0, 5)));
}
}
public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;
public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}
static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}
public static List<T> Randomize<T>(List<T> list)
{
List<T> randomizedList = new List<T>();
Random rnd = new Random();
while (list.Count > 0)
{
int index = rnd.Next(0, list.Count); //pick a random item from the master list
randomizedList.Add(list[index]); //place it at the end of the randomized list
list.RemoveAt(index);
}
return randomizedList;
}
var listCopy = list.ToList()
, чтобы не удалять все элементы из входящего списка? Я действительно не понимаю, почему вы хотите, чтобы эти списки были пустыми.
– Chris Marisic
17 September 2014 в 18:38
Я обычно использую:
var list = new List<T> ();
fillList (list);
var randomizedList = new List<T> ();
var rnd = new Random ();
while (list.Count != 0)
{
var index = rnd.Next (0, list.Count);
randomizedList.Add (list [index]);
list.RemoveAt (index);
}
Очень простой подход к этой проблеме состоит в том, чтобы использовать ряд случайных свопов в списке.
В псевдокоде это будет выглядеть так:
do
r1 = randomPositionInList()
r2 = randomPositionInList()
swap elements at index r1 and index r2
for a certain number of times
Идея получает анонимный объект с элементом и случайным порядком, а затем переупорядочивает элементы этим порядком и возвращает значение:
var result = items.Select(x => new { value = x, order = rnd.Next() })
.OrderBy(x => x.order).Select(x => x.value).ToList()
Вот эффективный Shuffler, который возвращает массив байтов перетасованных значений. Он никогда не перемешивает больше, чем нужно. Он может быть перезапущен с того места, где он ранее был остановлен. Моя фактическая реализация (не показана) является компонентом MEF, который позволяет пользователю задавать замененный shuffler.
public byte[] Shuffle(byte[] array, int start, int count)
{
int n = array.Length - start;
byte[] shuffled = new byte[count];
for(int i = 0; i < count; i++, start++)
{
int k = UniformRandomGenerator.Next(n--) + start;
shuffled[i] = array[k];
array[k] = array[start];
array[start] = shuffled[i];
}
return shuffled;
}
`
Это безопасный для потоков способ:
public static class EnumerableExtension
{
private static Random globalRng = new Random();
[ThreadStatic]
private static Random _rng;
private static Random rng
{
get
{
if (_rng == null)
{
int seed;
lock (globalRng)
{
seed = globalRng.Next();
}
_rng = new Random(seed);
}
return _rng;
}
}
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
return items.OrderBy (i => rng.Next());
}
}
EDIT RemoveAt
- слабость в моей предыдущей версии. Это решение преодолевает это.
public static IEnumerable<T> Shuffle<T>(
this IEnumerable<T> source,
Random generator = null)
{
if (generator == null)
{
generator = new Random();
}
var elements = source.ToArray();
for (var i = elements.Length - 1; i >= 0; i--)
{
var swapIndex = generator.Next(i + 1);
yield return elements[swapIndex];
elements[swapIndex] = elements[i];
}
}
Обратите внимание на необязательный параметр Random generator
, если реализация базовой фреймворка Random
не является потокобезопасной или криптографически достаточно сильной для ваших нужд, вы можете ввести свою реализацию в операция.
Вот идея, расширить IList в (надеюсь) эффективный путь.
public static IEnumerable<T> Shuffle<T>(this IList<T> list)
{
var choices = Enumerable.Range(0, list.Count).ToList();
var rng = new Random();
for(int n = choices.Count; n > 1; n--)
{
int k = rng.Next(n);
yield return list[choices[k]];
choices.RemoveAt(k);
}
yield return list[choices[0]];
}
Если у вас есть фиксированное число (75), вы можете создать массив из 75 элементов, а затем перечислить список, перемещая элементы в рандомизированные позиции в массиве. Вы можете сгенерировать сопоставление номера списка с индексом массива с помощью Fisher-Yates shuffle .
Простая модификация принятого ответа , который возвращает новый список вместо работы на месте и принимает более общий IEnumerable<T>
, как это делают многие другие методы Linq.
private static Random rng = new Random();
/// <summary>
/// Returns a new list where the elements are randomly shuffled.
/// Based on the Fisher-Yates shuffle, which has O(n) complexity.
/// </summary>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) {
var source = list.ToList();
int n = source.Count;
var shuffled = new List<T>(n);
shuffled.AddRange(source);
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = shuffled[k];
shuffled[k] = shuffled[n];
shuffled[n] = value;
}
return shuffled;
}
Это мой предпочтительный метод перетасовки, когда желательно не изменять оригинал. Это вариант алгоритма Fisher-Yates «наизнанку» , который работает над любой перечислимой последовательностью (длина source
не обязательно должна быть известна с начала).
public static IList<T> NextList<T>(this Random r, IEnumerable<T> source)
{
var list = new List<T>();
foreach (var item in source)
{
var i = r.Next(list.Count + 1);
if (i == list.Count)
{
list.Add(item);
}
else
{
var temp = list[i];
list[i] = item;
list.Add(temp);
}
}
return list;
}
Этот алгоритм также может быть реализован путем выделения диапазона от 0
до length - 1
и случайного исчерпания индексов путем замены случайно выбранного индекса последним индексом до тех пор, пока все индексы не будут выбраны ровно один раз. Этот выше код выполняет то же самое, но без дополнительного распределения. Это довольно опрятно.
Что касается класса Random
, это генератор чисел общего назначения (и если бы я запускал лотерею, я бы подумал об использовании чего-то другого). По умолчанию он также полагается на начальное значение по времени. Небольшое облегчение проблемы состоит в том, чтобы засеять класс Random
с помощью RNGCryptoServiceProvider
, или вы можете использовать RNGCryptoServiceProvider
в методе, подобном этому (см. Ниже), чтобы генерировать равномерно выбранные случайные значения двойной с плавающей запятой, но запускать лотерею в значительной степени требует понимания случайности и характера источника случайности.
var bytes = new byte[8];
_secureRng.GetBytes(bytes);
var v = BitConverter.ToUInt64(bytes, 0);
return (double)v / ((double)ulong.MaxValue + 1);
Точка генерации случайного двойника (между 0 и 1 исключительно) заключается в использовании масштабирования для целочисленного решения. Если вам нужно выбрать что-то из списка на основе случайного двойного x
, который всегда будет 0 <= x && x < 1
, это прямо.
return list[(int)(x * list.Count)];
Наслаждайтесь!
Если вы не возражаете использовать два Lists
, то это, вероятно, самый простой способ сделать это, но, вероятно, не самый эффективный или непредсказуемый:
List<int> xList = new List<int>() { 1, 2, 3, 4, 5 };
List<int> deck = new List<int>();
foreach (int xInt in xList)
deck.Insert(random.Next(0, deck.Count + 1), xInt);
Если нам нужно только перетасовать элементы в полностью случайном порядке (просто для смешивания элементов в списке), я предпочитаю этот простой, но эффективный код, который заказывает элементы по guid ...
var shuffledcards = cards.OrderBy(a => Guid.NewGuid()).ToList();
var shuffledcards = cards.OrderBy(a => rng.Next());
compilr.com/grenade/sandbox/Program.cs
– grenade
27 May 2013 в 11:54
NewGuid
гарантирует, что он дает уникальный идентификатор GUID. Он не дает никаких гарантий относительно случайности. Если вы используете GUID для целей, отличных от создания значения unique i>, вы делаете это неправильно.
– Eric Lippert
13 September 2013 в 22:31
Метод расширения для IEnumerable:
public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
Random rnd = new Random();
return source.OrderBy<T, int>((item) => rnd.Next());
}
OrderBy
использует вариант QuickSort для сортировки элементов по их (якобы случайным) ключам. Производительность QuickSort - O (N log N) i>; напротив, перетасовка Фишера-Йейта O (N) i>. Для коллекции из 75 элементов это не может быть большой проблемой, но разница станет заметной для больших коллекций.
– John Beyer
26 June 2013 в 17:47
Random.Next()
может привести к обоснованному псевдослучайному распределению значений, но not i> гарантирует, что значения будут уникальными. Вероятность дублирования ключей растет (нелинейно) с N i> до тех пор, пока не достигнет определенности, когда N i> достигнет 2 ^ 32 + 1. OrderBy
QuickSort является стабильной i> сортировкой; таким образом, если нескольким элементам присваивается одно и то же псевдослучайное значение индекса, то их порядок в выходной последовательности будет такой же i>, что и во входной последовательности; таким образом, смещение вводится в «перетасовку».
– John Beyer
26 June 2013 в 18:06
Старый пост наверняка, но я просто использую GUID.
Items = Items.OrderBy(o => Guid.NewGuid().ToString()).ToList();
GUID всегда уникален, и поскольку он регенерируется каждый раз, когда результат изменяется каждый раз.
Вы можете достичь этого, используя этот простой метод расширения
public static class IEnumerableExtensions
{
public static IEnumerable<t> Randomize<t>(this IEnumerable<t> target)
{
Random r = new Random();
return target.OrderBy(x=>(r.Next()));
}
}
, и вы можете использовать его, выполнив следующие
// use this on any collection that implements IEnumerable!
// List, Array, HashSet, Collection, etc
List<string> myList = new List<string> { "hello", "random", "world", "foo", "bar", "bat", "baz" };
foreach (string s in myList.Randomize())
{
Console.WriteLine(s);
}
Random
вне функции в качестве переменной static
. В противном случае вы можете получить такое же рандомизированное семя из таймера, если оно вызвано быстро.
– Lemonseed
2 June 2016 в 16:11
Я немного удивлен всеми неуклюжими версиями этого простого алгоритма. Fisher-Yates (или Knuth shuffle) немного сложный, но очень компактный. Если вы перейдете в Википедию, вы увидите версию этого алгоритма, которая имеет обратную связь, и многие люди действительно не понимают, почему это происходит наоборот. Основная причина заключается в том, что в этой версии алгоритма предполагается, что генератор случайных чисел Random(n)
в вашем распоряжении имеет следующие два свойства:
Однако генератор случайных чисел .Net не удовлетворяет свойству # 2. Random.Next(n)
вместо этого возвращает число от 0 до n-1 включительно. Если вы попытаетесь использовать for-loop в обратном порядке, вам нужно будет вызвать Random.Next(n+1)
, который добавит еще одну операцию.
Однако генератор случайных чисел .Net имеет другую приятную функцию Random.Next(a,b)
, которая возвращает a в b-1 включительно. Это действительно прекрасно сочетается с реализацией этого алгоритма, который имеет нормальный цикл for. Итак, без дальнейших церемоний, вот правильная, эффективная и компактная реализация:
public static void Shuffle<T>(this IList<T> list, Random rnd)
{
for(var i=0; i < list.Count; i++)
list.Swap(i, rnd.Next(i, list.Count));
}
public static void Swap<T>(this IList<T> list, int i, int j)
{
var temp = list[i];
list[i] = list[j];
list[j] = temp;
}
i = list.Count - 1
, т. Е. Последняя итерация, rnd.Next(i, list.Count)
вернет вас. Поэтому вам необходимо i < list.Count -1
в качестве условия цикла. Ну, вам это не нужно, но это экономит 1 итерацию;)
– Pod
28 May 2016 в 20:14
public Deck(IEnumerable<Card> initialCards)
{
cards = new List<Card>(initialCards);
public void Shuffle()
}
{
List<Card> NewCards = new List<Card>();
while (cards.Count > 0)
{
int CardToMove = random.Next(cards.Count);
NewCards.Add(cards[CardToMove]);
cards.RemoveAt(CardToMove);
}
cards = NewCards;
}
public IEnumerable<string> GetCardNames()
{
string[] CardNames = new string[cards.Count];
for (int i = 0; i < cards.Count; i++)
CardNames[i] = cards[i].Name;
return CardNames;
}
Deck deck1;
Deck deck2;
Random random = new Random();
public Form1()
{
InitializeComponent();
ResetDeck(1);
ResetDeck(2);
RedrawDeck(1);
RedrawDeck(2);
}
private void ResetDeck(int deckNumber)
{
if (deckNumber == 1)
{
int numberOfCards = random.Next(1, 11);
deck1 = new Deck(new Card[] { });
for (int i = 0; i < numberOfCards; i++)
deck1.Add(new Card((Suits)random.Next(4),(Values)random.Next(1, 14)));
deck1.Sort();
}
else
deck2 = new Deck();
}
private void reset1_Click(object sender, EventArgs e) {
ResetDeck(1);
RedrawDeck(1);
}
private void shuffle1_Click(object sender, EventArgs e)
{
deck1.Shuffle();
RedrawDeck(1);
}
private void moveToDeck1_Click(object sender, EventArgs e)
{
if (listBox2.SelectedIndex >= 0)
if (deck2.Count > 0) {
deck1.Add(deck2.Deal(listBox2.SelectedIndex));
}
RedrawDeck(1);
RedrawDeck(2);
}
Random rng = new Random();
astatic
решит проблему в столбце сравнения. Поскольку каждый последующий вызов будет следовать из предыдущих вызовов, последний случайный результат. – weston 28 November 2012 в 15:58