C#: How should I go about shuffling contents of an array? - c#

I have an array of integers called cards[52]. Each element contains an integer that represents a card (cards[0] = 5 would represent the 6 of clubs for example).
This is how I shuffled the array. It works, but I am getting repeats.
private void shuffleCards()
{
for (int i = 0; i < cards.Length; i++)
{
int randIndex = r.Next(0, 52);
int origCard = cards[i];
cards[i] = cards[randIndex];
cards[randIndex] = origCard;
}
}
r is the Random variable I initialized in the beginning of the program.
When testing the program, I noticed I would get the same card 2 or 3 times. Obviously I cannot get repeats, they all must be different. I can't see in any way how this method gives me repeats.
How can I shuffle this array without any repeats? Thanks.
EDIT
Okay, turns out the shuffle function wasn't the problem. It is the deal() function that is causing the problem.
private void deal()
{
for (int i = 0; i < 26; i++)
{
userCards[i] = cards[i];
setUserValue(); //ignore this
}
for (int i = 26; i < 52; i++)
{
opponentCards[i - 26] = cards[i];
setOpponentValue(); //ignore this
}
}
I know for sure it isn't the shuffle function. I printed the results of all the cards to a text file and saw no iterations. However, when the cards are dealt to the user and the opponent, that's when all the iterations occur. Any advice?

There is very simple algorithm known as Fisher Yates shuffling algorithm which does the shuffling in O(n) time and O(1) space complexity.
Use a random function that generates a random index from the given set and replace the random element generated with the last index. Decrement last index and continue like this for the rest of elements.
Let the array of size n be : arr[n]
void randomize ( int arr[], int n )
{
// Use a different seed value so that we don't get same
// result each time we run this program
srand ( time(NULL) );
// Start from the last element and swap one by one. We don't
// need to run for the first element that's why i > 0
for (int i = n-1; i > 0; i--)
{
// Pick a random index from 0 to i
int j = rand() % (i+1);
// Swap arr[i] with the element at random index
swap(&arr[i], &arr[j]);
}
}
Source : Algorithm

Related

Storing numbers between two numbers in an array

as you can read from the title I'm trying to store all the numbers between two numbers in an array.
For example store the numbers between 21 and 43 (22,23,24,25,26,27,28,29...) in an array.
This is the code, I don't know why but it prints only the higher number minus one.
class Program
{
static void Main(string[] args)
{
int higher = 43;
int lower = 21;
int[] numbers = new int[22]; //the numbers between 21 and 43 are 22
for (int i = lower; i < higher;i++)
{
for (int a = 0; a < 22; a++)
{
numbers[a] = i;
}
}
for (int c = 0; c < 22; c++)
{
Console.WriteLine(numbers[c]);
}
Console.ReadLine();
}
}
This is the code, I don't know why but it prints only the higher number minus one.
This question will attract answers giving you a half dozen solutions you can cut and paste to do your assignment.
I note you did not ask a question in your question -- next time, please format your question in the form of a question. The right question to ask here is how do I learn how to spot mistakes in code I've written? because that is the vital skill you lack. Answers that give you the code will not answer that question.
I already gave you a link to a recent answer where I explain that in detail, so study that.
In particular, in your case you have to read the program you wrote as though you had not written it. As though you were coming fresh to the program that someone else wrote and trying to figure out what it does.
The first thing I would do is look at the inner loop and say to myself "what does this do, in words?"
for (int a = 0; a < 22; a++)
{
numbers[a] = i;
}
That is "put the value i in every slot of the array. Now look at the outer loop:
for (int i = lower; i < higher;i++)
{
put the value i in every slot of the array
}
Now the technique to use here is to logically "unroll" the loop. A loop just does something multiple times so write that out. It starts with lower, it goes to higher-1, so that loop does this:
put the value lower in every slot of the array
put the value lower+1 in every slot of the array
…
put the value higher-1 in every slot of the array
What does the third loop do?
print every item in the array
And now you know why it prints the highest number minus one multiple times. Because that's what the program does. We just reasoned it out.
Incidentally the answers posted so far are correct, but some are not the best.
You have a technique that you understand for "do something to every member of an array, and that is:
loop an indexer from 0 to the array size minus one
do something to the array slot at the indexer
But the solutions the other answers are proposing are the opposite:
loop an indexer from the lower to the higher value
compute an index
do something to the array slot at that index
It's important to understand that both are correct, but my feeling is that for the beginner you should stick with the pattern you know. How would we
loop an indexer from 0 to the array size minus one
do something to the array slot at the indexer
for your problem? Let's start with giving you a much better technique for looping the indexer:
for (int i = 0; i < numbers.Length; ++i)
That's a better technique because when you change the size of the array, you don't have to change the loop! And also you are guaranteed that every slot in the array is covered. Design your loops so that they are robust to changes and have good invariants.
Now you have to work out what the right loop body is:
{
int number = i + lower;
numbers[i] = number;
}
Now you know that your loop invariant is "when the loop is done, the array is full of consecutive numbers starting at lower".
For everytime you loop through i, you put that number in every slot of the array. The inner loop is what is causing your issue. A better solution would be:
int higher = 43;
int lower = 21;
int[] numbers = new int[21];
int index = 0;
for (int i = lower + 1; i < higher; i++) // if you want to store everything
// between 21 and 43, you need to
// start with 22, thus lower + 1
{
numbers[index] = i;
index++;
}
for (int c = 0; c < 21; c++)
{
Console.WriteLine(numbers[c]);
}
Console.ReadLine();
Replace a with a direct translation of i
for (int i = lower; i < higher;i++)
{
numbers[i-lower] = i;
}
Use below
int higher = 43;
int lower = 21;
int[] numbers = new int[22]; //the numbers between 21 and 43 are 22
for (int i = lower+1; i < higher; i++)
{
numbers[i-lower] = i;
}
for (int c = 1; c < 21; c++)
{
Console.WriteLine(numbers[c]);
}
Console.ReadLine();
I think higher & lower are variables so following will give you output
for any higher and lower numbers
class Program
{
static void Main(string[] args)
{
int higher = 43;
int lower = 21;
int numDiff = higher - lower - 1;
int[] numbers = new int[numDiff]; //the numbers between 21 and 43 are 22
for(int i = 0; i<numbers.Length; i++)
{
numbers[i] = numDiff + i + 1;
}
for(int b = 0; b<numbers.Length; b++)
{
Console.WriteLine(numbers[b]);
}
Console.ReadLine();
}
}

