Convert jagged char[][] to char[,]? - c#

One of my 'for the hell of it' projects I started yesterday was a Befunge interpreter. I have it working for the most part except for an edge case.
I got lazy and decided to read in a befunge program with this:
char[][] program = File.ReadAllLines(args[0]).Select(x => x.ToCharArray()).ToArray();
I knew I was creating more work for myself later, but I wanted to get to other parts and left it at that. Now it's later and I need to fix the fact that program is not rectangular. Let's say I had this befunge program:
v v <
#
> ^
The 1st and 3rd lines are 9 characters long, but the 2nd line is only 5. In the way I have my befunge interpreter set up, I will get an IndexOutOfBoundsException before the program terminates because after interpreting ^ as a change of direction I would try to access program[1][8] and program[1] is only 5 long. Instead of trying to catch the exception and dance around it, how could I create a char[,] using program and filling the extra characters with spaces?
I know I could just determine the length of the longest line, the number of lines, create the char[,] with those and copy them over, but I'm hoping for something a little simpler and more elegant. I am completely ok with throwing out the above line if a new approach is better.

Rather than re-creating the entire jagged array (assuming it could be rather large) you could just create a wrapper for it. That wrapper would be able to do the bounds checking and return some default value if it would be out of bounds rather than erroring.
public class Matrix<T>
{
public T[][] UnderlyingCollection {get;set;} //should probably be readonly and set in the constructor
public T DefaultValue {get;set;}
public T this[int i, int j]
{
get
{
if(UnderlyingCollection.Length > i && UnderlyingCollection[i].Length > j)
return UnderlyingCollection[i][j];
else
return DefaultValue;
}
set
{ /*TODO implement*/ }
}
}

Building on #AndreCalil's previous answer, this might be more performant, especially for large arrays of primitive types. Arrays of primitive types can be treated as a flat buffer of bytes, which can be useful in this sort of work (if you've got experience with assembler or C):
static void Main( string[] args )
{
string[][] jagged = new string[][] { new string[] { "alpha" , } ,
new string[] { "bravo" , "charlie" , } ,
new string[] { "delta" , "echo" , "foxtrot" , } ,
new string[] { "golf" , "hotel" , "india" , "juliet" , } ,
new string[] { "kilo" , "lima" , "mike" , "nancy" , "oscar" , } ,
} ;
string[,] rectangular = RectArrayFromJagged<string>( jagged ) ;
return;
}
public static T[,] RectArrayFromJagged<T>( T[][] a )
{
int rows = a.Length;
int cols = a.Max( x => x.Length );
T[,] value = new T[ rows , cols ] ;
value.Initialize() ;
if ( typeof(T).IsPrimitive )
{
int elementSizeInOctets = Buffer.ByteLength(value) / value.Length ;
for ( int i = 0 ; i < rows ; ++i )
{
int rowOffsetInOctets = i * cols * elementSizeInOctets ;
int rowLengthInOctets = a[i].Length * elementSizeInOctets ;
Buffer.BlockCopy( a[i] , 0 , value , rowOffsetInOctets , rowLengthInOctets ) ;
}
}
else
{
for ( int i = 0 ; i < rows ; ++i )
{
int rowLength = a[i].Length ;
for ( int j = 0 ; j < rowLength ; ++j )
{
value[i,j] = a[i][j] ;
}
}
}
return value ;
}

