Could Reactive Extension capture/detect consecutive cases? - c#

Use this, Rx generates a series of random numbers between 0 and 99.
var R = new Random();
var ints = Observable.Interval(TimeSpan.FromSeconds(1));
var RandomNos = ints.Select(i=> R.Next(100)); // was new Random().Next(100)
RandomNos.Subscribe(r=> Console.Write(r+ ","));
1,75,49,23,97,71,45,19,93,66,40,14,88,62,36,10,84
I want to capture/detect when I get 6 more-than-50 numbers in a row. Can Rx do it?
RandomNos.?????()
.Subscribe(l=> Console.WriteLine ("You got 6 more-than-50 numbers in a row"));

One way to do this is with the Buffer method.
var random = new Random();
var result = Observable.Interval(TimeSpan.FromSeconds(1))
.Select(i => random.Next(100))
.Buffer(6, 1)
.Where(buffer => buffer.All(n => n > 50))
If instead of 6-in-a-row you were trying to detect K-in-a-row, where K was really really huge, then you'd probably want to do something using Window instead, but since K = 6 here it's easiest to just do what I suggested.
Also, be aware that the probability of a number drawn uniformly from {0, 1, ..., 99} being greater than 50 is 49/100, not 1/2.

Just for fun, here's one that only uses a single counter for any "n" in a row - it keeps a running count of numbers over 50 - the Take(1) completes the stream at the first occurrence.
RandomNos.Scan(0, (a,x) => x > 50 ? ++a : 0)
.Where(x => x == 6)
.Take(1)
.Subscribe(_ => Console.WriteLine("You got 6 more-than-50 numbers in a row"));

Related

How would i find a range of numbers inside of an array that suit a condition?