Generating an array of numbers based off the smallest numbers in other arrays?

How do I go about making an array of numbers based off of the lowest values generated in other arrays? I have made an array that generates numbers between -1000 and 1000 and calculates the the lowest number from that; my problem comes after that I believe. I cant figure out how to add the lowest value to the "lowestNumbers" array.
static void Main(string[] args)
{
//ints and arrays used in the program.
int min = -1000;
int max = 1000;
int currentMinimum = 1000;
int[] numbers = new int[10];
int[] lowestNumbers = new int[numbers.Length];
Random rndm = new Random();
//Using a loop to create random numbers within numbers array between -1000 and 1000.
for (int i = 0; i < lowestNumbers.Length; i++)
{
if (i < lowestNumbers.Length)
{
for (int index = 0; index < numbers.Length; index++)
{
if (index < numbers.Length)
{
numbers[index] = rndm.Next(min, max);
}
}
for (int index = 0; index < numbers.Length; index++)
{
if (numbers[index] < currentMinimum)
currentMinimum = numbers[index];
}
}
lowestNumbers[i] = currentMinimum;
}
foreach (int value in lowestNumbers)
Console.WriteLine(value);
Console.WriteLine("//////////////////////////////////////////////////////////////////////////");
}
}
}
Use the power of Linq:
var numbers = Enumerable.Range(1, 100).Select(_ => rndm.Next(min, max)).ToArray();
var lowest = numbers.Min();
Based off the answer of #Laoujin, but slightly expanded a bit.
This will generate 1000 integers, from -1000 to 1000, and give you an array of the 100 lowest. I added 1 to max in the call to Random.Next(Int32, Int32) since maxValue is exclusive, otherwise the number 1000 would never show up in the resulting array.
var numbers = Enumerable.Range(1, 1000).Select(_ => rndm.Next(min, max+1)).ToArray();
var lowest = numbers.OrderBy(n => n).Take(100).ToArray();
As an extra note, if you are confused about the _ in the first line (many people are, the first time they see it used this way), that is just commonly used to denote an unused argument in a lambda expression. In this case, that variable contains the current number generated from Enumerable.Range() via the Select() call, but since it isn't being used, it is just named _. This isn't enforced by the language in any way, it is a common pattern that lots of programmers use.
That might contain duplicates though, so if you don't want them, you can pass the list though IEnumerable.Distinct() first, which returns only unique items:
var lowest = numbers.Distinct().OrderBy(n => n).Take(100).ToArray();
If you want the absolute lowest number from the first array, you can just do:
var lowestNumber = numbers.Min();

ArgumentsOutOfRangeExeption in a list<T>

Im getting this exeption thrown when the method is invoked. the list contains exactly 52 objects(number of cards).
Any suggestions what may cause it? maybe the Add and RemoveAt Methods? Or maybe the Random?
The compiler also tell the the problem is in deck.Add(temp[j]); line.
public void Shuffle()
{
List<Card> temp = new List<Card>();
Random rand = new Random();
for (int i = 0; i < 52; i++)
{
for (int j = rand.Next(i, 52); j < 52; j++)
{
temp.Add(deck[j]);
deck.RemoveAt(j);
deck.Add(temp[j]);
}
}
}
Ok, let's imagine we are on the first run through the loops. First iteration of both the outer and inner loop. i is 0, and Rand(i, 52) produces 13.
So we have:
i - 0
j - 13
temp - empty list
deck - assume this is a list with 52 elements
Now let's run the three lines of code inside the loop:
temp.Add(deck[j]);
Get the 13th item of deck and add it to temp. Ok, done. temp now has 1 item.
deck.RemoveAt(j);
Remove the 13th item in deck. Ok, fine.
deck.Add(temp[j]);
Get the 13th item in temp and add it to, wait, what?1? temp only has 1 item! Exception! Exception!.
There isn't a need for the temp list in the first place. There is a very good shuffling algorithm that involves going through the original list of items once (not N times). And you just need one temp variable to hold a value while you swap it with another. Like this:
public void Shuffle()
{
Card temp;
Random rand = new Random();
for (int i = deck.Length; i >= 1; i--)
{
int j = rand.Next(0, i + 1);
temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
}
Done.
If you want to shuffle list of items then you can use the following method:
public static void Shuffle<T>(IList<T> arr, Random rnd)
{
for (var i = 0; i < arr.Count; i++)
{
var j = rnd.Next(i, arr.Count);
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
This method will help you shuffle your deck without ArgumentsOutOfRangeExeption
temp[j] does not neccessarily exist. You will need to initialize temp so it has at least j+1 entries or you need to change the line of temp[j] to something more fitting.
When you call rand.Next(i, 52), the result could be 52, which would be out of range for your deck and temporary deck.
Also, you need to initialize your temp list, as #nvoigt points out. List has a constructor that takes an integer for the initial size. You could pass in 52. See: http://msdn.microsoft.com/en-us/library/dw8e0z9z(v=vs.110).aspx.
You could have also easily debugged this yourself by looking at the value of j in your debugger.

Im trying scramble randomaly strings but some of the strings dosen't get scrambled at all what could it be?

This is the code:
private static StringBuilder MakeRandomwords(string theWord)
{
var jumbleSb = new StringBuilder();
jumbleSb.Append(theWord);
int lengthSb = jumbleSb.Length;
for (int i = 0; i < lengthSb; ++i)
{
int index1 = (RandomGen.Next() % lengthSb);
int index2 = (RandomGen.Next() % lengthSb);
Char temp = jumbleSb[index1];
jumbleSb[index1] = jumbleSb[index2];
jumbleSb[index2] = temp;
}
return jumbleSb;
}
And this is the List that im using to build the scrambled words:
private void GetText()
{
_lengthaboveone = new List<string>();
for (int i = 0; i < _words.Count; i++)
{
string word = _words[i];
if (word.Length < 4) continue;
string first = word.Substring(0, 1);
string last = word.Substring(word.Length - 1, 1);
string middle = word.Substring(1, word.Length - 2);
_lengthaboveone.Add(middle);
_words[i] = first + MakeRandomwords(middle) + last;
}
_scrambledWords = _words;
}
In the end the List _scrambledWords contain over 1000 strings in each index a string of a word most of them scrambled but some of them are left the same as they were in the original.
The question is if there is something wrong with my MakeRandomwords ?
Could be it did scrambled the word and it was scrambled to how it was before ? So maybe i need to add something to the code that will keep scramble the word untill the word is scrambled by compraing it ot the original untill the word is scrambled ?
Take a look at the Fisher–Yates shuffle algorithm and implement the following pseudo code to achieve a good distribution of elements:
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]
To elaborate on Tim Schmelter's comment asking about RandomGen.Next(), in case you've been wondering: If you instantiate a new Random instance every time you're entering the for loop, the generated pseudo-random numbers will by nature be quite repetitive. This is due to the way that the Random class is implemented in the .NET framework. By reusing a shared instance like you do, one can avoid that issue.
This is not the problem here, though. In your algorithm, you're picking two random array elements and swapping them. It's highly likely that there are some array elements that never get selected this way. Thus, there's a good chance that some elements won't have changed their position in the array when you're done, which is why it doesn't look shuffled well.
Instead of randomly swapping two locations, swap every location to a random location... it will look more random because each element will have moved. With your current design there is a good chance some items won't ever move.
for (int i = 0; i < lengthSb; ++i)
{
int index1 = i;
int index2 = (RandomGen.Next() % lengthSb);
Char temp = jumbleSb[index1];
jumbleSb[index1] = jumbleSb[index2];
jumbleSb[index2] = temp;
}

Generate some unique numbers and put into array

I'm trying to create a method that produces 10 unique random numbers, but the numbers are not unique, I get several duplicates! How can I improve the code to work as I want?
int[] randomNumbers = new int[10];
Random random = new Random();
int index = 0;
do
{
int randomNum = random.Next(0, 10);
if (index == 0)
{
randomNumbers[0] = randomNum;
index++;
}
else
{
for (int i = 0; i < randomNumbers.Length; i++)
{
if (randomNumbers[i] == randomNum)
break;
else
{
randomNumbers[index] = randomNum;
index++;
break;
}
}
}
}
while (index <= 9);
foreach (int num in randomNumbers)
System.Console.Write(num + " ");
EDIT 2: I have corrected all errors now, but this code isn't working becuase the numbers are not unique! I have updated my code above to the latest version. I preciate some help to solve this! Thanks!
The simplest way to have this is to have an array of the numbers you want (i.e. 1-10) and use a random shuffling algorithm.
The Fisher-Yates shuffle is the easiest way to do this.
EDIT:
Here's a link to a C# implementation: http://www.dotnetperls.com/fisher-yates-shuffle
random.next(0, 10) returns a random number between 0 and 10. It can happen, that it returns the same number multiple times in a row and it is not guaranteed, that you get every number between 0 and 10 exactly one time, when you call it 10 times.
What you want is a list of numbers, every number is unique, between 0 and 9 (or 1 and 10). A possible solution to this would be something like this:
//Create the list of numbers you want
var list = new List<int>();
for(var x = 0; x < 10; x++)
{
list.Add(x);
}
var random = new Random();
//Prepare randomized list
var randomizedList = new List<int>();
while(list.Length > 0)
{
//Pick random index in the range of the ordered list
var index = random.Next(0, list.Length);
//Put the number from the random index in the randomized list
randomizedList.Add(list[index]);
//Remove the number from the original list
list.RemoveAt(index);
}
Explained in words, this is what you do:
Create the list with all the numbers, that your final list should contain
Create a second empty list.
Enter a loop. The loop continues, as long as the ordered list still has numbers in it.
Pick a random index between 0 and list.Length
Put this random number in the randomized list
Remove the item at the index position from the ordered list.
Like this you create the set of numbers you want to have in your randomized list and then always pick a random entry from the ordered list. With this technique, you can also achieve, that certain values occur multiple time in the list.
I'm not sure if my code compiles against c#, as I currently do not have visual studio running here, but I think the code should be mostly correct.
Something like this?
const int maxNumbers = 10;
List<int> numbers = new List<int>(maxNumbers);
for (int i = 0; i < maxNumbers; i++)
{
numbers.Add(i);
}
Random r = new Random();
while (numbers.Count > 0)
{
int index = r.Next(numbers.Count);
Console.Write("{0} ", numbers[index]);
numbers.RemoveAt(index);
}
Console.WriteLine();
Edit: For any random numbers:
const int maxNumbers = 10;
const int biggestNumbers = 10000;
List<int> numbers = new List<int>(maxNumbers);
Random r = new Random();
while (numbers.Count < maxNumbers)
{
int index = r.Next(biggestNumbers);
if (numbers.IndexOf(index) < 0)
{
numbers.Add(index);
Console.Write("{0} ", index);
}
}
Console.WriteLine();

Categories

Resources