I want to generate an array that has 144 number from 1->36 in random order (so each number is repeated 4 times). Can we use Enumerable.Repeat and Enumerable.Range to do that. If yes than please explain to me how?
Well, creating the sequence with all the numbers in is easy:
var items = from x in Enumerable.Range(1, 36)
from y in Enumerable.Repeat(x, 4)
select y;
Then you can just use ToArray to get it into an array and shuffle it. There are numerous questions about shuffling an array in C# on SO, such as this one. You could either use that code directly, or call ToArray and shuffle the array in place without yielding it at the end.
int[] numbers = Enumerable.Range(0, 144).Select(i => (i % 36)+1).OrderBy(g => Guid.NewGuid()).ToArray();
// Generate the list (not in random order)
var one_to_36 = Enumerable.Range(1, 36);
var lst = one_to_36.Concat(one_to_36).Concat(one_to_36).Concat(one_to_36).ToList();
// Randomize the list by swapping random elements
Random rnd = new Random();
for(int i = 0; i < lst.Count; i++)
{
int i1 = rnd.Next(lst.Count);
int i2 = rnd.Next(lst.Count);
int tmp = lst[i1];
lst[i1] = lst[i2];
lst[i2] = tmp;
}
var seq = Enumerable.Range(0, 144);
var all = seq.ToList();
var random = new Random();
var result = seq.Select(i => {
var index = random.Next()%all.Count;
var r = all[index] % 36 + 1; all.RemoveAt(index);
return r;
}).ToList();
Related
For instance I have an array that gets filled with random numbers and am going to call this one dice.
Random rnd = new Random()
int[] dice=new int [5]
for (int i=0;i<dice.length;i++)
{
dice[i]= rnd.next(1,7)
}
Now for the sake of simplicity I wanna ask how can I find out if got a three of a kind of instance.
use a IDictionary<int,int>
var dict = new Dictionary<int,int>();
foreach (int i in dice)
if(!dict.ContainsKey(i))
dict.Add(i,1);
else dict[i]++;
(optional) you can use Linq to get the numbers that appear multiple times
var duplicates = dict.Where( x=>x.Value > 1 )
.Select(x=>x.Key)
.ToList();
// preparation (basically your code)
var rnd = new Random();
var dice = new int[5];
for (int i=0; i < dice.Length; i++)
{
dice[i]= rnd.Next(1,7);
}
// select dices, grouped by with their count
var groupedByCount = dice.GroupBy(d => d, d => 1 /* each hit counts as 1 */);
// show all dices with their count
foreach (var g in groupedByCount)
Console.WriteLine(g.Key + ": " + g.Count());
// show the dices with 3 or more
foreach (var g in groupedByCount.Where(g => g.Count() >= 3))
Console.WriteLine("3 times or more: " + g.Key);
To give a completely different approach, instead of:
Random rnd = new Random();
int[] dice=new int[5];
for (int i=0;i<dice.length;i++)
{
dice[i]= rnd.next(1,7);
}
Try this:
Random rnd = new Random();
int[] valueCount = new int[6];
for (int i=0; i<5; i++)
{
valueCount[rnd.next(0,6)]++;
}
//you have kept track of each value.
if (valueCount.Any(c => c == 3))
//3 of a kind
Of course you can combine both....
Do note that this works for a really specific rule engine, optimized for counting events.
If you really want a card/dice game, you'll need to rethink the rule engine to coupe with rules like "is it: 1,2,3,4,5,6, and in that order?".
For that, try: How to implement a rule engine?
I tried to find 2 or more same elements from array x and then that duplicate to add into new array Y
So if i have in x array number like: 2,5,7,2,8 I want to add numbers 2 into y array
int[] x = new int[20];
Random rnd = new Random();
int[] y = new int[20];
int counter = 0;
for (int i = 0; i < x.Length; i++)
{
x[i] = rnd.Next(1, 15);
for (int j=i+1; j< x.Length; j++)
{
if (x[i] == x[j])
{
y[counter] = x[i];
Console.WriteLine("Repeated numbers are " + y[counter]);
counter++;
}
else
{
Console.WriteLine("There is no repeated numbers, numbers that are in x are " + x[i]);
}
break;
}
}
But having problems with that, when it come to the if loop it doesn't want to proceed with executing if loop (even if condition is true)
If someone could give me some suggestion, that would be helpful, thank you
There are various logical errors in your use of for. You should work more on your logic, because while libraries can be learnt by rote, logical errors are more something that is inside you.
int[] x = new int[20];
Random rnd = new Random(5);
// You don't know the length of y!
// So you can't use arrays
List<int> y = new List<int>();
// First initialize
for (int i = 0; i < x.Length; i++)
{
x[i] = rnd.Next(1, 15);
}
// Then print the generated numbers, otherwise you won't know what numbers are there
Console.WriteLine("Numbers that are in x are: ");
for (int i = 0; i < x.Length; i++)
{
Console.WriteLine(x[i]);
}
// A blank line
Console.WriteLine();
// Then scan
for (int i = 0; i < x.Length; i++)
{
for (int j = i + 1; j < x.Length; j++)
{
if (x[i] == x[j])
{
y.Add(x[i]);
Console.WriteLine("Repeated numbers is " + x[i]);
}
}
}
// Success/failure in finding repeated numbers can be decided only at the end of the scan
if (y.Count == 0)
{
Console.WriteLine("There is no repeated numbers");
}
I've put some comments in the code (plus the changes)
And for debugging purpose, I suggest you use a fixed Random sequence. new Random(5) (or any other number) will return the same sequence every time you launch your program.
Note that if there are multiple repetitions of a number, like { 4, 4, 4 } then the y array will be { 4, 4 }
at first:
why do u use the 'break;' ?
second:
in the first for - loop u assign a random number to x[i]
but then in the nested second loop
u already ask x[j] to check for same values (but that doesn't exist yet)
there are so many ways to check if values are equal,
but i like your approach:
so what i would suggest:
make a for - loop and assign all the random numbers to int[] x
then think again how u can evaluate
x[0] = x[1] or x[2] or x[3] ...
Try to use Linq to find the duplicate in the Array
int[] x = new int[] { 2, 5, 7, 2, 8 };
int[] y;
var result = x.GroupBy(item => item)
.Select(grp => new { key = grp.Key, Count = grp.Count() });
y = result.Where(res => res.Count > 1).Select(res => res.key).ToArray();
int[] array = new int[5] {1,2,3,4,4};
List<int> duplitcateList = array.Where(x => array.Where(y => y == x).Count() > 1).Distinct().ToList();
or you can replace last line of above code with below.
List<int> duplitcateList = array.
GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
above code is using Linq.
suppose your first array (in question x) is array.
Linq will first check for all elements in to list which occur more then once, and select them distinctly and store it to duplicateList
if you need an array at the, you can simply convert this list to array by doing this,
int[] yArray = duplitcateList.ToArray();
Make use of linq in your code , as below
//first populate array x
var duplicates= xArray.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => y.Key)
.ToArray();
linq query above make use of groupby and find duplicate i.e. element occuring more then one time in you array and then you select those element and return result
I think this will will be the most understandable solution without complicated extension methods:
int[] x = new int[20];
// there can be at most 10 duplicates in array of length of 20 :)
// you could use List<int> to easily add elements
int[] y = new int[10];
int counter = 0;
Random rnd = new Random();
// fill the array
for (int i = 0; i < x.Length; i++)
x[i] = rnd.Next(1, 15);
// iterate through distinct elements,
// otherwise, we would add multiple times duplicates
foreach (int i in x.Distinct())
// if the count of an elements is greater than one, then we have duplicate
if(x.Count(n => n == i) > 1)
{
y[counter] = i;
counter++;
}
Lets say I have an array of integers. I found out that I can randomize the order of the elements simply by doing:
Random rnd = new Random();
array = array.OrderBy(x => rnd.Next()).ToArray();
But lets say I want to keep the first and the last elements in their original place. Can I do it using the same approach (using OrderBy()) or do I need to re-think my situation?
You can't do that in a single expression, but maybe a List<int> could help:
Random rnd = new Random();
var list = new List<int>();
list.Add(array[0]);
var partialArray = array.Skip(1).Take(array.Length - 2);
list.AddRange(partialArray.OrderBy(x => rnd.Next()));
list.Add(array[array.Length -1 ]);
You can do it:
int[] ints = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ints = ints.Select((x, index) => new { Value = x, Index = index })
.OrderBy(tuple => (tuple.Index >= start && tuple.Index <= stop) ? random.Next(start, stop) : tuple.Index)
.Select(tuple => tuple.Value)
.ToArray();
Of course you can use the same approach, but you have to take care of the first and the last value. Just an example for your input array:
var list = array.Skip(1).Take(array.Length - 2).OrderBy(x => rnd.Next()).ToList();
list.Insert(0, array.First());
list.Add(array.Last());
array = list.ToArray();
Dmitry's example is basically the same, but gives you the option to keep more elements.
You can just use a standard shuffle algorithm modified to use a range, for example (using the Fisher-Yates algorithm):
public static void Shuffle<T>(IList<T> array, Random rng, int start, int end)
{
for (int n = end+1; n > start+1;)
{
int k = rng.Next(start, n);
--n;
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
}
Then call it like this:
Random rng = new Random();
for (int i = 0; i < 100; ++i)
{
var array = Enumerable.Range(1, 12).ToArray();
Shuffle(array, rng, 3, 9);
Console.WriteLine(string.Join(", ", array));
}
A slightly different approach which doesn't involve using List....
Random rnd = new Random();
//array = array.OrderBy(x => rnd.Next()).ToArray();
int lastIndexToChange = array.Length - 1;
for (int i = 1; i < lastIndexToChange; i++)
{
var tempStore = array[i];
int newPosition = rnd.Next(1, lastIndexToChange);
array[i] = array[newPosition];
array[newPosition] = tempStore;
}
I feel like this task should not be done this way...
My sequenceY length is not equal to number of steam numbers because you can't assign int[] length with int that have 0 as a starting value.
Therefore my sequenceY have a lot of 0 inside and I can't print the whole sequence. I even tried adding this after for loop:
sequenceY = new int[steamnumbercounter];
But it didn't work ... Why ?
My other question is how do programmers deal with sequences that have unknown length?
I managed to print only steam numbers but the task says print sequenceY not only part of it.
// 4. sequenceX[20] is made of random numbers from 1 to 30 ,
// sequenceY is made of steam numbers from sequenceX. Print sequneceY.
int[] nizx = new int[20];
int[] nizy = new int[20];
int n = 0;
int steamnumbercounter = 0;
Random rnd = new Random();
for (int i = 0; i < nizx.Length; i++)
{
nizx[i] = rnd.Next(1, 30);
if (nizx[i]%2==0)
{
nizy[n] = nizx[i];
n++;
steamnumbercounter++;
}
Console.Write("{0} , ", nizx[i]);
}
for (int i = 0; i < steamnumbercounter; i++)
{
Console.WriteLine("{0} , ",nizy[i]);
}
Partial code review along with an answer.
But it didn't work ... Why ?
That code didn't work because you're reassigning sequenceY to a completely new value.
My other question is how do programmers deal with sequences that have unknown length?
So, with that known we can do a few things here: create an array and use Array.Resize, use a List<T>, fill the initial array then swap it for one of the right size which is filled.
I'm going to assume a "steam" number is an even one.
Your naming is not good: nizx and nizy don't convey the meaning or line up with the problem.
I'm going to demonstrate the last option (since you stated that you don't know how to use many of the moderately complex parts of .NET in this class yet, which is fine): fill the initial array and swap it for a new one. This will run in O(n^2) time (sorta).
So, let's start with our source array.
int[] sequenceX = new int[20];
Next we'll define our destination array to be the same size as our source array. (This is the maximum number of values that could be stored in it, we'll shrink it later.)
int[] sequenceY = new int[sequenceX.Length];
Then we need a variable to hold how many numbers we found that meet our criteria:
int steamNumbers = 0;
And lastly, our Random.
Random random = new Random();
Then, we look through all our sequenceX as you did, but we'll update the logic a bit.
for (int i = 0; i < sequenceX.Length; i++)
{
sequenceX[i] = random.Next(1, 30);
if (sequenceX[i] % 2 == 0)
{
sequenceY[steamNumbers] = sequenceX[i];
steamNumbers++;
}
}
So our code looks almost the same as yours, but we have one more thing to do: since you only want sequenceY to contain steamNumbers we have to shrink it or something.
int[] tempSequenceY = sequenceY;
sequenceY = new int[steamNumbers];
for (int i = 0; i < steamNumbers; i++)
{
sequenceY[i] = tempSequenceY[i];
}
Now sequenceY only has your steam numbers in it.
Final code:
int[] sequenceX = new int[20];
int[] sequenceY = new int[sequenceX.Length];
int steamNumbers = 0;
Random random = new Random();
for (int i = 0; i < sequenceX.Length; i++)
{
sequenceX[i] = random.Next(1, 30);
if (sequenceX[i] % 2 == 0)
{
sequenceY[steamNumbers] = sequenceX[i];
steamNumbers++;
}
}
int[] tempSequenceY = sequenceY;
sequenceY = new int[steamNumbers];
for (int i = 0; i < steamNumbers; i++)
{
sequenceY[i] = tempSequenceY[i];
}
// Print your `sequenceY` here.
You could extract this to a method pretty easily as well:
public int[] GetSteamNumbers(int sequenceCount, int randomMinimum, int randomMaximum)
{
int[] sequenceX = new int[sequenceCount];
int[] sequenceY = new int[sequenceX.Length];
int steamNumbers = 0;
Random random = new Random();
for (int i = 0; i < sequenceX.Length; i++)
{
sequenceX[i] = random.Next(randomMinimum, randomMaximum);
if (sequenceX[i] % 2 == 0)
{
sequenceY[steamNumbers] = sequenceX[i];
steamNumbers++;
}
}
int[] tempSequenceY = sequenceY;
sequenceY = new int[steamNumbers];
for (int i = 0; i < steamNumbers; i++)
{
sequenceY[i] = tempSequenceY[i];
}
return sequenceY;
}
And then call it with:
int[] steamNumbers = GetSteamNumbers(20, 1, 30);
Of course, for the more advanced users (this doesn't help you, but it may help others) we can do something as follows using LINQ:
var random = new Random();
var sequenceY = Enumerable.Range(1, 20)
.Select(x => random.Next(1, 30))
.Where(x => x % 2 == 0)
.ToArray();
Which should have the same effect. (Just demonstrating that there are still things in C# to look forward to in the future.)
Disclaimer: I wrote this entire answer outside of the IDE and without actually compiling it, I make no guarantees to the accuracy of the code but the procedure itself should be fairly straight forward.
The thing with arrays in C# is that they are of fixed size.
You'll have to iterate through and re-create it or use a IEnumerable that has dynamic sizes, such as Lists.
Solution here would be to use a List that contains your integers and then you would use nizx.Add(rnd.Next(1, 30));
Elaborating on my comment above: You can create a 'fake' list by concatenating the values you need in a string, separated by commas. The string.Split(',') will give you the resulting array that you need.
Given a string of form "a,b,c,d" string.Split(',') will create the array ["a","b,"c","d"]. The code:
{
int[] nizx = new int[20];
string numberString = string.Empty;
int n = 0;
Random rnd = new Random();
for (int i = 0; i < nizx.Length; i++)
{
nizx[i] = rnd.Next(1, 30);
if (nizx[i] % 2 == 0)
{
numberString += nizx[i] + ",";
n++;
}
}
var numberArray = numberString.Split(',');
for (int i = 0; i < n; i++)
{
Console.WriteLine("{0} , ", numberArray[i]);
}
}
listBox1.Items.Clear();
int[] sayısal = new int[6];
Random rastgele = new Random();
for (int i = 0; i < 6; i++)
{
do
{
sayısal = rastgele.Next(1, 50);
}
while (listBox1.Items.IndexOf(sayısal) != -1);
listBox1.Items.Add(sayısal);
}
When I did like this, I take an error that calls
"Cannot implicitly convert type 'int' to 'int[]' "
in line "sayısal = rastgele.Next(1, 50);". What can I do for it?
You can generate sequence 1..50 and shuffle it (i.e. sort by random value):
Random rastgele = new Random();
int[] sayısal = Enumerable.Range(1, 50) // generate sequence
.OrderBy(i => rastgele.Next()) // shuffle
.Take(6) // if you need only 6 numbers
.ToArray(); // convert to array
Your code is not working, because you are trying to assign generated item to array variable.
sayısal = rastgele.Next(1, 50);
It should be instead:
do {
sayısal[i] = rastgele.Next(1, 50);
} while(listBox1.Items.IndexOf(sayısal[i]) != -1);
As I already pointed in comments, it's better to separate UI logic and array generation. I.e.
// generate array (optionally move to separate method)
int itemsCount = 6;
int[] items = new int[itemsCount]; // consider to use List<int>
Random random = new Random();
int item;
for(int i = 0; i < itemsCount; i++)
{
do {
item = random.Next(1, 50);
} while(Array.IndexOf(items, item) >= 0);
items[i] = item;
}
// display generated items
listBox1.Items.Clear();
for(int i = 0; i < items.Length; i++) // or use foreach
listBox1.Items.Add(items[i]);
Because Random.Next method returns an int, not int[]. And there is no implicit conersation from int[] to int.
Return Value
Type: System.Int32
A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.
If you want to fill your array, you can use Enumerable.Range like lazyberezovsky mentioned.
This method takes an integer array and randomly sorts them.
So fill an array with a loop then use this to randomly sort the array.
You should credit one of the others as they were first to post with valid answers. I just thought another way to do this would be good.
amount is the amount of times you want the array to randomize. The higher the number the higher the chance of numbers being random.
private Random random = new Random();
private int[] randomizeArray(int[] i, int amount)
{
int L = i.Length - 1;
int c = 0;
int r = random.Next(amount);
int prev = 0;
int current = 0;
int temp;
while (c < r)
{
current = random.Next(0, L);
if (current != prev)
{
temp = i[prev];
i[prev] = i[current];
i[current] = temp;
c++;
}
}
return i;
}
Be careful in your choice of data structures and algorithms, pick the wrong one and you'll wind up with O(n^2). A reasonable solution IMHO is a typed hash table (i.e. dictionary) which will give you O(n):
Random rnd = new Random();
var numbers = Enumerable
.Range(1, 1000000)
.Aggregate(new Dictionary<int, int>(), (a, b) => {
int val;
do {val = rnd.Next();} while (a.ContainsKey(val));
a.Add(val, val);
return a;
})
.Values
.ToArray();
Still not ideal though as performance depends on the array size being significantly smaller than the set of available numbers and there's no way to detect when this condition isn't met (or worse yet when it's greater, in which case the algorithm will go into an infinite loop).