I'm new to programming, and I'm reading a book on C#. This code is not outputting what I was expecting.
Here is the code:
public partial class Form1 : Form
{
static string stars = "****************************************************************";
const int MAXVAL = 52;
const int MAXELEMENTS = 100;
int[] data = new int[MAXELEMENTS];
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int i;
Random rd = new Random(5);
int j;
string buff;
for (i = 0; i < data.Length; i++)
{
data[i] = rd.Next(MAXVAL);
buff = " ";
for (j = 0; j < data[i]; j++)
{
buff += "*";
lstResult.Items.Add(data[i].ToString() + " " + buff);
}
}
}
}
Here is the output:
Why isn't there random numbers in an random order in the listview?
You are seeding the random instance always with the same number 5. That'll cause repeating numbers. You just have to use the default constructor:
Random rd = new Random();
Have a look at the example on MSDN which shows exactly this behaviour.
By the way, this is also a common pitfall, you should always reuse the same random instance instead of creating always a new one(in a loop) since the seed is created from the current time. If you create randoms very fast you'll get the same seed which causes repeating numbers.
Take out the 5 after the Random(), that is a seed value. Just use the default constructor:
Seed - A integer used to set the starting point for generating a series of random numbers. The seed sets the generator to a random starting point. A unique seed returns a unique random number sequence.
Because you are using 5 as a seed every time, you are getting repeating numbers
Random rd = new Random();
The output is logical if you do your Random.Next call outside the j loop and the Items.Add inside it.
I suggest moving the Items.Add call behind the j loop.
You are using the same seed (5) each time with this line Random rd = new Random(5);
Use the default constructor to generate a random number:
Random rd = new Random();
Declare your random class without seed.
Random rd = new Random();
Related
I want to check if there is same value in the array or not as I mentioned in the title. And if there is, I want to pass that value and check another random value to add to listbox.
In my form, there is 2 textBox, 1 listbox and 1 button. When button is clicked, listbox has to show random numbers up to sum of textbox1 and textbox2. For instance;
5 entered from textbox1 and 10 entered from textbox2. Sum is of course 15 and listbox has to show 15 random numbers but those numbers have to be different from each other.
I wrote something like that and used Contains method to check if there is same value or not. But the program froze and didn't give any error.
int a, b;
Random rnd = new Random();
int[] array;
private void button1_Click(object sender, EventArgs e)
{
a = Convert.ToInt32(textBox1.Text);
b = Convert.ToInt32(textBox2.Text);
int c = a + b;
array = new int[c];
for (int i = 0; i < array.Length; i++)
{
int number = rnd.Next(c);
foreach(int numbers in array)
{
if (array.Contains(numbers))
{
i--;
}
else
{
array[i] = number;
listBox1.Items.Add(array[i]);
}
}
}
I also did it without foreach(Only Contains part I mean). Also didn't work. I wrote in "else";
array[i] += number;
it also didn't work.
I would be very appreciated if you help me. Thanks in advance.
instead of a for loop, use a while loop:
int = 0;
while(i<c)
{
int random rnd.Next(c);
if(!array.Contains(random))
array[i++] = random;
}
you may also create a list of numbers from 1-15 and then shuffle them (as your random function will create only random numbers from 1-15 just random):
array = Enumberable.Range(0,c).OrderBy(x => rnd.Next()).ToArray();
The above code is much faster, because imagine that we have generated 14 random numbers and only one number (5 for instance) left, it has to go through loop several times so that finally random number that is generated equals to 5, but in the above code there is no need to check that, we just have all numbers and then we shuffle it.
You can try to use do...while instead of for loop
Random.Next get the value from 0 to c - 1, so rnd.Next(c + 1); need to add 1 otherwise, the loop will not be stopped.
var array = new int[c];
int number;
for (int i = 0; i < array.Length; i++)
{
do
{
number = rnd.Next(c + 1);
} while (array.Contains(number));
array[i] = number;
listBox1.Items.Add(array[i]);
}
You basically need to shuffle your data. Create a collection with all values:
var temp = Enumerable.Range(0, c);
Now order it by random
temp = temp.OrderBy(_ => rnd.Next());
Now you can add temp to your listBox
Or, as single line:
listBox1.Items.AddRange(Enumerable.Range(0, c).OrderBy(_ => rnd.Next()));
This is my random unique numbers generator I try to create for my cards software. It generates numbers and write into array OK. I have problem with the loop here. when integer i reaches 29, it stops growing and code cycles infinitely and never reaches 30, which would stop the loop.
Without the if statement it works, but it won't fill the range needed.
fixed the code, now works OK, the initial value in array was the problem. now I ged needed 0-29 values
public partial class Form1 : Form
{
int[] rndCards = new int[30];
public Form1()
{
InitializeComponent();
richTextBox1.Text = #"random numbers";
}
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
rndCards = new int[30];
richTextBox1.Clear();
Random rnd = new Random();
while (i < 30)
{
int cardTest = rnd.Next(0, 30);
while (rndCards.Contains(cardTest))
{
cardTest++;
if (cardTest == 31)
{
cardTest = 1;
}
}
rndCards[i] = cardTest;
i++;
}
i = 0;
while (i < 30)
{
rndCards[i] = rndCards[i] -1;
richTextBox1.Text += rndCards[i] + ", ";
i++;
}
}
}
You problem lies in the simple fact that the array already contains the number 0 when you create it (because each item of an array is initialized to the default value for its member's type) That's why you should start your i from 1 and not zero.
int i = 1;
Alternative Simpler Approach:
You can do this as a simple random number generation:
Random rnd = new Random();
rndCards = Enumerable.Range(0, 30).OrderBy(x => rnd.Next()).ToArray();
foreach(var card in rndCards)
{
// do something
}
rnd.Next(0,30) would return a random number from 0-29.
From the documentation for Random.Next(Int32, Int32):
The Next(Int32, Int32) overload returns random integers that range from minValue to maxValue – 1. However, if maxValue equals minValue, the method returns minValue.
Use int cardText = rnd.Next(0, 31);, and this should solve your issue.
The upper bound is exclusive (C# Random.Next - never returns the upper bound?).
int cardTest = rnd.Next(0, 31);
after reading some C# tutorials I decided to make a minigame of Heads/Tails. It generates a random number, 0 or 1, and writes out the result. Using a loop I repeat this a thousand times. The problem is that it would only write "heads" or "tails" depending on whether 0 or 1 gets generated more. For example if there are 535 "0s" and 465 "1s" it would only write down "Heads". Here is my code:
//variables
int count = 0;
int tails = 0;
int heads = 0;
while(count < 1000)
{
Random rnd = new Random();
int result = rnd.Next(0,2);
if(result == 1)
{
Console.WriteLine("Tails!");
tails = tails + 1;
count = count + 1;
}
else if(result == 0)
{
Console.WriteLine("Heads!");
heads = heads + 1;
count = count + 1;
}
}
Console.WriteLine("Heads = " + heads + "Tails = " + tails + " Counts = " + count);
Console.ReadLine();
Try moving Random rnd = new Random(); outside of the while loop:
Random rnd = new Random();
while (count < 1000)
//...
The problem is that there are no true random numbers in computers; they all work off of a list of previously-generated random numbers. Since you are instantiating Random every loop, you're essentially picking the same start seed every time. By using just one instance of Random, created outside of the loop, then your application will truly behave as if the numbers are being generated randomly.
EDIT: To echo what Solal Pirelli said in the comments, the instance of Random is actually being seeded using the current computer system's time (if you don't give it any seed value in the constructor); however, since the loop iterations are happening so quickly, each instance created for each loop iteration has the same seed.
EDIT #2: As CalebB pointed out, it's also a good practice to provide your own seed to your Random instance via its other constructor. I would suggest using the hash value from a GUID:
Random rnd = new Random(Guid.NewGuid().GetHashCode());
This essentially guarantees that each of your instances of Random will always be seeded differently, even if you create new instances in quick succession. I say essientially because, while statistically very VERY low in probability, there is some chance that two GUID values could be the same.
Fixed! :
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
public static void Main(string[] args)
{
Random rand = new Random();
Console.WriteLine(String.Join("\n",Enumerable.Repeat(0, 1000).Select(i => rand.Next(0,2) == 1 ? "Tails" : "Heads").GroupBy(i=>i).Select(g=> g.Key + " " + g.Count())));
Console.ReadLine();
}
}
}
I have a method which I am using to generate random strings by creating random integers and casting them to char
public static string GenerateRandomString(int minLength, int maxLength)
{
var length = GenerateRandomNumber(minLength, maxLength);
var builder = new StringBuilder(length);
var random = new Random((int)DateTime.Now.Ticks);
for (var i = 0; i < length; i++)
{
builder.Append((char) random.Next(255));
}
return builder.ToString();
}
The problem is that when I call this method frequently, it is creating the same sequence of values, as the docs already says:
The random number generation starts from a seed value. If the same
seed is used repeatedly, the same series of numbers is generated. One
way to produce different sequences is to make the seed value
time-dependent, thereby producing a different series with each new
instance of Random.
As you can see I am making the seed time dependent and also creating a new instance of Random on each call to the method. Even though, my Test is still failing.
[TestMethod]
public void GenerateRandomStringTest()
{
for (var i = 0; i < 100; i++)
{
var string1 = Utilitaries.GenerateRandomString(10, 100);
var string2 = Utilitaries.GenerateRandomString(10, 20);
if (string1.Contains(string2))
throw new InternalTestFailureException("");
}
}
How could I make sure that independently of the frequency on which I call the method, the sequence will "always" be different?
Your test is failing because the GenerateRandomString function completes too soon for the DateTime.Now.Ticks to change. On most systems it is quantized at either 10 or 15 ms, which is more than enough time for a modern CPU to generate a sequence of 100 random characters.
Inserting a small delay in your test should fix the problem:
var string1 = Utilitaries.GenerateRandomString(10, 100);
Thread.Sleep(30);
var string2 = Utilitaries.GenerateRandomString(10, 20);
You're effectively doing the same as Random's default constructor. It's using Environment.TickCount. Take a look at the example in this MSDN documentation for the Random constructor. It shows that inserting a Thread.Sleep between the initialization of the different Random instances, will yield different results.
If you really want to get different values, I suggest you change to a seed value that's not time-dependent.
dasblinkenlight has given why this is happening.
Now you should do this to overcome this problem
public static string GenerateRandomString(Random random , int minLength,
int maxLength)
{
var length = GenerateRandomNumber(random , minLength, maxLength);
var builder = new StringBuilder(length);
for (var i = 0; i < length; i++)
builder.Append((char) random.Next(255));
return builder.ToString();
}
public void GenerateRandomStringTest()
{
Random rnd = New Random();
for (var i = 0; i < 100; i++)
{
var string1 = Utilitaries.GenerateRandomString(rnd, 10, 100);
var string2 = Utilitaries.GenerateRandomString(rnd, 10, 20);
if (string1.Contains(string2))
throw new InternalTestFailureException("");
}
}
Generating a random password is easy. but generating a batch is more difficult.
public static string getRandomPassword(int letters, int getallen) {
//int letters = 8;
//int getallen = 5;
char[] letterdeel = new char[letters];
int minGetal = (int)Math.Pow(10, getallen - 1);
int maxGetal = (int)Math.Pow(10, getallen);
string password;
Random r = new Random();
int test = (int)(DateTime.Now.Ticks);
for (int i = 0; i < letters; i++) {
r = new Random((int)(DateTime.Now.Ticks) + i);
bool capital = r.Next(2) == 0 ? true : false;
if (capital) {
letterdeel[i] = (char)r.Next(65, 91);
} else {
letterdeel[i] = (char)r.Next(97, 123);
}
}
password = new string(letterdeel);
password += r.Next(minGetal, maxGetal);
return password;
}
this is my method, the passwords should be in a certain letter-number format.
this works fine, however if i have a for loop pulling 100 passwords from this method, in my array i have 5-8 the same passwords, then again 5-8 the same pass.
i know WHY this is, because of the random function and the clock it depends on, but how do i fix this?
Move Random r to outside the method if you are repeatedly calling it. You are going to be hitting it several times in the same relative timeframe, so you are going to be generating the same seeds. You also want to get rid of the line below. It is unnecessary, and (again), with the nature of DateTime.Now, you would just continue to generate the same sequence of "random" numbers.
r = new Random((int)(DateTime.Now.Ticks) + i);
Define your random number generator as a static outside of the function.
How can I get true randomness in this class without Thread.Sleep(300)?
Use a set rather than whatever collection you store into and don't loop 100 times but until the set has 100 items in it.