0

I have a string s of size kn.

I want to shuffle each of the k n-sized blocks of s (given k and n).

Example: s = abcdabcdabcd, n = 4, k = 3.

BEGIN: abcdabcdabcd

 abcd       abcd       abcd
 └─┬─┘      └─┬─┘      └─┬─┘      
shuffle    shuffle    shuffle
   ↓          ↓          ↓
 bdac       adbc       cdba

RESULT: bdacadbccdba
ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
Daniel
  • 7,357
  • 7
  • 32
  • 84
  • 2
    It is guaranteed that the size of **s** is **kn**. To simplify, both **k** and **n** are at least 2. – Daniel Feb 05 '19 at 06:01
  • What do you want to happen if, e.g. your string contains a surrogate pair or combining diacritics? When you say the size of the string, do you mean UTF-16 code units (in which case a surrogate pair counts as 2), the length in code points, or the length in text elements (so that, e.g. an e followed by a combining acute accent counts as 1)? –  Feb 05 '19 at 06:34
  • When I say "size of string", I mean the number of characters it contains. You can consider the string contains only chars from *a* to *z* (97, ...,122 ASCII) – Daniel Feb 05 '19 at 06:37
  • This should definitely be on **[codegolf](https://codegolf.stackexchange.com/)** and not here. And as far as I think, usually we do not act like a writing service? – Hille Feb 05 '19 at 06:54
  • @Daniel - ok under that restriction things become simpler. You need to be careful with terms like "character" because in Unicode this is not a well-defined concept (and the CLR is somewhat unhelpful here by exposing the gory details of the UTF-16 encoding to the programmer via `System.Char`). –  Feb 05 '19 at 06:56

6 Answers6

3
Random rnd = new Random();
var s = "abcdabcdabcd";
var k = 3;
var n = 4;
var result = "";
for (int i = 0; i < k; i++)
{
    var current = s.Substring((i * n), n);
    var shuffled = string.Join("",current.OrderBy(x=>rnd.Next()));
    result += shuffled;
}

One Result :

"bcadabcdbcda"
Mehrdad Dowlatabadi
  • 1,335
  • 2
  • 9
  • 11
3

One nasty Linq statement

Given

public static Random _rnd=new Random();

public static string WeirdShuffle(string input, int n) 
   => string.Concat(input.ToCharArray()
                         .Select((s, i) => (s, i))
                         .GroupBy(x => x.i / n)
                         .Select(g => string.Concat(g.Select(x => x.s)
                                                     .OrderBy(x => _rnd.Next()))));

Usage

Console.WriteLine(WeirdShuffle("abcdabcdabcd",4));

Additional Resources

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • 1
    I chose another answer just for readability, your answer uses a lot of concepts and its time complexity seems to be the same than Mehrdad's answer, which is easier to understand. – Daniel Feb 05 '19 at 06:30
2

Something like this should work. It's essentially a modified Fisher-Yates shuffle:

private static Random _random = new Random(); 

public static string ShuffleSubstrings(string input, int n, int k)
{
    if (input.Length != (n * k))
    {
        throw new ArgumentException("Length of input is not equal to kn");
    }

    var characters = input.ToCharArray();
    for (int g = 0; g < input.Length; g += n)
    {
        ShuffleSubarray(characters, g, n);
    }
    return new string(characters);
}

private static void ShuffleSubarray<T>(T[] array, int startPosition, int length)
{
    // For loop to handle individual group
    for (int i = startPosition; i < startPosition + length; ++i)
    {
        // shuffle taken from taken from https://www.dotnetperls.com/fisher-yates-shuffle, modified to work with groups)
        int r = i + _random.Next(length - (i % length));
        T tmp = array[r];
        array[r] = array[i];
        array[i] = tmp;
    }
}

Try it online

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
1

I like this one:

private static Random _rnd = new Random();

public static string ShuffleSubstrings(string input, int n, int k)
{
    if (input.Length != (n * k))
    {
        throw new ArgumentException("Length of input is not equal to kn");
    }

    return String.Join("",
        from i in Enumerable.Range(0, k)
        from x in
            from y in input.Substring(i * n, n)
            orderby _rnd.Next()
            select y
        select x);
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0
public string Shuffle(string str, int shuffleSize)
{
    if (str.Length % shuffleSize != 0)
    {
        throw new ArgumentException();
    }
    var result = Enumerable.Range(0, str.Length / shuffleSize)
        .Select(i =>
            SmallShuffle(str.Substring(i * shuffleSize, shuffleSize))
        );

    return string.Join("", result);
}

public string SmallShuffle(string str)
{
    char[] array = str.ToCharArray();
    Random rng = new Random();
    int n = array.Length;
    while (n > 1)
    {
        n--;
        int k = rng.Next(n + 1);
        var value = array[k];
        array[k] = array[n];
        array[n] = value;
    }
    return new string(array);
}

Usage:

var s = "123456789123";
int k = 4;
var shuffled = Shuffle(s, k);

Heavily influenced by these two answers:

https://stackoverflow.com/a/1450797/4949005

https://stackoverflow.com/a/4740014/4949005

smoksnes
  • 10,509
  • 4
  • 49
  • 74
0
 static string Shffule(string str,int blockSize)
        {
            Random randomIndex= new Random();
            for (int indexInString = 0; indexInString < str.Length; indexInString+= blockSize)
            {
                for (int shufflesInBlock = 0; shufflesInBlock < blockSize; shufflesInBlock++)
                {
                    var firstRandomIndex = randomIndex.Next(indexInString, indexInString + blockSize);
                    var secondRandomIndex = randomIndex.Next(indexInString, indexInString + blockSize);
                    //str.Swap(firstRandomIndex, secondRandomIndex);
                }
            }
            return swapedString;
        }

there is a lot of ways to swap 2 chars string here so i will leave the string.Swap extension method to u

you can control how many shuffles per block from the nested loop by changing the shufflesInBlock < blockSize condition