I need to generate all possible numbers (integers) from 0 to 999999 (without repetition) while respecting a series of constraints.
To better understand the requirements, imagine each number being formed by a 2 digit prefix and a suffix of 4 digits. Like 000000 being read as 00-0000 and 999999 as 99-9999. Now to the rules:
prefixes must be in random order
suffixes must be in random order while making sure that each 10k numbers in the sequence have all numbers from 0000 to 9999.
must be able to generate the numbers again in the same order, given a seed.
Not really a requirement but it would be great if it was done using Linq.
Thus far, I have written some code that meets all requirements but the first one:
var seed = 102111;
var rnd = new Random(seed);
var prefix = Enumerable.Range(0, 100).OrderBy(p => rnd.Next());
var suffix = Enumerable.Range(0, 10000).OrderBy(s => rnd.Next());
var result = from p in prefix
from s in suffix
select p.ToString("d2") + s.ToString("d4");
foreach(var entry in result)
{
Console.WriteLine(entry);
}
Using this I am able to reproduce the sequence by using the same seed, the first 10000 numbers have all numbers from 0000 to 9999, as does the second 10k and so on, but the prefixes are not really random since each 10k group will have the same prefix.
I also thought of creating a class with the number and it's group (100 groups, each group having 10k numbers) to make it easier to shuffle but I believe that are better, simpler ways to do it.
[I've overwritten an earlier, wrong solution based on a misunderstanding of the problem].
We start by making a helper method that produces a shuffled range based on a given seed:
static IEnumerable<int> ShuffledRange(int size, int seed)
{
var rnd = new Random(seed);
return Enumerable.Range(0, size).OrderBy(p => rnd.Next());
}
The next thing we're going to do is to randomize all the suffixes and get all of them into a sequence. Note that we use a different seed for each shuffle, but the value of the seed is predictable.
static IEnumerable<string> ShuffledIds(int seed)
{
const int s = 10000;
const int p = 100;
var suffixes = Enumerable.Range(0, p)
.Select(seedOffset => ShuffledRange(s, seed + seedOffset)
.SelectMany(x => x);
We've met the constraint that each chunk of 10000 has all 10000 suffixes, in random order. Now we have to distribute 10000 of each prefix. Let's make a sequence of prefixes for each possible suffix. (Again, we use a not-yet-used seed for each shuffle.)
var dict = new Dictionary<int, IEnumerator<int>>();
for (int suffix = 0; suffix < s; suffix += 1)
dict[suffix] = ShuffledRange(p, seed + p + suffix).GetEnumerator();
And now we can distribute them
foreach(int suffix in suffixes)
{
dict[suffix].MoveNext();
yield return dict[suffix].Current.ToString("d2") +
suffix.ToString("d4");
}
}
And that should do it.
Notice that this also has the nice property that the shuffling algorithm is no longer the concern of the code which needs shuffles. Try to encapsulate details like that in helper functions.
Using the idea posted by ckuri and including the imporvements suggested by Eric Lippert, you can group the list of numbers by suffix:
var prefixLength = 100;
var suffixLength = 10000;
Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(number => rnd.Next())
.GroupBy(number => number % suffixLength)
Then, you can flatten the list:
Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(number => rnd.Next())
.GroupBy(number => number % suffixLength)
.SelectMany(g => g)
Until here, you will have a list of numbers, where, in each 100 lines (prefixLength), the prefixes will be the same. So, you can select them, getting the index of each line:
Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(number => rnd.Next())
.GroupBy(number => number % suffixLength)
.SelectMany(g => g)
.Select((g, index) => new { Index = index, Number = g })
Using the index information, you can group the lines applying the mod function, using the prefixLength as a factor:
Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(number => rnd.Next())
.GroupBy(number => number % suffixLength)
.SelectMany(g => g)
.Select((g, index) => new { Index = index, Number = g })
.GroupBy(g => g.Index % prefixLength, g => g.Number)
Finally, you can flatten the list again, and convert the values to string, in order to get the final result:
Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(number => rnd.Next())
.GroupBy(number => number % suffixLength)
.SelectMany(g => g)
.Select((g, index) => new { Index = index, Number = g })
.GroupBy(g => g.Index % prefixLength, g => g.Number)
.SelectMany(g => g)
.Select(number => $"{number/suffixLength:d2}{number%suffixLength:d4}")
This solution is inspired by Rodolfo Santos's answer. It improves over his solution by shuffling the numbers inside each group that share the same suffix, completing the randomness of the resulting sequence. The algorithm takes advantage of the fact that LINQ's OrderBy sorting is stable, so ordering the numbers by prefix does not destroy the previous order by random. If this was not the case, an extra grouping and flattening would be required.
public static IEnumerable<int> RandomConstrainedSequence(
int prefixLength, int suffixLength, int seed)
{
var random = new Random(seed);
return Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(_ => random.Next()) // Order by random
.OrderBy(n => n / suffixLength) // Order by prefix (randomness is preserved)
.Select((n, i) => (n, i)) // Store the index
.GroupBy(p => p.n % suffixLength) // Group by suffix
// Suffle the numbers inside each group, and zip with the unsuffled stored indexes
.Select(g => g.OrderBy(_ => random.Next()).Zip(g, (x, y) => (x.n, y.i)))
.SelectMany(g => g) // Flatten the sequence
.OrderBy(p => p.i) // Order by the stored index
.Select(p => p.n); // Discard the index and return the number
}
Usage example:
int index = 0;
foreach (var number in RandomConstrainedSequence(5, 10, 0))
{
Console.Write($"{number:00}, ");
if (++index % 10 == 0) Console.WriteLine();
}
Output:
44, 49, 47, 13, 15, 00, 02, 01, 16, 48,
25, 30, 29, 41, 43, 32, 38, 46, 04, 17,
23, 19, 35, 28, 07, 34, 20, 31, 26, 12,
36, 10, 22, 08, 27, 21, 24, 45, 39, 33,
42, 18, 09, 03, 06, 37, 40, 11, 05, 14,
Update: This solution can be generalized to solve a larger spectrum of problems, where the sorting is constrained into each subgroup of a sequence. Here is an extension method that does exactly that:
public static IEnumerable<TSource> OrderGroupsBy<TSource, TGroupKey, TOrderKey>(
this IEnumerable<TSource> source,
Func<TSource, TGroupKey> groupByKeySelector,
Func<TSource, TOrderKey> orderByKeySelector)
{
return source
.Select((x, i) => (Item: x, Index: i))
.GroupBy(e => groupByKeySelector(e.Item))
.Select(group =>
{
var itemsOrdered = group.Select(e => e.Item).OrderBy(orderByKeySelector);
var indexesUnordered = group.Select(e => e.Index);
return itemsOrdered.Zip(indexesUnordered, (x, i) => (Item: x, Index: i));
})
.SelectMany(group => group)
.OrderBy(pair => pair.Index)
.Select(pair => pair.Item);
}
The effects of this method can be more clearly seen with a different example. An array of names are ordered, but the ordering is constrained inside each subgroup of names starting with the same letter:
var source = new string[] { "Ariel", "Billy", "Bryan", "Anton", "Alexa", "Barby" };
Console.WriteLine($"Source: {String.Join(", ", source)}");
var result = source.OrderGroupsBy(s => s.Substring(0, 1), e => e);
Console.WriteLine($"Result: {String.Join(", ", result)}");
Source: Ariel, Billy, Bryan, Anton, Alexa, Barby
Result: Alexa, Barby, Billy, Anton, Ariel, Bryan
Using this extension method, the original problem can be solved like this:
public static IEnumerable<int> RandomConstrainedSequence(
int prefixLength, int suffixLength, int seed)
{
var random = new Random(seed);
return Enumerable
.Range(0, prefixLength * suffixLength)
.OrderBy(_ => random.Next()) // Order by random
.OrderBy(n => n / suffixLength) // Order again by prefix
// Suffle each subgroup of numbers sharing the same suffix
.OrderGroupsBy(n => n % suffixLength, _ => random.Next());
}
Related
Hi I'm reading a book called "Thinking in LINQ",
at page 11 there's the below code snippet which if using LINQPad it displays properly (Key and Value columns)...
However, I'm testing all the book code example in C# / Console App as I'm working in C# at work, not in LINQ...
so how would i get the same result in a C#? as I tried many things but doesn't not display the value, only the key.
PS : I know that in the book they used Dump but VS has no Dump method, we can replace Dump by a variable assignation, and use the variable in a Console.WriteLine to display data. I tried it in another example of the book and worked well.
static void Main(string[] args)
{
int[] nums = { 20, 15, 31, 34, 35, 40, 50, 90, 99, 100 };
nums.ToLookup(k => k, k => nums.Where(n => n < k))
.Select(k => new KeyValuePair<int, double>
(k.Key, 100 * ((double)k.First().Count() / (double)nums.Length)));
foreach (var item in nums)
{
Console.WriteLine("{0}", item);
}
Console.Read();
}
}
Try assigning nums to a new variable (e.g. items):
Try it online!
static void Main(string[] args)
{
int[] nums = { 20, 15, 31, 34, 35, 40, 50, 90, 99, 100 };
// here we go
var items = nums.ToLookup(k => k, k => nums.Where(n => n < k))
.Select(k => new KeyValuePair<int, double>
(k.Key, 100 * ((double)k.First().Count() / (double)nums.Length)));
foreach (var item in items)
{
Console.WriteLine("{0}", item);
}
Console.Read();
}
You should take the LINQ to another variable
int[] nums = { 20, 15, 31, 34, 35, 40, 50, 90, 99, 100 };
var results = nums.ToLookup(k => k, k => nums.Where(n => n < k))
.Select(k => new KeyValuePair<int, double>
(k.Key, 100 * ((double)k.First().Count() / (double)nums.Length)));
foreach (var item in results)
{
Console.WriteLine("{0}", item);
}
Console.Read();
You forgot to assign the Select method result to a variable in your code, currently you are enumerating the source nums collection. Select returns an IEnumerable<KeyValuePair<int,double>> instance, it isn't changing the source collection. You should assign this instance and enumerate then
var result = nums.ToLookup(k => k, k => nums.Where(n => n < k))
.Select(k => new KeyValuePair<int, double>
(k.Key, 100 * ((double)k.First().Count() / (double)nums.Length)));
foreach (var item in result)
{
Console.WriteLine("{0}", item);
}
Code in book also uses the Dump method from LinqPad, which is missing in your code. Actually this method will produce the useful output, which you will see in the book
In C# you are just printing nums collection using for loop. The screenshot of the book shows, there is Dump at the end of converting to lookup. So, in order to get exact same result, you need to enumerate nums after converting to lookup as shown below: (ToLookup won't modify base collection which is nums integer collection to key-value pair collection by invoking the method.)
static void Main(string[] args)
{
int[] nums = { 20, 15, 31, 34, 35, 40, 50, 90, 99, 100 };
var numsLookup = nums.ToLookup(k => k, k => nums.Where(n => n < k))
.Select(k => new KeyValuePair<int, double>
(k.Key, 100 * ((double)k.First().Count() / (double)nums.Length)));
Console.WriteLine("\tKey\t\tValue");
Console.WriteLine("================");
foreach (var item in numsLookup)
{
Console.WriteLine("{0}\t\t {1}", item.Key, item.Value);
}
Console.Read();
}
As the title says i have a task to find the longest repeating sequence in a string and it has to be done with linq only - no ifs, no loop, no try, assignment is only allowed on initialization of variables, recursion is allowed. I've found the solution online and i understand what is happening but i can't transform it to linq -I'm not that familiar with it. I would greatly appreciate if someone could help me. Here is a link to what ive found -https://www.javatpoint.com/program-to-find-longest-repeating-sequence-in-a-string.
List<int> a = new List<int> {1, 2, 1, 2, 1, 2, 3, 2, 1, 2};
List<List<int>> aa = new List<List<int>>();
outerLoop(a);
var max = aa.Max(x => x.Count);
var m = from v in aa
where v.Count == max
select v;
m.Dump();
void outerLoop(List<int> list)
{
List<int> f = new List<int>();
f.AddRange(list.Skip(list.Count-1).Take(list.Count).ToList());
innerLoop(list, list.Skip(1).Take(list.Count).ToList());
f.ForEach(k => outerLoop(list.Skip(1).Take(list.Count).ToList()));
}
void innerLoop(List<int> l, List<int> subList)
{
List<int> f = new List<int>();
f.AddRange(subList.Skip(subList.Count-1).Take(subList.Count).ToList());
var tt = l.TakeWhile((ch, i) => i < subList.Count && subList[i] == ch).ToList();
aa.Add(tt);
f.ForEach(k => innerLoop(l, subList.Skip(1).Take(subList.Count).ToList()));
}
so i came up with this "beauty", i don't think it's good code but i think it works. If anyone is interested and wants to make suggestions how to make it better, they are more than welcome to :)
if input is int[] x= {1, 2, 1, 2, 1, 2, 3, 2, 1, 2}
result should be 1212
Give this a go:
List<int> words = new List<int> { 1, 2, 1, 2, 1, 2, 3, 2, 1, 2 };
string result =
words
.Select((c, i) => i)
.SelectMany(i => Enumerable.Range(1, words.Count - i).Select(j => words.Skip(i).Take(j)), (i, w) => new { i, w })
.GroupBy(x => String.Join(",", x.w), x => x.i)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key)
.OrderByDescending(x => x.Length)
.First();
That gives me 1,2,1,2.
If you want one that actually works with strings, try this:
var word = "supercalifragilisticexpialidocious";
string result =
word
.Select((c, i) => i)
.SelectMany(i => Enumerable.Range(1, word.Length - i).Select(j => word.Skip(i).Take(j)), (i, w) => new { i, w })
.GroupBy(x => new string(x.w.ToArray()), x => x.i)
.Where(x => x.Skip(1).Any())
.Select(x => x.Key)
.OrderByDescending(x => x.Length)
.First();
That gives me ali.
Here's a slightly more understandable version:
var word = "supercalifragilisticexpialidocious";
string result =
(
from i in Enumerable.Range(0, word.Length)
from j in Enumerable.Range(1, word.Length - i)
group i by word.Substring(i, j) into gis
where gis.Skip(1).Any()
orderby gis.Key.Length descending
select gis.Key
).First();
Here is my version. It isn't a single LINQ expression, but does use only LINQ. It does return all same length subsequences if there are multiple answers. It should work any type of sequence. It was written to only use standard LINQ methods.
It uses GroupBy with a string key to implement a sequence Distinct. (Because of this trick, lists that contain items with commas might not work right.) In production code, I would use a Distinct with an IEqualityComparer for sequences based on SequenceEqual. It also has a separate step for finding the maximum repeated sequence length and then finding all the matching sequences, in production code I would use a MaxBy extension.
Update: Since I was using GroupBy for DistinctBy, I realized I could just use that to count the subsequence repeats directly rather than search for them.
var repeaters = Enumerable.Range(0, words.Count) // starting positions
.SelectMany(n => Enumerable.Range(1, (words.Count - n) / 2).Select(l => words.Skip(n).Take(l).ToList())) // subseqs from each starting position
.GroupBy(s => String.Join(",", s), (k, sg) => new { seq = sg.First(), Repeats = sg.Count() }) // count each sequence
.Where(sr => sr.Repeats > 1) // only keep repeated sequences
.Select(sr => sr.seq); // no longer need counts
var maxRepeaterLen = repeaters.Select(ss => ss.Count()).Max(); // find longest repeated sequence's length
var maxLenRepeaters = repeaters.Where(ss => ss.Count() == maxRepeaterLen); // return all sequences matching longest length
I want to sort a large integer array into 2 groups, i.e. 1 group the multiples of 4 and the other group the multiples of 5. How can I do this using just one query? Keep an eye on the performance which is really important in my case.
To further explain what I need, suppose my list of numbers is { 2, 7, 8, 10, 12, 14, 19,20, 25} then I would expect my output to be this:
new[]
{
new
{
Remainder = 4,
Numbers = new List<int>(){ 8, 12, 20}
},
new
{
Remainder = 5,
Numbers = new List<int>(){10, 20, 25}
}
}
Here's what I have gotten so far:
var numberGroupsTimes5 =
from n in numbers
group n by n % 5 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
var numberGroupsTimes4 =
from n in numbers
group n by n % 4 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
As you can see it gets me close with 2 queries but as I said I would like a single query.
You could use Concat:
var something = numberGroupsTimes5.Concat(numberGroupsTimes4);
to simply concatenate two sequences.
It's not entire clear why you use a GroupBy, then filter for Key == 0. Remainder will always be 0.
Maybe a simple Where is enough?
You can simply "combine" your queries by using a logical OR (||):
var something = numbers.Where(x => x%4 == 0 || x%5 == 0);
In response to your comment: Are you looking for something like this?
var result = new[] {4, 5}
.Select(d => new
{
Divider = d,
Values = numbers.Where(n => n % d == 0).ToList()
});
Do you mean?
var numberGroupsTimes4or5 = from n in numbers
group n by n into g
where g.Key % 4 == 0 || g.Key % 5 == 0
select new { Remainder = g.Key, Numbers = g };
Maybe this?
var result = new[] { 4, 5 }
.SelectMany(x => numbers.Select(n => (n, x)))
.Where(g => g.n % g.x == 0)
.GroupBy(g => g.x, (Key, g) =>
new { Remainder = Key, Numbers = g.Select(z => z.n) });
which gives this result
Here is a similar approach but this time using a query syntax like in your question.
var numbersAndRemainders = new[] { 4, 5 }
.SelectMany(rem => numbers.Select(n => (n, rem)));
var numberGroups =
from n in numbersAndRemainders
group n by new { remainder = n.n % n.rem, n.rem } into g
where g.Key.remainder == 0
select new { Remainder = g.Key.rem, Numbers = g.Select(z => z.n) };
There are two LINQ methods you could use for this:
//This will join the lists, excluding values that already appear once
var result = numberGroupsTimes5.Union(numberGroupsTimes4)
//This will simply append one list the the other
var result = numberGroupsTimes5.Concat(numberGroupsTimes4)
Suppose I have:
var correctOrder = new[] {2, 1, 0};
var actualPositionsFound = new[] {63,62,61];
How can I easily convert actualPositionsFound to a zero based sequence?
So if I had:
var actualPositionsFound = new[] {100,50,200];
I would like to end up with :
var result = new[] {1,0,2};
Update: In an attempt to make this clearer to avoid closing, what I believe is being asked for is to translate a list of numbers into another list of numbers representing the ascending order of the other list like a sort map, 0-based.
So { 16, 19, 2, 4 } would create a map { 2, 3, 0, 1 }, being 0-based.
If there are no duplicates:
var actualPositionsFound = new[] { 100, 50, 200 };
var indices = actualPositionsFound.OrderBy(n => n)
.Select((n, i) => new { n, i })
.ToDictionary(o => o.n, o => o.i);
var result = actualPositionsFound.Select(n => indices[n]).ToList();
Is it that you are looking for?
actualPositionsFound.Select((elem, idx) => new { elem, idx })
.OrderBy(wrap => wrap.elem)
.Select((wrap, idx) => new { wrap.idx, newIdx = idx })
.OrderBy(wrap => wrap.idx)
.Select(wrap => wrap.newIdx)
.ToArray();
actualPositionsFound
.OrderBy(x => x).ToList()
.Select(x => Array.IndexOf(actualPositionsFound,x)).ToArray();
This won't handle duplicates.
Is there a way that I could return duplicate values from an array in C#? also im looking to write a small algorithm that would return the most number of duplicate values in an array. for example
[1, 2,2,2 3,3] I need to return the duplicate values with the most number of occurrences and the number of occurrences as well.
I think I saw some post which said that It could be done using Linq but I have no clue what Linq is
Any help would be much appreciated.
Try this:
int[] data = new int[] { 1, 2, 2, 2, 3, 3 };
IGrouping<int, int> mostOccurrences = data
.GroupBy(value => value)
.OrderByDescending(group => group.Count())
.First();
Console.WriteLine("Value {0} occurred {1} time(s).", mostOccurrences.Key, mostOccurrences.Count());
Note that if multiple values occur the same number of times (such as if you added another 3 to that list), the above code will only list one of them. To handle that situation, try this:
int[] data = new int[] { 1, 2, 2, 2, 3, 3, 3 };
var occurrenceInfos = data
.GroupBy(value => value)
.Select(group =>
new {
Count = group.Count(),
Value = group.Key
}
);
int maxOccurrenceCount = occurrenceInfos.Max(info => info.Count);
IEnumerable<int> maxOccurrenceValues = occurrenceInfos
.Where(info => info.Count == maxOccurrenceCount)
.Select(info => info.Value);
foreach (int value in maxOccurrenceValues)
Console.WriteLine("Value {0} occurred {1} time(s).", value, maxOccurrenceCount);
Here's my take on this:
var data = new[] { 1, 2, 2, 2, 3, 3, };
var occurences =
data
.ToLookup(x => x)
.ToDictionary(x => x.Key, x => x.Count());
var mostOccurences =
occurences
.OrderByDescending(x => x.Value)
.First();
These will give you the following results: