Faster way of truncating 0s from long in lieu of % 10 - c#

I'm looking to "truncate" 0's from long values.
For example, for long value "1234560000", I'd like to drop the last four 0's (to 123456) and also need to know how many 0's have been dropped.
I can achieve this with a % 10 operations:
void Main()
{
Console.WriteLine(Truncate(1234560000));
}
public static long Truncate(long mantissa)
{
int droppedZeros = 0;
while (mantissa % 10 == 0)
{
mantissa /= 10;
droppedZeros++;
}
return mantissa;
}
This piece of code getting is getting called millions of times and is performance critical and I'm looking for ways to improve performance to achieve the same without modulo (can this be done with bit shifts?).
Per request, I added some benchmarks numbers including a benchmark that performs division with compile time known constant to showcase the relative overhead of the modulo operation:
Method | Mean | Error | StdDev | Median | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
---------------- |----------:|----------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
DivideNoModulo | 1.863 ms | 0.0431 ms | 0.1272 ms | 1.855 ms | - | - | - | - |
ModuloBasic | 21.342 ms | 0.8776 ms | 2.5876 ms | 20.813 ms | - | - | - | - |
DivisionBasic | 18.159 ms | 1.7218 ms | 5.0768 ms | 15.937 ms | - | - | - | - |
DivisionSmarter | 7.510 ms | 0.5307 ms | 1.5649 ms | 7.201 ms | - | - | - | - |
ModuloSmarter | 8.517 ms | 0.1673 ms | 0.2886 ms | 8.531 ms | - | - | - | - |
StringTruncate | 42.370 ms | 1.7358 ms | 5.1181 ms | 40.566 ms | 1000.0000 | - | - | 8806456 B |
Benchmark code:
[SimpleJob(RunStrategy.ColdStart, 1)]
[MemoryDiagnoser]
public class EncoderBenchmark
{
private long _mantissa;
[Benchmark]
public void DivideNoModulo()
{
for (var i = 0; i < 100000; i++)
{
_mantissa = 12345600000000;
_mantissa /= 100000000;
}
}
[Benchmark]
public void ModuloBasic()
{
for (var i = 0; i < 100000; i++)
{
_mantissa = 12345600000000;
while (_mantissa % 10 == 0)
{
_mantissa /= 10;
}
}
}
[Benchmark]
public void DivisionBasic()
{
for (var i = 0; i < 100000; i++)
{
_mantissa = 12345600000000;
for (;;)
{
long temp = _mantissa / 10;
if (temp * 10 != _mantissa)
break;
_mantissa = temp;
}
}
}
[Benchmark]
public void DivisionSmarter()
{
for (var i = 0; i < 100000; i++)
{
_mantissa = 12345600000000;
for (; ; )
{
long temp = _mantissa / 1000000;
if (temp * 1000000 != _mantissa)
break;
_mantissa = temp;
}
for (; ; )
{
long temp = _mantissa / 10;
if (temp * 10 != _mantissa)
break;
_mantissa = temp;
}
}
}
[Benchmark]
public void ModuloSmarter()
{
for (var i = 0; i < 100000; i++)
{
_mantissa = 12345600000000;
while (_mantissa % 1000000 == 0)
{
_mantissa /= 1000000;
}
while (_mantissa % 10 == 0)
{
_mantissa /= 10;
}
}
}
[Benchmark]
public void StringTruncate()
{
for (var i = 0; i < 100000; i++)
{
_mantissa = 12345600000000;
_mantissa = long.Parse(_mantissa.ToString().TrimEnd('0'));
}
}
}

You'll be very unlikely to get it working efficiently with bit shifting since that's ideal for dividing by powers of two, of which 10 is not one.
A possibility for improvement, where you may often get numbers with lots of trailing zeros, is to use multiple loops to do the work in bigger chunks, such as:
if (mantissa != 0) {
while (mantissa % 1000000 == 0) {
mantissa /= 1000000;
droppedZeros += 6;
}
while (mantissa % 1000 == 0) {
mantissa /= 1000;
droppedZeros += 3;
}
while (mantissa % 10 == 0) {
mantissa /= 10;
droppedZeros++;
}
}
This will usually result in fewer instructions being performed but, as with all optimisations, measure, don't guess! It may be that the added code complexity is not worth the gains you get (if any).
Note that I've also caught the mantissa == 0 case since that will result in an infinite loop in your original code.
Another possibility you may want to consider is if you're doing this operation to the same item multiple times. For example, let's say you have a collection of integers and, each time you need to process one of them, you have to strip off and count the trailing zeros.
In that case, you could actually store them in a different way. For example, consider the (pseudo-code) structure:
struct item:
int originalItem
int itemWithoutZeroes
int zeroCount
Basically, whenever you first receive an item (such as 1234560000), you immediately convert it to the structure once and once only:
{ 1234560000, 123456, 4 }
This provides a cached version of the zero-stripped item so you never have to calculate it again.
So, if you want the stripped mantissa, you just use item.itemWithoutZeros. If you want to output the number in its original form, you can use item.originalItem. And, if you want the count of zeros, use item.zeroCount.
Obviously, this will take up more storage space but you'll often find that optimisation is a time/space trade-off.

A bit faster replace '%' with '*'
public static long T(long mantissa)
{
if (mantissa == 0)
return 0;
int droppedZeros = 0;
for (; ; )
{
long temp = mantissa / 1000000;
if (temp * 1000000 != mantissa)
break;
mantissa = temp;
droppedZeros += 6;
}
for (; ; )
{
long temp = mantissa / 1000;
if (temp * 1000 != mantissa)
break;
mantissa = temp;
droppedZeros += 3;
}
for (; ; )
{
long temp = mantissa / 10;
if (temp * 10 != mantissa)
break;
mantissa = temp;
droppedZeros++;
}
return mantissa;
}

Update your Truncate logic as below.
public static long Truncate(long mantissa)
{
if (mantissa == 0)
return 0;
var mantissaStr = mantissa.ToString();
var mantissaTruncated = mantissaStr.TrimEnd('0');
int droppedZeros = mantissaStr.Length - mantissaTruncated.Length;
return Convert.ToInt64(mantissaTruncated);
}

Related

how is this algorithm counting all the unique integers?

below I have an algorithm that counts all the unique integers in an ordered array, but I am not sure how it is doing it using binary search? is anyone able to explain how? thanks.
int unique(int[] a) {
int i = 0;
int count = 0;
while (i < a.length) {
i = nextIndex(a, i, a[i]);
count++;
}
return count;
}
int nextIndex(int[] a, int l, int target) {
int r = a.length - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (a[mid] == target) l = mid + 1;
else r = mid - 1;
}
return r + 1;
}
JonasH has posted the actual answer about how this works.
But I was interested to see how much faster (if at all) a binary search would be compared to using Distinct() or a linear search.
Here's the code I used to benchmark it:
using System;
using System.Linq;
using BenchmarkDotNet.Attributes;
namespace ConsoleApp1;
public class UnderTest
{
public UnderTest()
{
const int NUM_VALUES = 1_000_000;
const double PROBABLILITY_OF_CHANGE = 0.00001;
_data = new int[NUM_VALUES];
var rng = new Random(98891); // Fixed seed.
for (int value = 0, i = 0; i < NUM_VALUES; ++i)
{
_data[i] = value;
if (rng.NextDouble() <= PROBABLILITY_OF_CHANGE)
++value;
}
// Print out to prove they all return the same value.
Console.WriteLine(usingBinarySearch(_data));
Console.WriteLine(usingDistinct (_data));
Console.WriteLine(usingLinearSearch(_data));
}
[Benchmark]
public void UsingBinarySearch()
{
usingBinarySearch(_data);
}
[Benchmark]
public void UsingDistinct()
{
usingDistinct(_data);
}
[Benchmark]
public void UsingLinearSearch()
{
usingLinearSearch(_data);
}
static int usingBinarySearch(int[] a)
{
int i = 0;
int count = 0;
while (i < a.Length)
{
i = nextIndex(a, i, a[i]);
count++;
}
return count;
}
static int nextIndex(int[] a, int l, int target)
{
int r = a.Length - 1;
while (l <= r)
{
int mid = l + (r - l) / 2;
if (a[mid] == target) l = mid + 1;
else r = mid - 1;
}
return r + 1;
}
static int usingDistinct(int[] a)
{
return a.Distinct().Count();
}
static int usingLinearSearch(int[] a)
{
int count = 1;
for (int i = 1; i < a.Length; i++)
{
count += a[i - 1] != a[i] ? 1 : 0;
}
return count;
}
readonly int[] _data;
}
For the first test run, I gave it some data where I'd expect the binary search to be significantly faster: An array of 1M ints where the probability of each element's value being larger than the previous was 0.00001 (PROBABLILITY_OF_CHANGE = 0.00001).
Using a random number generator with a fixed seed of 98891 this resulted in there only being 7 distinct values in the array, yielding the following timing results (using .NET 6.0):
| Method | Mean | Error | StdDev |
|------------------ |---------------:|--------------:|--------------:|
| UsingBinarySearch | 251.0 ns | 4.93 ns | 12.64 ns |
| UsingDistinct | 9,341,067.8 ns | 185,888.76 ns | 294,839.27 ns |
| UsingLinearSearch | 1,607,222.2 ns | 51,565.50 ns | 146,282.75 ns |
As you might expect, the binary search is way faster for this case. Of note is that Distinct() is very slow compared to the linear search.
This isn't really a fair comparison because Distinct() will work with unsorted data while the other two algorithms require sorted data, so bear that in mind. If you have unsorted data then the overhead of sorting it for the other algorithms will make Distinct() a better choice. I leave such comparisons as the proverbial exercise for the reader...
Now let's try with PROBABLILITY_OF_CHANGE = 0.001 (resulting in 919 distinct elements):
| Method | Mean | Error | StdDev |
|------------------ |------------:|-----------:|-----------:|
| UsingBinarySearch | 93.08 us | 1.787 us | 4.831 us |
| UsingDistinct | 9,944.12 us | 197.825 us | 356.718 us |
| UsingLinearSearch | 1,503.85 us | 28.239 us | 63.740 us |
Binary search is still significantly faster.
Now with PROBABLILITY_OF_CHANGE = 0.1 (resulting in 100,058 distinct elements):
| Method | Mean | Error | StdDev |
|------------------ |----------:|----------:|----------:|
| UsingBinarySearch | 5.541 ms | 0.1096 ms | 0.1347 ms |
| UsingDistinct | 14.331 ms | 0.5516 ms | 1.6091 ms |
| UsingLinearSearch | 2.319 ms | 0.0422 ms | 0.0782 ms |
Now linear search is faster than binary search.
This goes to show that Distinct() is not a good way to solve this - a linear search is always better (primarily because the data is already sorted - if it wasn't then we'd have to sort it which would change the timings significantly for the other algorithms).
And using a binary search is only worth it if the number of distinct values is relatively low compared to the size of the container.
(Do note the different units output by Benchmark.Net for these runs - ns, us and ms. I realised afterwards that I forgot to specify the units so they were automatically scaled for the fastest benchmark in the run...)
The nextIndex method is essentially a binary search method that searches for the last occurrence of the target value, and then returns the index one larger than this. So the outer loop will iterate and increase the count the same number of times that there are unique values.
Note that I would only use such a complicated method if profiling showed the need for it. The standard method would be myValues.Distinct().Count(), that should be faster for unordered lists, and work for types other than int arrays.
Successive binary searches should have a complexity of O(m log n) where n is the total amount of items, and m the number of unique values. This suggest a linear search should be faster if you have less then log n duplicated values on average. Such a linear search could look something like :
int count = 1;
for(int i = 1; i < a.Count; i++){
count += a[i-1] != a[i] ? 1 : 0;
}
Also keep in mind that there are constant factors at play that can affect the result. As an example, random memory accesses are more expensive than linear access, since it makes caching more difficult. So in some cases a simpler algorithm that matches the hardware better is preferred over a more complex one, and some implementations switch strategies depending on the data set.

C# Different probabilities in if statements

I am writing a program where I put different students in different classrooms. I'm having some trouble figuring out how to use probabilities in C#, and programming in general. I want it to be a 5% probability to become a student in Alpha, 10& for Omega & 60% for Nightwalkers. I don't understand what numbers to put in.My method right now:
public string AssignClassroom()
{
int rand = random.Next(0, 100);
{
if (rand < 5) // or >95%?
{
student.Classroom = "Alpha";
}
if (rand < 10) // or >90?
{
student.Classroom = "Omega";
}
if (rand < 60) // or >40?
{
student.Classroom = "Nightwalkers";
}
}
}
You should add up figures in ifs:
if (rand < 5) {
student.Classroom = "Alpha";
}
else if (rand < 10 + 5)
{
student.Classroom = "Omega";
}
else if (rand < 60 + 10 + 5)
{
student.Classroom = "Nightwalkers";
}
Note, that 5, 10, 60 are differencies:
0 .. 5 .. 15 .. 75
| 5 | | | -> 5% top students go to the 1st class
| 10 | | -> 10% next go to the 2nd
| 60 | -> 60% goes to the 3d

Parse IDs from input string to long number using Span<T> in .Net