I'm trying to understand arrays better and I've found this piece of code on this website that fills an array with a load of random numbers. I was wondering how you would go about, say, extracting a range of numbers. So if i wanted to find out how many of these random numbers inside of the array, were between 25 and 50, how would i go about doing this? I've heard about the Array.FindAll<> however i have no clue on how to use it.
Thank you in advance.
Random r = new Random();
int count = 100;
// Create an array with count elements.
int[] numbers = new int[count];
// Loop over each index
for (int i = 0; i < count; i++)
{
// Generate and store a random number at current index
numbers[i] = r.Next(1, 100);
}
Using LINQ you can try the following:
var numbersInRange = numbers.Where(number => number > 25 && number < 60)
.ToArray();
The above will filter out all the numbers in the range (25,60). If you want to get also the numbers that are equal to either 25 or 60, you just have to use >= and <= repsectively.
If you don't want to use LINQ (which I couldn't explain), you could try the following:
var numbersInRange = new List<int>();
foreach(var number in numbers)
{
if(number > 25 && number < 60)
{
numbersInRange.Add(number);
}
}
So at the end you would have a list that would contains your numbers.
Comparing this to the LINQ version, I think that LINQ is the winner in simplicity and readability.
Using Linq it is a single line
int[] inRange = numbers.Where(x => x >= 25 && x <= 50).ToArray();
The Enumerable methods are a collection of methods that operates on sequences of values and apply a predicate expression (x => 25 && x <= 50) to each member (x) of the sequence (numbers). In your particular case the Where method filters the sequence based on the result of the boolean expression (the return includes the members where the expression returns true).
We can use Linq. First have using System.Linq in your namespaces so the extension methods are available.
To find the numbers:
numbers.Where(x => x >= 25 & x <= 50)
This takes a lambda expression (in this case x => x >= 25 & x <= 50) that takes on object of the type in the sequence (in this case int) returns bool and then evaluates for that expression for all contained items, filtering out those that do not.
You can use ToArray() on the result to get it back into an array, or just work on the results directly.
To get which numbers, that is to say, which indices the matches where at, you can use:
numbers.Select((x, i) => new {El = x, Idx = i}).Where(x => x.EL >= 25 & x.El <= 50).Select(x => x.Idx)
This first creates a new anonymous object for each item where El is the value held and Idx is the index it was at. Then it does the same Where as before, but on the El property of the object we created in the first step. Finally it extracts out the Idx property of the survivors so we have all of the matching indices.
Although "Linq" can do the job as shown by other answers ; there is also, as you heard about, Array.FindAll which behaves the same but already returns an array (and so doesn't need a call to ToArray)
int[] matchedNumbers = Array.FindAll (currentNumber => 25 <= currentNumber && currentNumber <= 50);
As for the "strange" arrow notation it's a lambda expression

Randomly pick a specific int from a 2D Array

I'm trying to randomly get a specific integer (1) from a 2D array list filled with 0's and not many 1's. I made this, and it works:
while (wallsList[randomX, randomY] != 1)
{
randomX = randomizer.Next(34);
randomY = randomizer.Next(34);
}
The downside of it, it's that it takes too much time to just find one time the int (1), and I have to do this process over 1000 times since new 1's get added and removed to the 2D array each time. It takes about 3m to launch my program so I would like to know if there is an optimized version of this, I searched a lot and only found this solution for 1D arrays. Thanks for your time.
You have a sparse array. Why not represent it as a list of X/Y int pairs? Then, if the X/Y int pair is in the list, it's a 1, if not, it's a 0.
Then, to find a random value/cell containing 1, you just pick a random value from the list.
You could use a list like
new List <Tuple<int, int>> { new Tuple<int, int>(1, 5), new Tuple<int, int>(2, 7) }
Since most of your random guesses will fail, it would be far more efficient to build a second array of known good indexes and randomly search only those.
var randomizer = new Random();
var wallsList = new int[34, 34];
wallsList[23,11] = 1;
// Build an array of points that are known to pass
var knownHits =
(from x in Enumerable.Range(0, 34)
from y in Enumerable.Range(0, 34)
where wallsList[x, y] == 1
select new { x, y })
.ToArray();
// Pick a random point from previous array
var randomPoint = knownHits[randomizer.Next(knownHits.Length)];
var randomX = randomPoint.x;
var randomY = randomPoint.y;
Console.Write($"X = {randomX}, Y = {randomY}"); // X = 23, Y = 11
Alternatively, you could build the secondary array like this:
var knownHits = wallsList.Cast<int>()
.Select((v, i) => new { v, x = i / 34, y = i % 34 })
.Where(x => x.v == 1)
.ToArray();
There are a few approaches you can take. One would be to change your representation from a 2d array into something like a list of pairs of coordinates. Now selecting one at random is easy, but maybe some other operations you want to perform become harder. This approach and yours have the advantage that it selects one of the 1's uniformly. Another approach that would sacrifice this quality would be to choose a random x and y and then return the next 1, scanning by x or y. This is also not as efficient as the first solution.
I'm trying to randomly get a specific integer (1) from a 2D array list filled with 0's and not many
If you must do it randomly, your approach is fine. However, what you can improve it by avoiding drawing the same index again :
var randomizer = new Random();
var wallsList = new int[34, 34];
wallsList[01, 23] = 1;
var indexes =
from x in Enumerable.Range(0, 34)
from y in Enumerable.Range(0, 34)
select new { X = x, Y = y };
var result = indexes
.OrderBy(_ => randomizer.Next())
.FirstOrDefault(index => wallsList[index.X, index.Y] == 1);
if (result == null)
throw new Exception("Index not found");
Console.WriteLine("1 is found at[{0}, {1}]", result.X, result.Y);

C# resulting sequence using Where clause

I was doing some work by my own for upcoming test and found this question that I could not understand.
int[] div = new int[]{2,3,5};
IEnumerable<int>seq = new int[]{10,15,20,25,30};
for(int i = 0; i<div.Length;i++){
int y = div[i];
seq = seq.Where(s => s%y == 0)
}
seq = seq.ToList();
I thought the resulting sequence would be 10 15 20 25 30 but the actual answer was 30.
I tried to run the code by my self and I found that when the code runs the for-loop, the sequence does not resets to the original but it keeps the sequence created by the previous div[i].
For example, when i = 0, div[0] is 2 so the seq picks the 10, 20 and 30.
As the code proceeds to i = 1 and div[1] = 3, the sequence it uses for the calculation part is still {10,20,30} not {10,15,20,25,30}.
Can someone explain why?
When I move int y to the outside of the for-loop like this,
int[] div = new int[]{2,3,5};
IEnumerable<int>seq = new int[]{10,15,20,25,30};
int y;
for(int i = 0; i<div.Length;i++){
y = div[i];
seq = seq.Where(s => s%y == 0)
}
seq = seq.ToList();
it gives the answer I was expecting.
Any help will be really helpful.
Thank you.
This is the resulting query you end up with:
IEnumerable<int> seq = new int[]{10,15,20,25,30};
seq = seq
.Where(s => s%2 == 0)
.Where(s => s%3 == 0)
.Where(s => s%5 == 0);
So, every number that is divisible by 2, 3 and 5:
10 is not divisible by 3
15 is not divisible by 2
20 is not divisible by 3
25 is not divisible by 2
30 is divisible by 2 (15), 3 (10), and 5 (6)
So 30 is the only result.
Now, if you instead want this query: "I want every number in seq that is divisible by 2, 3 or 5", then you need to do it different.
The code you had will filter every step of the way, only letting through to the next step any numbers that fit, so each will filter, but you want the opposite, if any one of them would say that it was OK, then you want the number.
To get the or expression instead, this would be the code:
seq.Where(s => div.Any(y => s % y == 0))
This gives this result:
10
15
20
25
30
Basically this query says:
Any number s in seq, which for any number y in div, where s is divisible by y.
As for your last part, why did it change after you moved the y outside the loop?
Well, let's see what actually happens.
When you have the y inside the loop, as in the first example, for each iteration through the loop, you can think of y as a "new y just for this loop iteration".
As such, you can think of the code that is being executed producing this query:
int y1 = 2;
int y2 = 3;
int y3 = 5;
seq = seq
.Where(s => s%y1 == 0)
.Where(s => s%y2 == 0)
.Where(s => s%y3 == 0);
But, if you move the y variable outside the loop you only ever have one y, which you change, so the final result is this:
int y = 5; // the last number in the loop
seq = seq
.Where(s => s%y == 0)
.Where(s => s%y == 0)
.Where(s => s%y == 0);
Which basically checks that every number is divisible by 5, 3 times.
So while you say that this gives you the result you want, it doesn't do what you want. Or rather, I assume it doesn't do what you want since you haven't actually described what you wanted the code to do.
Here are some links that explain the differences:
Anonymous Methods (MSDN)
Captured variable in a loop in C#
Detailed Explanation of Variable Capture in Closures
You code can be simplified by adding some more LINQness.
If what you are trying to get is the values of seq that can be divided by any of the values of div, you can simply do:
seq.Where(s => div.Any(d => s%d == 0));
If you want the values of seq that can be divided by all of the values of div, replace the .Any with .All.

Find First Unique Number

I recently took a Codility test and the question was to find the first unique number in a numeric sequence. Although I get the correct result using LINQ, it is apparently too expensive computationally and not scalable enough.
How would I improve my solution?
var a = new int[] {1, 2, -3, 4, 5, -6, 0, 8, 9, 1, 2};
const int expected = -3;
var retVal = -1;
var y = a.GroupBy(z => z).Where(z => z.Count() == 1).Select(z => z.Key).ToList();
if (y.Count > 0) retVal = y[0];
Console.Write(retVal==expected);
How about this:
var result = a.ToLookup(i => i).First(i => i.Count() == 1).Key;
This should give -3.
It builds a Lookup object with a key created using each number in the list, and a value of the same number.
In the case of duplicates, then multiple entries are created under each key. The first unique value will be the first group in the Lookup with one entry.
(You could just as easily use GroupBy, instead of ToLookup. The end result will be the same.)
try this
var result = a.GroupBy(g => g).Where(w => w.Count() == 1).Select(s => s.Key).FirstOrDefault();
I decided to test the various answers posted above and count the number of ticks using the StopWatch class.
Changing y.Count > 0 to y.Any() resulted in the second lowest tick count, but the largest reduction belongs to the group by comment from Baldrick
To get the average each test was run 50 times.
Ticks
Min Max Avg Actual
2 14502 584 -3 Original
2 919 40 -3 if (y.Any()) actual = y[0];
2 1423 60 -3 a.GroupBy(g => g).Where(w => w.Count() == 1)
.Select(s => s.Key)
.FirstOrDefault();
2 1553 65 -3 a.ToLookup(i => i).First(i => i.Count() == 1).Key;
2 317 15 -3 a.GroupBy(i => i).First(i => i.Count() == 1).Key;

How can I group odd and even using LINQ?

I'd like to use LINQ to group my odd and even numbers in two separate arrays.
int[] randNum = randomNums(20, 1000, 1000);
var oddEvnNums = from num in randNum
group ???
select new { odd = oddNums[], even = evnNums[]}
int oddNum = oddEvnNums.odd[0];
int evenNum = oddEvnNums.even[0];
Just create a lookup with a key of num & 1 (equivalent to num % 2 for positive numbers, but sticking with 1 instead of -1 for negative numbers):
var lookup = randNum.ToLookup(num => num & 1);
var even = grouped[0].ToArray();
var odd = grouped[1].ToArray();
Note that a lookup will return an empty sequence if you ask for a key which doesn't have any entries, so you don't need to worry about whether or not there are odd and even numbers.
Alternatively, instead of grouping, you could just potentially go through the whole sequence twice:
var even = randNum.Where(num => (num & 1) == 0).ToArray();
var odd = randNum.Where(num => (num & 1) == 1).ToArray();
That only works if the sequence is the same both times (rather than being randomly generated each time you iterate, for example) but it may be more efficient than the grouping approach.
just group by the modulo
from num in randNum
group num by (num % 2) == 0

Categories

Resources