Man, I'm not sure if this is what you're looking for, but check this out:
public static class CharArrayExtension
{
public static char[,] FormatMatrix(this char[][] matrix)
{
int TotalColumns = matrix.Length;
int TotalLines = 0;
//Get the longest line of the current matrix
for (int column = 0; column < TotalColumns; column++)
{
int line = matrix[column].Length;
if (line > TotalLines)
TotalLines = line;
}
//Instantiate the resulting matrix
char[,] Return = new char[TotalColumns, TotalLines];
Return.Initialize();
//Retrieve values from the current matrix
for (int CurrentColumn = 0; CurrentColumn < TotalColumns; CurrentColumn++)
{
int MaxLines = matrix[CurrentColumn].Length;
for (int CurrentLine = 0; CurrentLine < MaxLines; CurrentLine++)
{
Return[CurrentColumn, CurrentLine] = matrix[CurrentColumn][CurrentLine];
}
}
return Return;
}
}
Usage:
char[] Length5 = new char[]{ 'a', 'b', 'c', 'd', 'e'};
char[] Length10 = new char[10];
char[][] Matrix = new char[2][];
Matrix[0] = Length5;
Matrix[1] = Length10;
char[,] FormattedMatrix = Matrix.FormatMatrix();
Any feedback will be appreciated.
UPDATE
Nicholas pointed out the performance issue. I was curious about it, so I made the following micro-weak-benchmarking:
char[] Length5 = new char[]{ 'a', 'b', 'c', 'd', 'e'};
char[] Length10 = new char[10];
char[][] Matrix = new char[2][];
Matrix[0] = Length5;
Matrix[1] = Length10;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = 0; i < 5000; i++)
{
char[,] FormattedMatrix = Matrix.FormatMatrix();
}
stopWatch.Stop();
Console.WriteLine(string.Format("Andre Calil: {0} ms", stopWatch.ElapsedMilliseconds));
stopWatch.Reset();
stopWatch.Start();
for (int i = 0; i < 5000; i++)
{
char[,] FormattedMatrix = RectArrayFromJagged<char>(Matrix);
}
stopWatch.Stop();
Console.WriteLine(string.Format("Nicholas Carey: {0} ms", stopWatch.ElapsedMilliseconds));
Console.ReadLine();
I've run it multiple times, and the average results was:
Andre Calil: 3 ms
Nicholas Carey: 5 ms
I know that this is not a proper benchmarking, but looke like my solution isn't so bad in terms of performance after all.

Related

Rotating a multiline string 90 degrees - lines become columns