I'm trying to parse different IDs(long) from a single string which contains different number, and I need to minimize memory allocation for the performance.
Below is the code extracting IDs using Split, but I found that I could use AsSpan and Splice to do the same without allocating memory. But unfortunately I'm not so familiar with this Span concept even after looking up on the web. Can anyone please chime in how I can achieve this?
As you can see below, input string has 3 different IDs but I only need 2 of them and parse into long type.
string[] machineIdPart;
string[] employeeIdPart;
long machineId;
long employeeId;
//Input String
var description = "machineId: 276744, engineId: 59440, employeeId: 4619825";
Console.Out.Write(description);
Console.Out.WriteLine();
var infoList = description.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var info in infoList)
{
if (info.TrimStart().StartsWith("machineId", StringComparison.OrdinalIgnoreCase))
{
machineIdPart = info.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (machineIdPart.Count() > 1)
{
long.TryParse(machineIdPart[1].Trim(), out machineId);
}
}
if (info.TrimStart().StartsWith("employeeId", StringComparison.OrdinalIgnoreCase))
{
employeeIdPart = info.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (employeeIdPart.Count() > 1)
{
long.TryParse(employeeIdPart[1].Trim(), out employeeId);
}
}
}
I want to modify this code to minimize memory allocation as this method will be run extremely frequently.
This solution works with .NET Core 2.2. It utilizes allocation-free extension method on ReadOnlySpan<char> (SplitNext).
public class Program {
public void MyAnswer() {
long machineId = 0;
long employeeId = 0;
var description = "machineId: 276744, engineId: 59440, employeeId: 4619825";
var span = description.AsSpan();
while (span.Length > 0) {
var entry = span.SplitNext(',');
var key = entry.SplitNext(':').TrimStart(' ');
var value = entry.TrimStart(' ');
if (key.Equals("machineId", StringComparison.Ordinal)) {
long.TryParse(value, out machineId);
}
if (key.Equals("employeeId", StringComparison.Ordinal)) {
long.TryParse(value, out employeeId);
}
}
}
}
public static class Extensions {
public static ReadOnlySpan<char> SplitNext(this ref ReadOnlySpan<char> span, char seperator) {
int pos = span.IndexOf(seperator);
if (pos > -1) {
var part = span.Slice(0, pos);
span = span.Slice(pos + 1);
return part;
} else {
var part = span;
span = span.Slice(span.Length);
return part;
}
}
}
I compared your original code, the existing answer and my answer via BenchmarkDotnet. It shows that this solution is indeed allocation-free and performs faster than the original version:
BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.316 (1809/October2018Update/Redstone5)
Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), 1 CPU, 4 logical and 4 physical cores
.NET Core SDK=2.2.104
[Host] : .NET Core 2.2.2 (CoreCLR 4.6.27317.07, CoreFX 4.6.27318.02), 64bit RyuJIT
DefaultJob : .NET Core 2.2.2 (CoreCLR 4.6.27317.07, CoreFX 4.6.27318.02), 64bit RyuJIT
| Method | Mean | Error | StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
|--------- |-----------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
| Original | 1,164.1 ns | 11.606 ns | 10.289 ns | 0.2937 | - | - | 928 B |
| Answer | 460.5 ns | 4.527 ns | 4.234 ns | - | - | - | - |
| MyAnswer | 445.7 ns | 2.578 ns | 2.412 ns | - | - | - | - |
Apart from the string-handling optimizations, the actual parsing function can also be optimized. This would be a faster long-parser:
public static long LongParseFast(ReadOnlySpan<char> value) {
long result = 0;
for (int i = 0; i < value.Length; i++) {
result = 10 * result + (value[i] - 48);
}
return result;
}
If used in my sample, it doubles performance to 216.0 ns in my benchmarks. Of course this function cannot deal with things like negative numbers, commas, points and other locale stuff. But if you're ok with that, this is probably as fast as you can get.
The solution will become little more complicated than current, but there will be no more string allocations. Works for .NET Core 2.2
long machineId = 0;
long employeeId = 0;
var description = "machineId: 276744, engineId: 59440, employeeId: 4619825";
ReadOnlySpan<char> descriptionSpan = description.AsSpan();
var nameValueBlockStartIndex = 0;
while(nameValueBlockStartIndex < description.Length)
{
var blockEndIndex = description.IndexOf(',', nameValueBlockStartIndex);
if (blockEndIndex == -1)
{
blockEndIndex = description.Length;
}
var namePartEndIndex = description.IndexOf(':', nameValueBlockStartIndex);
var namePartLength = namePartEndIndex - nameValueBlockStartIndex;
var namePart = descriptionSpan.Slice(nameValueBlockStartIndex, namePartLength);
var valuePartStartIndex = namePartEndIndex + 1;
var valuePartLength = blockEndIndex - valuePartStartIndex + 1;
var valuePart = descriptionSpan.Slice(valuePartStartIndex, valuePartLength - 1);
while(namePart[0] == ' ')
{
namePart = namePart.Slice(1);
}
if (namePart.Equals("machineId", StringComparison.OrdinalIgnoreCase))
{
Int64.TryParse(valuePart, out machineId);
}
else if (namePart.Equals("employeeId", StringComparison.OrdinalIgnoreCase))
{
Int64.TryParse(valuePart, out employeeId);
}
nameValueBlockStartIndex = blockEndIndex + 1;
}

Unity: getting a maximum of 10 out of 20 questions quiz game Multiple Choice [duplicate]

