Card shuffling

ddanbe 2 Tallied Votes 773 Views Share

There seems to be a lot of shuffling going around here lately. So I looked here to learn about the Fisher-Yates shuffle etc. But unless my English is not good enough I did not find anything about the way we shuffle a card deck here. Take a deck in your left hand and pick out some cards from the middle of the deck with your right hand and place them in front or behind of the rest. Repeat this a number of times.
So I have a little snippet here who does this trick.
It was a snap to do!
C# and .NET are great!

using System;
using System.Collections.Generic;

namespace Shuffle
{
    class Program
    {
        static void Main(string[] args)
        {
            const int cNumberOfShuffles = 5;
            List<int> cards = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
            // Yeah ok this should be 52 for a complete card deck, but programmers tend to be lazy...
            Random rand = new Random();
            int LowPick = 0;
            int HighPick = 0;
            int temp = 0;
            for (int i = 0; i < cNumberOfShuffles; i++)
            {
                LowPick = rand.Next(1, 14); //MSDN says 13 + 1 here
                HighPick = rand.Next(1, 14);
                // make LowPick always smaller than HighPick
                if (HighPick < LowPick)
                {
                    temp = LowPick;
                    LowPick = HighPick;
                    HighPick = temp;
                }
                // Pick some cards and remove them from the deck
                List<int> pick = cards.GetRange(LowPick - 1, HighPick - LowPick + 1);
                cards.RemoveRange(LowPick - 1, HighPick - LowPick + 1);
                // insert them back in front
                cards.InsertRange(0, pick); 
            }
            // Show the shuffled deck
            for (int i = 0; i < cards.Count; i++)
            {
                Console.Write(cards[i]);
                Console.Write(' ');
            }
            Console.ReadKey();
        }
    }
}
apegram 302 LINQ! Team Colleague

I tried your algorithm and it works, however I found that you need to increase your number of executions for it to deliver a really good shuffle. 5 iterations leaves a lot of cards in the same order as the original. I bumped it up to 100 to get a good redistribution.

LINQ can provide a cleaner sorting mechanism, and we've displayed that in prior threads. You can do something like this and get the sort in just 2 lines.

Random random = new Random ();
cards = cards.OrderBy(card => random.Next()).ToList();

The problem with LINQ can be that although it is a cleaner approach and can provide a coding shortcut, there is some overhead that LINQ brings to the table. For the benefit (reduced code), there appears to be a cost.

So I'd like to present a third shuffling option, one that doesn't involve LINQ or moving ranges of "cards" around, but it is sort of in the same ballpark. Rather than pull a range, it removes a card at a random index from the original list and adds it to a second list. By pulling the cards one by one, you won't end up with a contiguous section still together by design (although it could happen by mere random chance). By moving the card from one list to another, you're assured of not hitting the same card multiple times while leaving others untouched. That's not to say there aren't better ways, this is just one other way.

static void ShuffleWithAlternateList()
        {
            List<int> cards = new List<int>();
            for (int i = 0; i < 52; i++)
            {
                cards.Add(i);
            }

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            List<int> shuffledCards = new List<int>();
            Random random = new Random();

            int iterations = cards.Count;
            int index = 0;
            for (int i = 0; i < iterations; i++)
            {
                index = random.Next(0, iterations - i);
                shuffledCards.Add(cards[index]);
                cards.RemoveAt(index);
            }

            foreach (int card in shuffledCards)
            {
                Console.Write("{0} ", card);
            }

            stopwatch.Stop();
            Console.WriteLine("\n\n{0}\n\n", stopwatch.ElapsedMilliseconds);

        }

The reason the Stopwatch is in there is that I wanted to measure performance. It can vary by machine, but I'm getting 2 milliseconds (ms) on the method above, versus 4 ms on the original algorithm (after I bumped up the number of iterations, it is much shorter if you stick with the original 5 iterations), and 13 ms on the cleaner LINQ approach. The entire source is below.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Shuffle
{
    class Program
    {
        static void Main()
        {            
            Shuffle();
            ShuffleWithAlternateList();
            ShuffleWithLinq();

            Console.ReadKey();
        }

        static void ShuffleWithAlternateList()
        {
            List<int> cards = new List<int>();
            for (int i = 0; i < 52; i++)
            {
                cards.Add(i);
            }

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            List<int> shuffledCards = new List<int>();
            Random random = new Random();

            int iterations = cards.Count;
            int index = 0;
            for (int i = 0; i < iterations; i++)
            {
                index = random.Next(0, iterations - i);
                shuffledCards.Add(cards[index]);
                cards.RemoveAt(index);
            }

            foreach (int card in shuffledCards)
            {
                Console.Write("{0} ", card);
            }

            stopwatch.Stop();
            Console.WriteLine("\n\n{0}\n\n", stopwatch.ElapsedMilliseconds);

        }

        static void ShuffleWithLinq()
        {            
            List<int> cards = new List<int>();
            for (int i = 0; i < 52; i++)
            {
                cards.Add(i);
            }

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            Random random = new Random ();
            cards = cards.OrderBy(card => random.Next()).ToList();

            foreach (int card in cards)
            {
                Console.Write("{0} ", card);
            }

            stopwatch.Stop();
            Console.WriteLine("\n\n{0}\n\n", stopwatch.ElapsedMilliseconds);
        }

        static void Shuffle()
        {
            const int cNumberOfShuffles = 100;
            List<int> cards = new List<int>();
            for (int i = 0; i < 52; i++)
            {
                cards.Add(i);
            }

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            Random rand = new Random();
            int LowPick = 0;
            int HighPick = 0;
            int temp = 0;
            for (int i = 0; i < cNumberOfShuffles; i++)
            {
                LowPick = rand.Next(1, 53);
                HighPick = rand.Next(1, 53);
                // make LowPick always smaller than HighPick
                if (HighPick < LowPick)
                {
                    temp = LowPick;
                    LowPick = HighPick;
                    HighPick = temp;
                }
                // Pick some cards and remove them from the deck
                List<int> pick = cards.GetRange(LowPick - 1, HighPick - LowPick + 1);
                cards.RemoveRange(LowPick - 1, HighPick - LowPick + 1);
                // insert them back in front
                cards.InsertRange(0, pick);
            }
            // Show the shuffled deck
            for (int i = 0; i < cards.Count; i++)
            {
                Console.Write(cards[i]);
                Console.Write(' ');
            }

            stopwatch.Stop();
            Console.WriteLine("\n\n{0}\n\n", stopwatch.ElapsedMilliseconds);
        }
    }
}
commented: Thanks for your reply! +6
ddanbe 2,724 Professional Procrastinator Featured Poster

@apegram
Very much appreciate your contribution! In fact I was expecting this.
What I was doing here was what I would call a "practical" shuffle, not a theoretical one. What I mean is normally, when playing cards with some friends the cards all already in disorder, so we shuffle them from 5 to 10 times or so.(I admit a little cheating :S sneeks in here from time to time, but we play for little money and amuse ourselves a lot! ;) )
Thanks for the LINQ snippet also, just started to get into it:)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.