Say I have a string like
Line 1
Line 2
I want to turn this string 90 degrees clockwise, so that it becomes
LL
ii
nn
ee
12
The rotation needs to be performed only once such that it is in effect 'turning lines into columns.' Performing it twice should give the original string. (If it was truly rotating by 90 degrees, it would have to be repeated four times to arrive back at the original.)
using System;
using System.Collections;
using System.Collections.Generic;
namespace rotateString
{
class Program
{
static void Main(string[] args)
{
var strings = new string[] { "Line 1", "Line 2" };
var lines = new List<string>();
var done = false;
var j = 0;
do
{
var line = "";
for (var i = 0; i < strings.Length; ++i)
{
var s = strings[i];
if (j >= s.Length)
{
done = true;
break;
}
line += s[j];
}
lines.Add(line);
++j;
} while (!done);
for(int i=0; i < lines.Count; ++i)
{
Console.WriteLine(string.Format("{0} : '{1}'", i, lines[i]));
}
}
}
}
Output:
0 : 'LL'
1 : 'ii'
2 : 'nn'
3 : 'ee'
4 : ' '
5 : '12'
6 : ''
Note that this pretty much assumes the strings are all the same length. Adjusting it to work with the longest string length is trivial.
Using a StringBuilder would be a bit more efficient but, unless you're working with a lot of very long strings, you'll never see the difference in performance.
This was interesting to try out and write. I haven't spent time on validation and characters, but I was trying to see if I can write something that is somewhat "compact" (challenge to myself mostly in evening hours).
#KayZed I will also run your implementation, I see you did some more validations about the inputs.
#3Dave I see we had similar ideas about this ;)
My idea around this was a projection of the input strings (plus now I hard-coded the length of the Span<char>, that calculation can be made simple) into the "flattened" structure of all characters with the offset of the index based on the number of inputs.
My "solution":
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
var inputs = new[] { "Short", "Line 1", "Line 2", "Much longer line 3", "🧐" };
Rotate(inputs);
}
public static void Rotate(IReadOnlyCollection<string> aValues)
{
Span<char> chars = stackalloc char[100];
var offset = 0;
foreach (var value in aValues)
{
var index = 0;
foreach (char character in value)
{
chars[index + offset] = character;
index += aValues.Count;
}
offset++;
}
var position = 0;
foreach (char character in chars)
{
Console.Write(character == default(char) ? ' ' : character);
Console.Write(' ');
if (position == aValues.Count - 1)
{
Console.WriteLine();
position = 0;
continue;
}
position++;
}
}
}
I probably missed some edge cases (and didn't handle the "special" characters), but hope this gives an idea on some "optimisations".
The output:
S L L M �
h i i u �
o n n c
r e e h
t
1 2 l
o
n
g
e
r
l
i
n
e
3
It will need to add leading spaces (i.e. columns which were preceding shorter lines in the original string.) This method gives you an option to make this visible (fillChar - several options provided as constants.)
Also the additional ReverseLineOrder method reverses the line order in case you would want to rotate 90 degrees, reversing all lines.
using System;
using System.Globalization;
using System.Text;
namespace Library.Text
{
public static class TextRotate
{
public const char Space = ' ';
public const char MiddleDot = '\u00b7';
public const char Circle = '\u25cb';
public const char BlackSquare = '\u25a0';
public const char NoBreakSpace = '\u00a0';
public static string LinesToColumns(string s, char fillChar = Space)
{
// A line feed at the end of the text is not seen as content.
// However, if the text ends in a line feed, we make sure the output ends in one, too.
bool endsWithNewline = s.EndsWith(Environment.NewLine);
string[] linesIn = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
int[][] textElements = new int[linesIn.Length][];
int longestLine_chars = 0;
int longestLine_elems = 0; // The longest line, in text elements
for (int l = 0; l < linesIn.Length; l++)
{
string line = linesIn[l];
if (line.Length > longestLine_chars)
{
longestLine_chars = line.Length;
}
var elems = StringInfo.ParseCombiningCharacters(line); // Gets indices of surrogate pairs, combining characters etc.
if (elems.Length > longestLine_elems)
{
longestLine_elems = elems.Length;
}
textElements[l] = elems;
}
// Go through columns (columns in the input text, and columns in terms of text elements - NOT chars)
string[] columns = new string[longestLine_elems];
var builder = new StringBuilder(longestLine_chars * linesIn.Length + Math.Max(longestLine_chars, linesIn.Length) * Environment.NewLine.Length);
for (int column = 0; column < longestLine_elems; column++)
{
builder.Clear();
System.Diagnostics.Debug.Assert(builder.Length == 0);
int cutoff = 0;
for (int l = 0; l < linesIn.Length; l++)
{
// Is the line long enough to reach to this column?
int[] lineTextElements = textElements[l];
int textElementsInLine = lineTextElements.Length;
if (textElementsInLine > column)
{
int firstCharIndex = lineTextElements[column];
if (column + 1 < textElementsInLine)
{
int nrOfChars = lineTextElements[column + 1] - firstCharIndex;
builder.Append(linesIn[l], firstCharIndex, nrOfChars);
}
else
{
builder.Append(linesIn[l], firstCharIndex, linesIn[l].Length - firstCharIndex);
}
cutoff = builder.Length;
}
else
{
builder.Append(fillChar);
}
}
// Trim the fill char off line endings (for when rotating back)
while (cutoff > 0 && builder[cutoff - 1] == fillChar)
{
cutoff--;
}
// Resulting column
columns[column] = builder.ToString(0, cutoff);
}
// Turn the columns into lines
builder.Clear();
foreach (var c in columns)
{
builder.AppendLine(c);
}
if (!endsWithNewline && builder.Length > 0)
{
builder.Length -= Environment.NewLine.Length;
}
return builder.ToString();
}
public static string ReverseLineOrder(string s)
{
// A line feed at the end of the text is not seen as content.
// However, if the text ends in a line feed, we make sure the output ends in one, too.
bool endsWithNewline = s.EndsWith(Environment.NewLine);
string[] linesIn = s.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
var builder = new StringBuilder(s.Length);
for (int l = linesIn.Length - (endsWithNewline ? 2 : 1); l >= 0; l--)
{
builder.AppendLine(linesIn[l]);
}
if (!endsWithNewline && builder.Length > 0)
{
builder.Length -= Environment.NewLine.Length;
}
return builder.ToString();
}
}
}

C# Have problems generating random 2d array from smaller arrays

I am trying to generate a random 2D array, from a number of smaller arrays. I plan to use it some day to generate a random game map.
Each smaller array is called "Island". Each of them is manually predefined.
char[,] Island1 = new char[,]
{
{'A', 'A', 'A'},
{'A','B','A'},
{'A','A','A'}
};
char[,] Island2 = new char[,]
{
{'C', 'C'},
{'C','C'}
};
char[,] Island3 = new char[,]
{
{'D', 'D', 'D'},
{'D','D','D'},
{'D','D','D'}
};
I am trying to generate a larger array, with all smaller ones inside, placed randomly.
What's important, is that smaller arrays shouldn't overlap each other.
public static Boolean CanPlaceIsland(int StartX, int StartY, Island thisIsland)
{
Boolean Answer = true;
for (int i = StartX; i<StartX+thisIsland.CellArray.GetLength(0);i++)
{
for (int j = StartX; j<StartY+thisIsland.CellArray.GetLength(1);j++)
{
if (WorldMap[i,j].Terrain!='.')
Answer = false;
}
}
return Answer;
}
I am trying to go through each island, one by one, and only add new one, if it doesn't overlap non-empty squares.
Here's updated method for filling the map with islands (previous version could cause infinite loop).
public static void CreateEmptyMap()
{
WorldMap = new Cell[WorldX, WorldY];
for (int i=0; i<WorldX; i++)
for (int j=0; j<WorldY; j++)
WorldMap[i,j] = new Cell('.');
}
public static void FillMap()
{
int IslandsPlaced=0;
foreach(Island thisIsland in IslandsList)
{
Boolean check = false;
int x = 0;
int y = 0;
Random w = rnd;
int SideA = thisIsland.CellArray.GetLength(0);
int SideB = thisIsland.CellArray.GetLength(1);
int WorldSideA = WorldMap.GetLength(0);
int WorldSideB = WorldMap.GetLength(1);
x = w.Next(2, WorldSideA-SideA-1);
y = w.Next(2,WorldSideB-SideB-1);
check = CanPlaceIsland(x,y,thisIsland);
if (check==true)
{
PlaceIsland(x,y,thisIsland);
IslandsPlaced++;
}
}
if (IslandsPlaced!=IslandsList.Count())
{
CreateEmptyMap();
FillMap();
}
}
The placing:
public static void PlaceIsland(int x, int y, Island thisIsland)
{
int SideA = thisIsland.CellArray.GetLength(0);
int SideB = thisIsland.CellArray.GetLength(1);
for (int i=0; i<SideA;i++)
{
for (int j=0; j<SideB;j++)
{
WorldMap[x+i,y+j] = thisIsland.CellArray[i,j];
}
}
}
However, sometimes islands still overlap, and I can't find why.
..........
..........
..........
..........
....AAA...
..DDDBA...
..DDDAA...
..DDD.....
..........
..........
Your bug is in CanPlaceIsland:
for (int j = StartX; //error here!
j < StartY + thisIsland.CellArray.GetLength(1);
j++) { ... }
Should be:
for (int j = StartY;
j < StartY + thisIsland.CellArray.GetLength(1);
j++) { ... }
Looks like a typical copy and paste bug...
Apart from that, if your maps are rather crowded, you risk entering an infinite loop if there is no solution for a given island.
Computing if one or more valid solutions exist and if the combination you are currently in is one of them can be expensive and somewhat tricky so, unless you really have to deal with crowded maps where a solution must be given, I'd bale out after a predetermined number of failed attempts placing an island; you might get false negatives once in a while but its probably something you can live with.

C# char Conversion/Append to/with int, int64, string

Lately I have been working through Project Euler, specifically
https://projecteuler.net/problem=4
I create to arrays
Multiply them together
Convert the number in a CharArry
Compare the numbers
If true, my problem arises
I attempt to convert the char back to an int, or long, or string,
and
I have attempted to append the char to an int, or long, or string, or whatever
void Main()
{
int[] arrOne = new int[900]; // Initializing Array One
int[] arrTwo = new int[900]; // Initializing Array Two
Console.WriteLine(PopulateAndConvert(arrOne, arrTwo)); // Sending info into class
}
int PopulateAndConvert(int[] a, int[] b)
{
char[] c = new char[1]; // char used to store tested number
//string[] m = new string[a.Length*b.Length];
long l = 0; // Used for testing code
for(int i = 0; i < a.Length; i++) // Populating Arrays One and Two
{
a[i] = i + 100;
b[i] = i + 100;
}
for(int j = a.Length-1; j >= 0; j--) // Beginning for-loops for multiplication and testing
{
//Console.WriteLine(j);
for(int k = b.Length-1; k >= 0; k--) // Second part of for-loop previously mentioned
{
//Console.WriteLine(k);
c = (a[j] * b[k]).ToString().ToCharArray(); // Where the math and conversion happens
//Console.WriteLine(c);
if(c.Length > 5) // Checking if digit of product is greater than 5
{
if((c[0] == c[c.Length-1]) && // Comparing first and second half of product
(c[1] == c[c.Length-2]) &&
(c[2] == c[c.Length-3]))
{
/*for(int n = 0; n < c.Length; n++) // Last tidbit of code that was being attempted
sb[l].Append(Convert.ToInt32(c[0]));
l++;
Console.WriteLine(sb); */
}
}
else if (c.Length < 5) // Product with less than 6 digits go here
{
if((Convert.ToInt32(c[0]) == Convert.ToInt32(c[4])) &&
(Convert.ToInt32(c[1]) == Convert.ToInt32(c[3])))
{
//m[l] = Convert.ToChar(c); l++;
}
}
}
}
// Everything below was used to check the code that I have been trying to work through
// And to place the given products in a ascending or descending order
//foreach (char x in m)
// Console.WriteLine(m);
//IEnumerable<char> sortDescendingQuery =
// from num in c
// orderby num descending
// select num;
return 0;
}
After some time (resting the mind is always beneficial) I found a solution:
if(c.Length > 5) // Checking if digit of product is greater than 5
{
int[] n = new int[c.Length];
StringBuilder sb = new StringBuilder();
if((c[0] == c[c.Length-1]) && // Comparing first and second half of product
(c[1] == c[c.Length-2]) &&
(c[2] == c[c.Length-3]))
{
for(int l = 0; l < c.Length; l++) // Converting each value in the char array to a stringbuilder
{
sb.Append(Convert.ToInt32(new string(c[l], 1)));
}
m[q] = Int32.Parse(sb.ToString()); // Converting stringbuilder into string and then into a long
q++;
}
}
I had to convert each individual value within the char array c[] to a string, then an int, then append it to the string builder sb.
After that I then convert sb to a string (via ToString()) and Parse it to an int.
It seems like a long work around, but it works.
Now I need to Sort it numerically (another hurdle).

Best way to randomize two identical arrays without overlap?

Suppose we have two identical arrays {"A", "B", "C", "D", "E", "F"}. Is there a quick way to randomize the order of each that ensures when the two are lined up, the same letters are never at the same indices? (Obviously we can just generate a new index if it will cause a match but I'm wondering if there's a way that produces less repetition).
This works, and I think it is fairly easy to understand.
var source = new [] { "A", "B", "C", "D", "E", "F" };
var output1 = (string[])null;
var output2 = (string[])null;
var rnd = new Random();
Action shuffle = () =>
{
output1 = source.OrderBy(x => rnd.Next()).ToArray();
output2 = source.OrderBy(x => rnd.Next()).ToArray();
};
shuffle();
while (output1.Zip(output2, (o1, o2) => new { o1, o2 })
.Where(x => x.o1 == x.o2)
.Any())
{
shuffle();
}
You could do it in two steps with O(n) complexity.
[Step 1]
Shuffle just one array in a way that every letter changes its original position, something like this:
var rnd = new Random(0);
var x = new char[] { 'A', 'B', 'C', 'D', 'E', 'F' };
for(int i = 0; i < x.Length; i++)
{
var j0 = (i == x[i] - 'A')? i + 1: i;
var j = rnd.Next(j0, x.Length);
// x[i] ⟷ x[j]
var t = x[i]; x[i] = x[j]; x[j] = t;
}
It ensures that the first and the second arrays are different in every position.
[Step 2]
Use Fisher–Yates shuffle for both arrays synchronously:
var y = new char[] { 'A', 'B', 'C', 'D', 'E', 'F' };
for(int i = 0; i < x.Length; i++)
{
var j = rnd.Next(i, x.Length);
// x[i] ⟷ x[j]; y[i] ⟷ y[j]
var
t = x[i]; x[i] = x[j]; x[j] = t;
t = y[i]; y[i] = y[j]; y[j] = t;
}
It ensures randomization of both, keeping difference at every position.
My best advice would be to make your own randomizer method that takes 2 arguments: array to be shuffled and array that its not allowed to match.
Here is a quick example of a class that has 2 string arrays that it will be shuffling by calling (objectName).Shuffle();
public class ArrayShuffler {
public String[] arr1;
public String[] arr2;
public ArrayShuffler() {
arr1 = new String[] { "A", "B", "C", "D", "E", "F" };
arr2 = new String[] { "A", "B", "C", "D", "E", "F" };
}
public void Shuffle() {
shuffleArr(arr1);
shuffleArr(arr2, arr1);
}
/// <summary>
/// Can shuffle array, maching against a second array to prevent dublicates in same intex spot.
/// </summary>
/// <param name="arr">Array to be shuffled</param>
/// <param name="validate">Array to mach against</param>
private void shuffleArr(String[] arr, String[] validate = null) {
Random r = new Random();
int indx = 0;
while(indx < arr.Length){
int rIndx = r.Next(indx, arr.Length);
string tmp = arr[indx];
if(validate != null) { //Will only be performed if you specify an array to be matched against.
if(arr[rIndx] != validate[indx]) {
arr[indx] = arr[rIndx];
arr[rIndx] = tmp;
indx++;
}
else if(indx == arr.Length - 1) {
shuffleArr(arr, validate);
}
}
else { //Default operation
arr[indx] = arr[rIndx];
arr[rIndx] = tmp;
indx++;
}
}
}
}
Assuming you're trying to minimize the quantity of unnecessary re-rolls, and that it's the two results that must not match one another (permitting an output character at a particular index to match the character in the input at that index), I think I've got a solution for you.
The gist of it is that we build the resulting strings on-the-fly keeping track of which characters have not been picked in each list and temporarily removing the one we picked first for a particular index from the candidates we select from for the second one. I don't think this method has any bias to it, but I'm admittedly not an expert in that regard.
public void Shuffle(int seed)
{
char[] orig = { 'A', 'B', 'C', 'D', 'E', 'F' };
List<char> buffer1 = new List<char>();
List<char> buffer2 = new List<char>();
// Keep track of which indexes haven't yet been used in each buffer.
List<int> availableIndexes1 = new List<int>(orig.Length);
List<int> availableIndexes2 = new List<int>(orig.Length);
for (int i = 0; i < orig.Length; i++)
{
availableIndexes1.Add(i);
availableIndexes2.Add(i);
}
Random rand = new Random(seed);
// Treat the last 2 specially. See after the loop for details.
for (int i = 0; i < orig.Length - 2; i++)
{
// Choose an arbitrary available index for the first buffer.
int rand1 = rand.Next(availableIndexes1.Count);
int index1 = availableIndexes1[rand1];
// Temporarily remove that index from the available indices for the second buffer.
// We'll add it back in after if we removed it (note that it's not guaranteed to be there).
bool removed = availableIndexes2.Remove(index1);
int rand2 = rand.Next(availableIndexes2.Count);
int index2 = availableIndexes2[rand2];
if (removed)
{
availableIndexes2.Add(index1);
}
// Add the characters we selected at the corresponding indices to their respective buffers.
buffer1.Add(orig[index1]);
buffer2.Add(orig[index2]);
// Remove the indices we used from the pool.
availableIndexes1.RemoveAt(rand1);
availableIndexes2.RemoveAt(rand2);
}
// At this point, we have 2 characters remaining to add to each buffer. We have to be careful!
// If we didn't do anything special, then we'd end up with the last characters matching.
// So instead, we just flip up to a fixed number of coins to figure out the swaps that we need to do.
int secondToLastIndex1Desired = rand.Next(2);
int secondToLastIndex2Desired = rand.Next(2);
// If the "desired" (i.e., randomly chosen) orders for the last two items in each buffer would clash...
if (availableIndexes1[secondToLastIndex1Desired] == availableIndexes1[secondToLastIndex2Desired] ||
availableIndexes1[(secondToLastIndex1Desired + 1) % 2] == availableIndexes2[(secondToLastIndex2Desired + 1) % 2])
{
// ...then swap the relative order of the last two elements in one of the two buffers.
// The buffer whose elements we swap is also chosen at random.
if (rand.Next(2) == 0)
{
secondToLastIndex1Desired = (secondToLastIndex1Desired + 1) % 2;
}
else
{
secondToLastIndex2Desired = (secondToLastIndex2Desired + 1) % 2;
}
}
else if (rand.Next(2) == 0)
{
// Swap the last two elements in half of all cases where there's no clash to remove an affinity
// that the last element has for the last index of the output, and an affinity that the first
// element has for the second-to-last index of the output.
int t = secondToLastIndex1Desired;
secondToLastIndex1Desired = secondToLastIndex2Desired;
secondToLastIndex2Desired = t;
}
buffer1.Add(orig[availableIndexes1[secondToLastIndex1Desired]]);
buffer1.Add(orig[availableIndexes1[(secondToLastIndex1Desired + 1) % 2]]);
buffer2.Add(orig[availableIndexes2[secondToLastIndex2Desired]]);
buffer2.Add(orig[availableIndexes2[(secondToLastIndex2Desired + 1) % 2]]);
Console.WriteLine(new string(buffer1.ToArray()));
Console.WriteLine(new string(buffer2.ToArray()));
}
Note that if this were used for particularly long arrays, then the data moving from List<T>.Remove / List<T>.RemoveAt and the linear searching done by the former may not scale well.

How to perform combinatoric task in C#

i have a problem with an combinatoric task. I want to have a code which can handle this math calculation 48/5 = 1712304 (5! = 5*4*3*2*1 = 120 48*47*46*45*44 = 205476480 205476480/120 = 1712304)
Here some more details.
I have a string array with 48 strings. The strings have a length between 2 and 3 chars.
string array[] = new string[]{"A1","1D","410" /*[....]*/}
i want to combine 5 strings together, and i want to do this with the whole array, my biggest problem is that a combined string is just allowed to include each of the 48 strings just once. And each combined string have to be unique.
In the end i need a List with 1712304 string entries, which have to be between 10 and 15 degits long. For example one string could look like this "A11A4103E1T".
Back to the math, i know that there are 1712304 combined options so any other result must be wrong. That's where my problem appears.
I created the following code without succesful result
string[] siDigit = new string[iNumDigits];
List<string> liste = new List<string>();
const int iDigitBase = 48;
const int iNumDigits = 5;
for (int i = 0; i < 1712303; ++i)
{
int i2 = i;
for (int i3 = 0; i3 < iNumDigits; ++i3)
{
siDigit[i3] = array[i2 % iDigitBase];
i2 /= iDigitBase;
}
bool duplicate_free = siDigit.Distinct().Count() == siDigit.Length;
if (duplicate_free == true)
{
liste.Add(siDigit[0] + siDigit[1] + siDigit[2] + siDigit[3] + siDigit[4]);
Console.Write(siDigit[0] + siDigit[1] + siDigit[2] + siDigit[3] + siDigit[4]);
Console.WriteLine();
}
}
What i get is way to less entries in my list, i just get 1317051 entries and dont know why. I cant find any solution for my problem, may someone out there can help me out.
Any help would be greatly appreciated.
P.S In germany they dont teach better english :)
I think your algorithm is wrong.
Consider i equals 0, i2 equals 0, so siDigit[i3](i3 = 0, 1, 2, 3 ,4) are array[0], duplicate_free is false. You lose one. So, you can not get all entries.
Here is a recursive algorithm:
private static void GetCombination(ref List<string[]> list, string[] t, int n, int m, int[] b, int M)
{
for (int i = n; i >= m; i--)
{
b[m - 1] = i - 1;
if (m > 1)
{
GetCombination(ref list, t, i - 1, m - 1, b, M);
}
else
{
if (list == null)
{
list = new List<string[]>();
}
string[] temp = new string[M];
for (int j = 0; j < b.Length; j++)
{
temp[j] = t[b[j]];
}
list.Add(temp);
}
}
}
public static List<string[]> GetCombination(string[] t, int n)
{
if (t.Length < n)
{
return null;
}
int[] temp = new int[n];
List<string[]> list = new List<string[]>();
GetCombination(ref list, t, t.Length, n, temp, n);
return list;
}

Categories

Resources