I need a quick algorithm to select 5 random elements from a generic list. For example, I'd like to get 5 random elements from a List<string>.
Using linq:
YourList.OrderBy(x => rnd.Next()).Take(5)
Iterate through and for each element make the probability of selection = (number needed)/(number left)
So if you had 40 items, the first would have a 5/40 chance of being selected. If it is, the next has a 4/39 chance, otherwise it has a 5/39 chance. By the time you get to the end you will have your 5 items, and often you'll have all of them before that.
This technique is called selection sampling, a special case of Reservoir Sampling. It's similar in performance to shuffling the input, but of course allows the sample to be generated without modifying the original data.
public static List<T> GetRandomElements<T>(this IEnumerable<T> list, int elementsCount)
{
return list.OrderBy(arg => Guid.NewGuid()).Take(elementsCount).ToList();
}
This is actually a harder problem than it sounds like, mainly because many mathematically-correct solutions will fail to actually allow you to hit all the possibilities (more on this below).
First, here are some easy-to-implement, correct-if-you-have-a-truly-random-number generator:
(0) Kyle's answer, which is O(n).
(1) Generate a list of n pairs [(0, rand), (1, rand), (2, rand), ...], sort them by the second coordinate, and use the first k (for you, k=5) indices to get your random subset. I think this is easy to implement, although it is O(n log n) time.
(2) Init an empty list s = [] that will grow to be the indices of k random elements. Choose a number r in {0, 1, 2, ..., n-1} at random, r = rand % n, and add this to s. Next take r = rand % (n-1) and stick in s; add to r the # elements less than it in s to avoid collisions. Next take r = rand % (n-2), and do the same thing, etc. until you have k distinct elements in s. This has worst-case running time O(k^2). So for k << n, this can be faster. If you keep s sorted and track which contiguous intervals it has, you can implement it in O(k log k), but it's more work.
#Kyle - you're right, on second thought I agree with your answer. I hastily read it at first, and mistakenly thought you were indicating to sequentially choose each element with fixed probability k/n, which would have been wrong - but your adaptive approach appears correct to me. Sorry about that.
Ok, and now for the kicker: asymptotically (for fixed k, n growing), there are n^k/k! choices of k element subset out of n elements [this is an approximation of (n choose k)]. If n is large, and k is not very small, then these numbers are huge. The best cycle length you can hope for in any standard 32 bit random number generator is 2^32 = 256^4. So if we have a list of 1000 elements, and we want to choose 5 at random, there's no way a standard random number generator will hit all the possibilities. However, as long as you're ok with a choice that works fine for smaller sets, and always "looks" random, then these algorithms should be ok.
Addendum: After writing this, I realized that it's tricky to implement idea (2) correctly, so I wanted to clarify this answer. To get O(k log k) time, you need an array-like structure that supports O(log m) searches and inserts - a balanced binary tree can do this. Using such a structure to build up an array called s, here is some pseudopython:
# Returns a container s with k distinct random numbers from {0, 1, ..., n-1}
def ChooseRandomSubset(n, k):
for i in range(k):
r = UniformRandom(0, n-i) # May be 0, must be < n-i
q = s.FirstIndexSuchThat( s[q] - q > r ) # This is the search.
s.InsertInOrder(q ? r + q : r + len(s)) # Inserts right before q.
return s
I suggest running through a few sample cases to see how this efficiently implements the above English explanation.
I think the selected answer is correct and pretty sweet. I implemented it differently though, as I also wanted the result in random order.
static IEnumerable<SomeType> PickSomeInRandomOrder<SomeType>(
IEnumerable<SomeType> someTypes,
int maxCount)
{
Random random = new Random(DateTime.Now.Millisecond);
Dictionary<double, SomeType> randomSortTable = new Dictionary<double,SomeType>();
foreach(SomeType someType in someTypes)
randomSortTable[random.NextDouble()] = someType;
return randomSortTable.OrderBy(KVP => KVP.Key).Take(maxCount).Select(KVP => KVP.Value);
}
I just ran into this problem, and some more google searching brought me to the problem of randomly shuffling a list: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
To completely randomly shuffle your list (in place) you do this:
To shuffle an array a of n elements (indices 0..n-1):
for i from n − 1 downto 1 do
j ← random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]
If you only need the first 5 elements, then instead of running i all the way from n-1 to 1, you only need to run it to n-5 (ie: n-5)
Lets say you need k items,
This becomes:
for (i = n − 1; i >= n-k; i--)
{
j = random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]
}
Each item that is selected is swapped toward the end of the array, so the k elements selected are the last k elements of the array.
This takes time O(k), where k is the number of randomly selected elements you need.
Further, if you don't want to modify your initial list, you can write down all your swaps in a temporary list, reverse that list, and apply them again, thus performing the inverse set of swaps and returning you your initial list without changing the O(k) running time.
Finally, for the real stickler, if (n == k), you should stop at 1, not n-k, as the randomly chosen integer will always be 0.
You can use this but the ordering will happen on client side
.AsEnumerable().OrderBy(n => Guid.NewGuid()).Take(5);
12 years on and the this question is still active, I didn't find an implementation of Kyle's solution I liked so here it is:
public IEnumerable<T> TakeRandom<T>(IEnumerable<T> collection, int take)
{
var random = new Random();
var available = collection.Count();
var needed = take;
foreach (var item in collection)
{
if (random.Next(available) < needed)
{
needed--;
yield return item;
if (needed == 0)
{
break;
}
}
available--;
}
}
From Dragons in the Algorithm, an interpretation in C#:
int k = 10; // items to select
var items = new List<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });
var selected = new List<int>();
double needed = k;
double available = items.Count;
var rand = new Random();
while (selected.Count < k) {
if( rand.NextDouble() < needed / available ) {
selected.Add(items[(int)available-1])
needed--;
}
available--;
}
This algorithm will select unique indicies of the items list.
Was thinking about comment by #JohnShedletsky on the accepted answer regarding (paraphrase):
you should be able to to this in O(subset.Length), rather than O(originalList.Length)
Basically, you should be able to generate subset random indices and then pluck them from the original list.
The Method
public static class EnumerableExtensions {
public static Random randomizer = new Random(); // you'd ideally be able to replace this with whatever makes you comfortable
public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int numItems) {
return (list as T[] ?? list.ToArray()).GetRandom(numItems);
// because ReSharper whined about duplicate enumeration...
/*
items.Add(list.ElementAt(randomizer.Next(list.Count()))) ) numItems--;
*/
}
// just because the parentheses were getting confusing
public static IEnumerable<T> GetRandom<T>(this T[] list, int numItems) {
var items = new HashSet<T>(); // don't want to add the same item twice; otherwise use a list
while (numItems > 0 )
// if we successfully added it, move on
if( items.Add(list[randomizer.Next(list.Length)]) ) numItems--;
return items;
}
// and because it's really fun; note -- you may get repetition
public static IEnumerable<T> PluckRandomly<T>(this IEnumerable<T> list) {
while( true )
yield return list.ElementAt(randomizer.Next(list.Count()));
}
}
If you wanted to be even more efficient, you would probably use a HashSet of the indices, not the actual list elements (in case you've got complex types or expensive comparisons);
The Unit Test
And to make sure we don't have any collisions, etc.
[TestClass]
public class RandomizingTests : UnitTestBase {
[TestMethod]
public void GetRandomFromList() {
this.testGetRandomFromList((list, num) => list.GetRandom(num));
}
[TestMethod]
public void PluckRandomly() {
this.testGetRandomFromList((list, num) => list.PluckRandomly().Take(num), requireDistinct:false);
}
private void testGetRandomFromList(Func<IEnumerable<int>, int, IEnumerable<int>> methodToGetRandomItems, int numToTake = 10, int repetitions = 100000, bool requireDistinct = true) {
var items = Enumerable.Range(0, 100);
IEnumerable<int> randomItems = null;
while( repetitions-- > 0 ) {
randomItems = methodToGetRandomItems(items, numToTake);
Assert.AreEqual(numToTake, randomItems.Count(),
"Did not get expected number of items {0}; failed at {1} repetition--", numToTake, repetitions);
if(requireDistinct) Assert.AreEqual(numToTake, randomItems.Distinct().Count(),
"Collisions (non-unique values) found, failed at {0} repetition--", repetitions);
Assert.IsTrue(randomItems.All(o => items.Contains(o)),
"Some unknown values found; failed at {0} repetition--", repetitions);
}
}
}
Selecting N random items from a group shouldn't have anything to do with order! Randomness is about unpredictability and not about shuffling positions in a group. All the answers that deal with some kinda ordering is bound to be less efficient than the ones that do not. Since efficiency is the key here, I will post something that doesn't change the order of items too much.
1) If you need true random values which means there is no restriction on which elements to choose from (ie, once chosen item can be reselected):
public static List<T> GetTrueRandom<T>(this IList<T> source, int count,
bool throwArgumentOutOfRangeException = true)
{
if (throwArgumentOutOfRangeException && count > source.Count)
throw new ArgumentOutOfRangeException();
var randoms = new List<T>(count);
randoms.AddRandomly(source, count);
return randoms;
}
If you set the exception flag off, then you can choose random items any number of times.
If you have { 1, 2, 3, 4 }, then it can give { 1, 4, 4 }, { 1, 4, 3 } etc for 3 items or even { 1, 4, 3, 2, 4 } for 5 items!
This should be pretty fast, as it has nothing to check.
2) If you need individual members from the group with no repetition, then I would rely on a dictionary (as many have pointed out already).
public static List<T> GetDistinctRandom<T>(this IList<T> source, int count)
{
if (count > source.Count)
throw new ArgumentOutOfRangeException();
if (count == source.Count)
return new List<T>(source);
var sourceDict = source.ToIndexedDictionary();
if (count > source.Count / 2)
{
while (sourceDict.Count > count)
sourceDict.Remove(source.GetRandomIndex());
return sourceDict.Select(kvp => kvp.Value).ToList();
}
var randomDict = new Dictionary<int, T>(count);
while (randomDict.Count < count)
{
int key = source.GetRandomIndex();
if (!randomDict.ContainsKey(key))
randomDict.Add(key, sourceDict[key]);
}
return randomDict.Select(kvp => kvp.Value).ToList();
}
The code is a bit lengthier than other dictionary approaches here because I'm not only adding, but also removing from list, so its kinda two loops. You can see here that I have not reordered anything at all when count becomes equal to source.Count. That's because I believe randomness should be in the returned set as a whole. I mean if you want 5 random items from 1, 2, 3, 4, 5, it shouldn't matter if its 1, 3, 4, 2, 5 or 1, 2, 3, 4, 5, but if you need 4 items from the same set, then it should unpredictably yield in 1, 2, 3, 4, 1, 3, 5, 2, 2, 3, 5, 4 etc. Secondly, when the count of random items to be returned is more than half of the original group, then its easier to remove source.Count - count items from the group than adding count items. For performance reasons I have used source instead of sourceDict to get then random index in the remove method.
So if you have { 1, 2, 3, 4 }, this can end up in { 1, 2, 3 }, { 3, 4, 1 } etc for 3 items.
3) If you need truly distinct random values from your group by taking into account the duplicates in the original group, then you may use the same approach as above, but a HashSet will be lighter than a dictionary.
public static List<T> GetTrueDistinctRandom<T>(this IList<T> source, int count,
bool throwArgumentOutOfRangeException = true)
{
if (count > source.Count)
throw new ArgumentOutOfRangeException();
var set = new HashSet<T>(source);
if (throwArgumentOutOfRangeException && count > set.Count)
throw new ArgumentOutOfRangeException();
List<T> list = hash.ToList();
if (count >= set.Count)
return list;
if (count > set.Count / 2)
{
while (set.Count > count)
set.Remove(list.GetRandom());
return set.ToList();
}
var randoms = new HashSet<T>();
randoms.AddRandomly(list, count);
return randoms.ToList();
}
The randoms variable is made a HashSet to avoid duplicates being added in the rarest of rarest cases where Random.Next can yield the same value, especially when input list is small.
So { 1, 2, 2, 4 } => 3 random items => { 1, 2, 4 } and never { 1, 2, 2}
{ 1, 2, 2, 4 } => 4 random items => exception!! or { 1, 2, 4 } depending on the flag set.
Some of the extension methods I have used:
static Random rnd = new Random();
public static int GetRandomIndex<T>(this ICollection<T> source)
{
return rnd.Next(source.Count);
}
public static T GetRandom<T>(this IList<T> source)
{
return source[source.GetRandomIndex()];
}
static void AddRandomly<T>(this ICollection<T> toCol, IList<T> fromList, int count)
{
while (toCol.Count < count)
toCol.Add(fromList.GetRandom());
}
public static Dictionary<int, T> ToIndexedDictionary<T>(this IEnumerable<T> lst)
{
return lst.ToIndexedDictionary(t => t);
}
public static Dictionary<int, T> ToIndexedDictionary<S, T>(this IEnumerable<S> lst,
Func<S, T> valueSelector)
{
int index = -1;
return lst.ToDictionary(t => ++index, valueSelector);
}
If its all about performance with tens of 1000s of items in the list having to be iterated 10000 times, then you may want to have faster random class than System.Random, but I don't think that's a big deal considering the latter most probably is never a bottleneck, its quite fast enough..
Edit: If you need to re-arrange order of returned items as well, then there's nothing that can beat dhakim's Fisher-Yates approach - short, sweet and simple..
I combined several of the above answers to create a Lazily-evaluated extension method. My testing showed that Kyle's approach (Order(N)) is many times slower than drzaus' use of a set to propose the random indices to choose (Order(K)). The former performs many more calls to the random number generator, plus iterates more times over the items.
The goals of my implementation were:
1) Do not realize the full list if given an IEnumerable that is not an IList. If I am given a sequence of a zillion items, I do not want to run out of memory. Use Kyle's approach for an on-line solution.
2) If I can tell that it is an IList, use drzaus' approach, with a twist. If K is more than half of N, I risk thrashing as I choose many random indices again and again and have to skip them. Thus I compose a list of the indices to NOT keep.
3) I guarantee that the items will be returned in the same order that they were encountered. Kyle's algorithm required no alteration. drzaus' algorithm required that I not emit items in the order that the random indices are chosen. I gather all the indices into a SortedSet, then emit items in sorted index order.
4) If K is large compared to N and I invert the sense of the set, then I enumerate all items and test if the index is not in the set. This means that
I lose the Order(K) run time, but since K is close to N in these cases, I do not lose much.
Here is the code:
/// <summary>
/// Takes k elements from the next n elements at random, preserving their order.
///
/// If there are fewer than n elements in items, this may return fewer than k elements.
/// </summary>
/// <typeparam name="TElem">Type of element in the items collection.</typeparam>
/// <param name="items">Items to be randomly selected.</param>
/// <param name="k">Number of items to pick.</param>
/// <param name="n">Total number of items to choose from.
/// If the items collection contains more than this number, the extra members will be skipped.
/// If the items collection contains fewer than this number, it is possible that fewer than k items will be returned.</param>
/// <returns>Enumerable over the retained items.
///
/// See http://stackoverflow.com/questions/48087/select-a-random-n-elements-from-listt-in-c-sharp for the commentary.
/// </returns>
public static IEnumerable<TElem> TakeRandom<TElem>(this IEnumerable<TElem> items, int k, int n)
{
var r = new FastRandom();
var itemsList = items as IList<TElem>;
if (k >= n || (itemsList != null && k >= itemsList.Count))
foreach (var item in items) yield return item;
else
{
// If we have a list, we can infer more information and choose a better algorithm.
// When using an IList, this is about 7 times faster (on one benchmark)!
if (itemsList != null && k < n/2)
{
// Since we have a List, we can use an algorithm suitable for Lists.
// If there are fewer than n elements, reduce n.
n = Math.Min(n, itemsList.Count);
// This algorithm picks K index-values randomly and directly chooses those items to be selected.
// If k is more than half of n, then we will spend a fair amount of time thrashing, picking
// indices that we have already picked and having to try again.
var invertSet = k >= n/2;
var positions = invertSet ? (ISet<int>) new HashSet<int>() : (ISet<int>) new SortedSet<int>();
var numbersNeeded = invertSet ? n - k : k;
while (numbersNeeded > 0)
if (positions.Add(r.Next(0, n))) numbersNeeded--;
if (invertSet)
{
// positions contains all the indices of elements to Skip.
for (var itemIndex = 0; itemIndex < n; itemIndex++)
{
if (!positions.Contains(itemIndex))
yield return itemsList[itemIndex];
}
}
else
{
// positions contains all the indices of elements to Take.
foreach (var itemIndex in positions)
yield return itemsList[itemIndex];
}
}
else
{
// Since we do not have a list, we will use an online algorithm.
// This permits is to skip the rest as soon as we have enough items.
var found = 0;
var scanned = 0;
foreach (var item in items)
{
var rand = r.Next(0,n-scanned);
if (rand < k - found)
{
yield return item;
found++;
}
scanned++;
if (found >= k || scanned >= n)
break;
}
}
}
}
I use a specialized random number generator, but you can just use C#'s Random if you want. (FastRandom was written by Colin Green and is part of SharpNEAT. It has a period of 2^128-1 which is better than many RNGs.)
Here are the unit tests:
[TestClass]
public class TakeRandomTests
{
/// <summary>
/// Ensure that when randomly choosing items from an array, all items are chosen with roughly equal probability.
/// </summary>
[TestMethod]
public void TakeRandom_Array_Uniformity()
{
const int numTrials = 2000000;
const int expectedCount = numTrials/20;
var timesChosen = new int[100];
var century = new int[100];
for (var i = 0; i < century.Length; i++)
century[i] = i;
for (var trial = 0; trial < numTrials; trial++)
{
foreach (var i in century.TakeRandom(5, 100))
timesChosen[i]++;
}
var avg = timesChosen.Average();
var max = timesChosen.Max();
var min = timesChosen.Min();
var allowedDifference = expectedCount/100;
AssertBetween(avg, expectedCount - 2, expectedCount + 2, "Average");
//AssertBetween(min, expectedCount - allowedDifference, expectedCount, "Min");
//AssertBetween(max, expectedCount, expectedCount + allowedDifference, "Max");
var countInRange = timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
}
/// <summary>
/// Ensure that when randomly choosing items from an IEnumerable that is not an IList,
/// all items are chosen with roughly equal probability.
/// </summary>
[TestMethod]
public void TakeRandom_IEnumerable_Uniformity()
{
const int numTrials = 2000000;
const int expectedCount = numTrials / 20;
var timesChosen = new int[100];
for (var trial = 0; trial < numTrials; trial++)
{
foreach (var i in Range(0,100).TakeRandom(5, 100))
timesChosen[i]++;
}
var avg = timesChosen.Average();
var max = timesChosen.Max();
var min = timesChosen.Min();
var allowedDifference = expectedCount / 100;
var countInRange =
timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
}
private IEnumerable<int> Range(int low, int count)
{
for (var i = low; i < low + count; i++)
yield return i;
}
private static void AssertBetween(int x, int low, int high, String message)
{
Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
}
private static void AssertBetween(double x, double low, double high, String message)
{
Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
}
}
Here you have one implementation based on Fisher-Yates Shuffle whose algorithm complexity is O(n) where n is the subset or sample size, instead of the list size, as John Shedletsky pointed out.
public static IEnumerable<T> GetRandomSample<T>(this IList<T> list, int sampleSize)
{
if (list == null) throw new ArgumentNullException("list");
if (sampleSize > list.Count) throw new ArgumentException("sampleSize may not be greater than list count", "sampleSize");
var indices = new Dictionary<int, int>(); int index;
var rnd = new Random();
for (int i = 0; i < sampleSize; i++)
{
int j = rnd.Next(i, list.Count);
if (!indices.TryGetValue(j, out index)) index = j;
yield return list[index];
if (!indices.TryGetValue(i, out index)) index = i;
indices[j] = index;
}
}
Extending from #ers's answer, if one is worried about possible different implementations of OrderBy, this should be safe:
// Instead of this
YourList.OrderBy(x => rnd.Next()).Take(5)
// Temporarily transform
YourList
.Select(v => new {v, i = rnd.Next()}) // Associate a random index to each entry
.OrderBy(x => x.i).Take(5) // Sort by (at this point fixed) random index
.Select(x => x.v); // Go back to enumerable of entry
The simple solution I use (probably not good for large lists):
Copy the list into temporary list, then in loop randomly select Item from temp list and put it in selected items list while removing it form temp list (so it can't be reselected).
Example:
List<Object> temp = OriginalList.ToList();
List<Object> selectedItems = new List<Object>();
Random rnd = new Random();
Object o;
int i = 0;
while (i < NumberOfSelectedItems)
{
o = temp[rnd.Next(temp.Count)];
selectedItems.Add(o);
temp.Remove(o);
i++;
}
This is the best I could come up with on a first cut:
public List<String> getRandomItemsFromList(int returnCount, List<String> list)
{
List<String> returnList = new List<String>();
Dictionary<int, int> randoms = new Dictionary<int, int>();
while (randoms.Count != returnCount)
{
//generate new random between one and total list count
int randomInt = new Random().Next(list.Count);
// store this in dictionary to ensure uniqueness
try
{
randoms.Add(randomInt, randomInt);
}
catch (ArgumentException aex)
{
Console.Write(aex.Message);
} //we can assume this element exists in the dictonary already
//check for randoms length and then iterate through the original list
//adding items we select via random to the return list
if (randoms.Count == returnCount)
{
foreach (int key in randoms.Keys)
returnList.Add(list[randoms[key]]);
break; //break out of _while_ loop
}
}
return returnList;
}
Using a list of randoms within a range of 1 - total list count and then simply pulling those items in the list seemed to be the best way, but using the Dictionary to ensure uniqueness is something I'm still mulling over.
Also note I used a string list, replace as needed.
Based on Kyle's answer, here's my c# implementation.
/// <summary>
/// Picks random selection of available game ID's
/// </summary>
private static List<int> GetRandomGameIDs(int count)
{
var gameIDs = (int[])HttpContext.Current.Application["NonDeletedArcadeGameIDs"];
var totalGameIDs = gameIDs.Count();
if (count > totalGameIDs) count = totalGameIDs;
var rnd = new Random();
var leftToPick = count;
var itemsLeft = totalGameIDs;
var arrPickIndex = 0;
var returnIDs = new List<int>();
while (leftToPick > 0)
{
if (rnd.Next(0, itemsLeft) < leftToPick)
{
returnIDs .Add(gameIDs[arrPickIndex]);
leftToPick--;
}
arrPickIndex++;
itemsLeft--;
}
return returnIDs ;
}
This method may be equivalent to Kyle's.
Say your list is of size n and you want k elements.
Random rand = new Random();
for(int i = 0; k>0; ++i)
{
int r = rand.Next(0, n-i);
if(r<k)
{
//include element i
k--;
}
}
Works like a charm :)
-Alex Gilbert
Here is a benchmark of three different methods:
The implementation of the accepted answer from Kyle.
An approach based on random index selection with HashSet duplication filtering, from drzaus.
A more academic approach posted by Jesús López, called Fisher–Yates shuffle.
The testing will consist of benchmarking the performance with multiple different list sizes and selection sizes.
I also included a measurement of the standard deviation of these three methods, i.e. how well distributed the random selection appears to be.
In a nutshell, drzaus's simple solution seems to be the best overall, from these three. The selected answer is great and elegant, but it's not that efficient, given that the time complexity is based on the sample size, not the selection size. Consequently, if you select a small number of items from a long list, it will take orders of magnitude more time. Of course it still performs better than the solutions based on complete reordering.
Curiously enough, this O(n) time complexity issue is true even if you only touch the list when you actually return an item, like I do in my implementation. The only thing I can thing of is that Random.Next() is pretty slow, and that performance benefits if you generate only one random number for each selected item.
And, also interestingly, the StdDev of Kyle's solution was significantly higher comparatively. I have no clue why; maybe the fault is in my implementation.
Sorry for the long code and output that will commence now; but I hope it's somewhat illuminative. Also, if you spot any issues in the tests or implementations, let me know and I'll fix it.
static void Main()
{
BenchmarkRunner.Run<Benchmarks>();
new Benchmarks() { ListSize = 100, SelectionSize = 10 }
.BenchmarkStdDev();
}
[MemoryDiagnoser]
public class Benchmarks
{
[Params(50, 500, 5000)]
public int ListSize;
[Params(5, 10, 25, 50)]
public int SelectionSize;
private Random _rnd;
private List<int> _list;
private int[] _hits;
[GlobalSetup]
public void Setup()
{
_rnd = new Random(12345);
_list = Enumerable.Range(0, ListSize).ToList();
_hits = new int[ListSize];
}
[Benchmark]
public void Test_IterateSelect()
=> Random_IterateSelect(_list, SelectionSize).ToList();
[Benchmark]
public void Test_RandomIndices()
=> Random_RandomIdices(_list, SelectionSize).ToList();
[Benchmark]
public void Test_FisherYates()
=> Random_FisherYates(_list, SelectionSize).ToList();
public void BenchmarkStdDev()
{
RunOnce(Random_IterateSelect, "IterateSelect");
RunOnce(Random_RandomIdices, "RandomIndices");
RunOnce(Random_FisherYates, "FisherYates");
void RunOnce(Func<IEnumerable<int>, int, IEnumerable<int>> method, string methodName)
{
Setup();
for (int i = 0; i < 1000000; i++)
{
var selected = method(_list, SelectionSize).ToList();
Debug.Assert(selected.Count() == SelectionSize);
foreach (var item in selected) _hits[item]++;
}
var stdDev = GetStdDev(_hits);
Console.WriteLine($"StdDev of {methodName}: {stdDev :n} (% of average: {stdDev / (_hits.Average() / 100) :n})");
}
double GetStdDev(IEnumerable<int> hits)
{
var average = hits.Average();
return Math.Sqrt(hits.Average(v => Math.Pow(v - average, 2)));
}
}
public IEnumerable<T> Random_IterateSelect<T>(IEnumerable<T> collection, int needed)
{
var count = collection.Count();
for (int i = 0; i < count; i++)
{
if (_rnd.Next(count - i) < needed)
{
yield return collection.ElementAt(i);
if (--needed == 0)
yield break;
}
}
}
public IEnumerable<T> Random_RandomIdices<T>(IEnumerable<T> list, int needed)
{
var selectedItems = new HashSet<T>();
var count = list.Count();
while (needed > 0)
if (selectedItems.Add(list.ElementAt(_rnd.Next(count))))
needed--;
return selectedItems;
}
public IEnumerable<T> Random_FisherYates<T>(IEnumerable<T> list, int sampleSize)
{
var count = list.Count();
if (sampleSize > count) throw new ArgumentException("sampleSize may not be greater than list count", "sampleSize");
var indices = new Dictionary<int, int>(); int index;
for (int i = 0; i < sampleSize; i++)
{
int j = _rnd.Next(i, count);
if (!indices.TryGetValue(j, out index)) index = j;
yield return list.ElementAt(index);
if (!indices.TryGetValue(i, out index)) index = i;
indices[j] = index;
}
}
}
Output:
| Method | ListSize | Select | Mean | Error | StdDev | Gen 0 | Allocated |
|-------------- |--------- |------- |------------:|----------:|----------:|-------:|----------:|
| IterateSelect | 50 | 5 | 711.5 ns | 5.19 ns | 4.85 ns | 0.0305 | 144 B |
| RandomIndices | 50 | 5 | 341.1 ns | 4.48 ns | 4.19 ns | 0.0644 | 304 B |
| FisherYates | 50 | 5 | 573.5 ns | 6.12 ns | 5.72 ns | 0.0944 | 447 B |
| IterateSelect | 50 | 10 | 967.2 ns | 4.64 ns | 3.87 ns | 0.0458 | 220 B |
| RandomIndices | 50 | 10 | 709.9 ns | 11.27 ns | 9.99 ns | 0.1307 | 621 B |
| FisherYates | 50 | 10 | 1,204.4 ns | 10.63 ns | 9.94 ns | 0.1850 | 875 B |
| IterateSelect | 50 | 25 | 1,358.5 ns | 7.97 ns | 6.65 ns | 0.0763 | 361 B |
| RandomIndices | 50 | 25 | 1,958.1 ns | 15.69 ns | 13.91 ns | 0.2747 | 1298 B |
| FisherYates | 50 | 25 | 2,878.9 ns | 31.42 ns | 29.39 ns | 0.3471 | 1653 B |
| IterateSelect | 50 | 50 | 1,739.1 ns | 15.86 ns | 14.06 ns | 0.1316 | 629 B |
| RandomIndices | 50 | 50 | 8,906.1 ns | 88.92 ns | 74.25 ns | 0.5951 | 2848 B |
| FisherYates | 50 | 50 | 4,899.9 ns | 38.10 ns | 33.78 ns | 0.4349 | 2063 B |
| IterateSelect | 500 | 5 | 4,775.3 ns | 46.96 ns | 41.63 ns | 0.0305 | 144 B |
| RandomIndices | 500 | 5 | 327.8 ns | 2.82 ns | 2.50 ns | 0.0644 | 304 B |
| FisherYates | 500 | 5 | 558.5 ns | 7.95 ns | 7.44 ns | 0.0944 | 449 B |
| IterateSelect | 500 | 10 | 5,387.1 ns | 44.57 ns | 41.69 ns | 0.0458 | 220 B |
| RandomIndices | 500 | 10 | 648.0 ns | 9.12 ns | 8.54 ns | 0.1307 | 621 B |
| FisherYates | 500 | 10 | 1,154.6 ns | 13.66 ns | 12.78 ns | 0.1869 | 889 B |
| IterateSelect | 500 | 25 | 6,442.3 ns | 48.90 ns | 40.83 ns | 0.0763 | 361 B |
| RandomIndices | 500 | 25 | 1,569.6 ns | 15.79 ns | 14.77 ns | 0.2747 | 1298 B |
| FisherYates | 500 | 25 | 2,726.1 ns | 25.32 ns | 22.44 ns | 0.3777 | 1795 B |
| IterateSelect | 500 | 50 | 7,775.4 ns | 35.47 ns | 31.45 ns | 0.1221 | 629 B |
| RandomIndices | 500 | 50 | 2,976.9 ns | 27.11 ns | 24.03 ns | 0.6027 | 2848 B |
| FisherYates | 500 | 50 | 5,383.2 ns | 36.49 ns | 32.35 ns | 0.8163 | 3870 B |
| IterateSelect | 5000 | 5 | 45,208.6 ns | 459.92 ns | 430.21 ns | - | 144 B |
| RandomIndices | 5000 | 5 | 328.7 ns | 5.15 ns | 4.81 ns | 0.0644 | 304 B |
| FisherYates | 5000 | 5 | 556.1 ns | 10.75 ns | 10.05 ns | 0.0944 | 449 B |
| IterateSelect | 5000 | 10 | 49,253.9 ns | 420.26 ns | 393.11 ns | - | 220 B |
| RandomIndices | 5000 | 10 | 642.9 ns | 4.95 ns | 4.13 ns | 0.1307 | 621 B |
| FisherYates | 5000 | 10 | 1,141.9 ns | 12.81 ns | 11.98 ns | 0.1869 | 889 B |
| IterateSelect | 5000 | 25 | 54,044.4 ns | 208.92 ns | 174.46 ns | 0.0610 | 361 B |
| RandomIndices | 5000 | 25 | 1,480.5 ns | 11.56 ns | 10.81 ns | 0.2747 | 1298 B |
| FisherYates | 5000 | 25 | 2,713.9 ns | 27.31 ns | 24.21 ns | 0.3777 | 1795 B |
| IterateSelect | 5000 | 50 | 54,418.2 ns | 329.62 ns | 308.32 ns | 0.1221 | 629 B |
| RandomIndices | 5000 | 50 | 2,886.4 ns | 36.53 ns | 34.17 ns | 0.6027 | 2848 B |
| FisherYates | 5000 | 50 | 5,347.2 ns | 59.45 ns | 55.61 ns | 0.8163 | 3870 B |
StdDev of IterateSelect: 671.88 (% of average: 0.67)
StdDev of RandomIndices: 296.07 (% of average: 0.30)
StdDev of FisherYates: 280.47 (% of average: 0.28)
It is a lot harder than one would think. See the great Article "Shuffling" from Jeff.
I did write a very short article on that subject including C# code:
Return random subset of N elements of a given array
Goal: Select N number of items from collection source without duplication.
I created an extension for any generic collection. Here's how I did it:
public static class CollectionExtension
{
public static IList<TSource> RandomizeCollection<TSource>(this IList<TSource> source, int maxItems)
{
int randomCount = source.Count > maxItems ? maxItems : source.Count;
int?[] randomizedIndices = new int?[randomCount];
Random random = new Random();
for (int i = 0; i < randomizedIndices.Length; i++)
{
int randomResult = -1;
while (randomizedIndices.Contains((randomResult = random.Next(0, source.Count))))
{
//0 -> since all list starts from index 0; source.Count -> maximum number of items that can be randomize
//continue looping while the generated random number is already in the list of randomizedIndices
}
randomizedIndices[i] = randomResult;
}
IList<TSource> result = new List<TSource>();
foreach (int index in randomizedIndices)
result.Add(source.ElementAt(index));
return result;
}
}
Short and simple. Hope this helps someone!
if (list.Count > maxListCount)
{
var rndList = new List<YourEntity>();
var r = new Random();
while (rndList.Count < maxListCount)
{
var addingElement = list[r.Next(list.Count)];
//element uniqueness checking
//choose your case
//if (rndList.Contains(addingElement))
//if (rndList.Any(p => p.Id == addingElement.Id))
continue;
rndList.Add(addingElement);
}
return rndList;
}
public static IEnumerable<TItem> RandomSample<TItem>(this IReadOnlyList<TItem> items, int count)
{
if (count < 1 || count > items.Count)
{
throw new ArgumentOutOfRangeException(nameof(count));
}
List<int> indexes = Enumerable.Range(0, items.Count).ToList();
int yieldedCount = 0;
while (yieldedCount < count)
{
int i = RandomNumberGenerator.GetInt32(indexes.Count);
int randomIndex = indexes[i];
yield return items[randomIndex];
// indexes.RemoveAt(i); // Avoid removing items from the middle of the list
indexes[i] = indexes[indexes.Count - 1]; // Replace yielded index with the last one
indexes.RemoveAt(indexes.Count - 1);
yieldedCount++;
}
}
public static IEnumerable<T> GetRandom<T>(IList<T> list, int count, Random random)
{
// Probably you should throw exception if count > list.Count
count = Math.Min(list.Count, count);
var selectedIndices = new SortedSet<int>();
// Random upper bound (exclusive)
int randomMax = list.Count;
while (selectedIndices.Count < count)
{
int randomIndex = random.Next(0, randomMax);
// skip over already selected indices
foreach (var selectedIndex in selectedIndices)
if (selectedIndex <= randomIndex)
++randomIndex;
else
break;
yield return list[randomIndex];
selectedIndices.Add(randomIndex);
--randomMax;
}
}
Memory: ~count
Complexity: O(count2)
I recently did this on my project using an idea similar to Tyler's point 1.
I was loading a bunch of questions and selecting five at random. Sorting was achieved using an IComparer.
aAll questions were loaded in the a QuestionSorter list, which was then sorted using the List's Sort function and the first k elements where selected.
private class QuestionSorter : IComparable<QuestionSorter>
{
public double SortingKey
{
get;
set;
}
public Question QuestionObject
{
get;
set;
}
public QuestionSorter(Question q)
{
this.SortingKey = RandomNumberGenerator.RandomDouble;
this.QuestionObject = q;
}
public int CompareTo(QuestionSorter other)
{
if (this.SortingKey < other.SortingKey)
{
return -1;
}
else if (this.SortingKey > other.SortingKey)
{
return 1;
}
else
{
return 0;
}
}
}
Usage:
List<QuestionSorter> unsortedQuestions = new List<QuestionSorter>();
// add the questions here
unsortedQuestions.Sort(unsortedQuestions as IComparer<QuestionSorter>);
// select the first k elements
why not something like this:
Dim ar As New ArrayList
Dim numToGet As Integer = 5
'hard code just to test
ar.Add("12")
ar.Add("11")
ar.Add("10")
ar.Add("15")
ar.Add("16")
ar.Add("17")
Dim randomListOfProductIds As New ArrayList
Dim toAdd As String = ""
For i = 0 To numToGet - 1
toAdd = ar(CInt((ar.Count - 1) * Rnd()))
randomListOfProductIds.Add(toAdd)
'remove from id list
ar.Remove(toAdd)
Next
'sorry i'm lazy and have to write vb at work :( and didn't feel like converting to c#
Here's my approach (full text here http://krkadev.blogspot.com/2010/08/random-numbers-without-repetition.html ).
It should run in O(K) instead of O(N), where K is the number of wanted elements and N is the size of the list to choose from:
public <T> List<T> take(List<T> source, int k) {
int n = source.size();
if (k > n) {
throw new IllegalStateException(
"Can not take " + k +
" elements from a list with " + n +
" elements");
}
List<T> result = new ArrayList<T>(k);
Map<Integer,Integer> used = new HashMap<Integer,Integer>();
int metric = 0;
for (int i = 0; i < k; i++) {
int off = random.nextInt(n - i);
while (true) {
metric++;
Integer redirect = used.put(off, n - i - 1);
if (redirect == null) {
break;
}
off = redirect;
}
result.add(source.get(off));
}
assert metric <= 2*k;
return result;
}
This isn't as elegant or efficient as the accepted solution, but it's quick to write up. First, permute the array randomly, then select the first K elements. In python,
import numpy
N = 20
K = 5
idx = np.arange(N)
numpy.random.shuffle(idx)
print idx[:K]
I would use an extension method.
public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> elements, int countToTake)
{
var random = new Random();
var internalList = elements.ToList();
var selected = new List<T>();
for (var i = 0; i < countToTake; ++i)
{
var next = random.Next(0, internalList.Count - selected.Count);
selected.Add(internalList[next]);
internalList[next] = internalList[internalList.Count - selected.Count];
}
return selected;
}
Using LINQ with large lists (when costly to touch each element) AND if you can live with the possibility of duplicates:
new int[5].Select(o => (int)(rnd.NextDouble() * maxIndex)).Select(i => YourIEnum.ElementAt(i))
For my use i had a list of 100.000 elements, and because of them being pulled from a DB I about halfed (or better) the time compared to a rnd on the whole list.
Having a large list will reduce the odds greatly for duplicates.

Every possible combination of X split into N stacks

I am sure this problem has a formal name, and knowing that name would probably help me find the solution, but I don't know it, and wording the problem for Google keeps pointing me to the Knapsack Problem, which isn't the same thing.
I want to take some value X and find every possible combination of splitting that value into N stacks of whole integers.
In case my wording is confusing, here is an example of X = 4, N = 3
Stack -> 1 | 2 | 3 |
----------------------
#1-----> 4 | 0 | 0 |
----------------------
#2-----> 3 | 1 | 0 |
----------------------
#3-----> 2 | 1 | 1 |
----------------------
#4-----> 2 | 2 | 0 |
Duplication is acceptable, since its easy to remove, but ideally it would not be calculated. An algorithm for solving the problem would be perfect, but even finding out of the problem has a name would make research easier. Thanks.
These are in fact integer partitions as a deleted answer remarks. Using Mathematica:
IntegerPartitions[4, 3] // PadRight //Grid
Output:
4 0 0
3 1 0
2 2 0
2 1 1
I could not find a C# implementation but here are a couple of related questions:
Elegant Python code for Integer Partitioning
Integer Partition in Java
Algorithm for generating integer partitions
Google hits:
Integer Partition Algorithm by Jerome Kelleher
Integer Partition Algorithm by Daniel Scocco
Fast Algorithms for Generating Integer Partitions (PDF) (looks heavy-duty)
Stony Brook Algorithm Repository - Partitions
This seems to do the trick:
vector<vector<int> > partitions(int X, int N, int Y)
{
vector<vector<int> > v;
if(X<=1 || N==1)
{
if(X<=Y)
{
v.resize(1);
v[0].push_back(X);
}
return v;
}
for(int y=min(X, Y); y>=1; y--)
{
vector<vector<int> > w = partitions(X-y, N-1, y);
for(int i=0; i<w.size(); i++)
{
w[i].push_back(y);
v.push_back(w[i]);
}
}
return v;
}
int main()
{
vector<vector<int> > v = partitions(5, 3, 5);
int i;
for(i=0; i<v.size(); i++)
{
int x;
for(x=0; x<v[i].size(); x++)
printf("%d ", v[i][x]);
printf("\n");
}
return 0;
}
This is user434507's answer in C#:
class Program
{
static void Main(string[] args)
{
var v = Partitions(5, 3, 5);
for (int i = 0; i < v.Count; i++)
{
for (int x = 0; x < v[i].Count; x++)
Console.Write(v[i][x] + " ");
Console.WriteLine();
}
}
static private List<List<int>> Partitions(int total, int stacks, int max)
{
List<List<int>> partitions = new List<List<int>>();
if (total <= 1 || stacks == 1)
{
if (total <= max)
{
partitions.Add(new List<int>());
partitions[0].Add(total);
}
return partitions;
}
for (int y = Math.Min(total, max); y >= 1; y--)
{
var w = Partitions(total - y, stacks - 1, y);
for (int i = 0; i < w.Count; i++)
{
w[i].Add(y);
partitions.Add(w[i]);
}
}
return partitions;
}
}

Categories