All letter-combinations (n-items) according pattern - c#

Im trying to decrypt a word which letters have been replaced with random other letters (but not 2 different letters into the same one).
The goal:
Im searching for a word which lenght and letter-pattern is known.
What I know:
The pattern itself means if searching for "guests" I know "123454" which shows the position of unique letters in this word. And for sure I know its an english word correctly written.
Software side:
I've created a DataGridView which headers are titled by the pattern. I want to populate each Column (pattern) with its possible combinations of all letters a-z.
What I've tried:
Ill start at the end => I've successfully implemented a spell-checker. So in the end I thought about just going through the columns and check for each result to find the actual word.
From the starting point seen I've written this so far:
private string[] alpha = new string[] { "a", "b", "c", ..."z"};
private int[] digits = new int[] { 0, 1, 2, 3, 4,....9 };
private void bruteforce()
{
// Each Column
foreach(DataGridViewColumn col in dgvResults.Columns)
{
// HeaderText to CharArray to IntArray (-48 to switch from ASCII to INT).
int[] pattern = Array.ConvertAll(col.HeaderText.ToCharArray(), c => c - 48);
// Prepare an result-array with the same length as the pattern.
string[] resultSet = Enumerable.Repeat("-", pattern.Length).ToArray();
// For each digit 0-9.
foreach(int digit in digits)
{
// In pattern search for each digit and save the index.
int[] indexes = pattern.FindAllIndexof(digit);
// If index is found.
if(indexes.Length > 0)
{
// Custom function ReplaceAtIndex.
// Replace resultSet-Values at given Indexes with unique letter
resultSet.ReplaceAtIndex(indexes, alpha[digit]);
}
}
}
}
Current result:
A pattern of 0112344 will be saved (resultSet) as abbcdee.
Now I would need to loop the letters while staying on the same pattern.
This step feels even more complicated then the stuff before. I thought, before continuing blowing away my head, Im going to see if there are some genius guys out there on stackoverflow who can provide a shorter easier way (maybe some shortcuts with LINQ).
So please, is there anyone thinking "easy doin" about this who could help me?
I appreciate every help in here. Thanks

Here is IMO a quite effective algorithm for generating what are you asking for.
It's a variation of the algorithm I've used in Looking at each combination in jagged array and System.OutOfMemoryException when generating permutations and is optimized to perform minimum allocations.
public static class Algorithms
{
private static readonly char[] alpha = Enumerable.Range('a', 'z' - 'a' + 1).Select(c => (char)c).ToArray();
public static IEnumerable<string> GenerateWords(this string pattern)
{
return pattern.GenerateWordsCore().Select(word => new string(word));
}
public static IEnumerable<char[]> GenerateWordsCore(this string pattern)
{
var distinctSet = pattern.Select(c => c - '0').Distinct().ToArray();
var indexMap = pattern.Select(c => Array.IndexOf(distinctSet, c - '0')).ToArray();
var result = new char[pattern.Length];
var indices = new int[distinctSet.Length];
var indexUsed = new bool[alpha.Length];
for (int pos = 0, index = 0; ;)
{
// Generate the next permutation
if (index < alpha.Length)
{
if (indexUsed[index]) { index++; continue; }
indices[pos] = index;
indexUsed[index] = true;
if (++pos < distinctSet.Length) { index = 0; continue; }
// Populate and yield the result
for (int i = 0; i < indexMap.Length; i++)
result[i] = alpha[indices[indexMap[i]]];
yield return result;
}
// Advance to next permutation if any
if (pos == 0) yield break;
index = indices[--pos];
indexUsed[index] = false;
index++;
}
}
}
Sample usage:
bool test = "12334".GenerateWords().Contains("hello");
foreach (var word in "123454".GenerateWords())
{
// Do something with word
}

Related

Algorithm for shortest list of words

The issue is as follows: the user provides a StartWord and EndWord string of X letters together with a list of strings that are also of length X (lets make it 4 but probably more)
static void Main(string[] args)
{
string StartWord = "Spot";
string EndWord = "Spin";
List<string> providedList = new List<string>
{
"Spin", "Spit", "Spat", "Spot", "Span"
};
List<string> result = MyFunc(StartWord, EndWord, providedList);
}
public List<string> MyFunc(string startWord, string endWord, List<string> input)
{
???
}
From the provided parameters I need to display to the user a result that comprises of the SHORTEST list of 4 letter words, starting with StartWord and ending with EndWord with a number of intermediate words that are to be found in the list, where each word differs from the previous word by PRECISELY one letter.
For example the above code should return a list of strings containing these elements:
Spot(as FirstWord),
Spit(only one letter is different from previous word),
Spin (as EndWord)
A bad exapmle would be: Spot, Spat, Span, Spin (as it takes 3 changes compared to the above 2)
I have been looking at some matching algorithms and recursion, but I am not able to figure out how to go about this.
Thank you for any kind of help in advance.
Create a graph where the vertices are words, and an edge connects any two words that differ by one letter.
Do a breadth-first search, starting at the StartWord, looking for the shortest path to the EndWord.
Here is sample code for this solution in a different language (Python). That may give you an even better pointer. :-)
def shortestWordPath (startWord, endWord, words):
graph = {}
for word in words:
graph[word] = {"connected": []}
for word in words:
for otherWord in words:
if 1 == wordDistance(word, otherWord):
graph[word]['connected'].append(otherWord)
todo = [(startWord,0)]
while len(todo):
(thisWord, fromWord) = todo.pop(0)
if thisWord == endWord:
answer = [thisWord, fromWord]
while graph[ answer[-1] ]["from"] != 0:
answer.append(graph[ answer[-1] ]["from"])
answer.reverse()
return answer
elif "from" in graph[thisWord]:
pass # We have already processed this.
else:
graph[thisWord]["from"] = fromWord
for nextWord in graph[thisWord]["connected"]:
todo.append([nextWord, thisWord])
return None
def wordDistance (word1, word2):
return len(differentPositions(word1, word2))
def differentPositions(word1, word2):
answer = []
for i in range(0, min(len(word1), len(word2))):
if word1[i] != word2[i]:
answer.append(i)
for i in range(min(len(word1), len(word2)),
max(len(word1), len(word2))):
answer.append(i)
return answer
print shortestWordPath("Spot", "Spin",
["Spin", "Spit", "Spat", "Spot", "Span"])
This is what I ended up using(please feel free to comment on the up and down side of it):
private List<List<string>> allWordSteps;
private string[] allWords;
public List<string> WordLadder(string wordStart, string wordEnd, string[] allWordsInput)
{
var wordLadder = new List<string>() { wordStart };
this.allWordSteps = new List<List<string>>() { wordLadder };
allWords = allWordsInput;
do
{
wordLadder = this.IterateWordSteps(wordEnd);
}
while (wordLadder.Count() == 0);
return wordLadder;
}
private List<string> IterateWordSteps(string wordEnd)
{
List<List<string>> allWordStepsCopy = this.allWordSteps.ToList();
this.allWordSteps.Clear();
foreach (var wordSteps in allWordStepsCopy)
{
var adjacent = this.allWords.Where(
x => this.IsOneLetterDifferent(x, wordSteps.Last()) &&
!wordSteps.Contains(x));
if (adjacent.Contains(wordEnd))
{
wordSteps.Add(wordEnd);
return wordSteps;
}
foreach (var word in adjacent)
{
List<string> newWordStep = wordSteps.ToList();
newWordStep.Add(word);
this.allWordSteps.Add(newWordStep);
}
}
return new List<string>();
}
private bool IsOneLetterDifferent(string first, string second)
{
int differences = 0;
if (first.Length == second.Length)
{
for (int i = 0; i < first.Length; i++)
{
if (first[i] != second[i])
{
differences++;
}
}
}
return differences == 1;
}

Algorithm to get all combinations of k elements from n

I have a list and I want to do some operation on elements of list starting from combination of 2.
Let's say below is my list:
List<string> strArr = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H" };
It will generate below combination if we select 2 elements at a time:-
(A,B) (A,C) (A,D) (A,E) (A,F) (A,G) (A,H) (B,C) (B,D)
and so on
It will generate below combination if we select 3 elements at a time:-
(A,B,C) (A,B,D) (A,B,E) (A,B,F) (A,B,G) (A,B,H) (A,C,D) (A,C,E) (A,C,F) (A,C,G) (A,C,H) (A,D,E) (A,D,F) (A,D,G) (A,D,H) (A,E,F) (A,E,G) (A,E,H) (A,F,G) (A,F,H) (A,G,H) (B,C,D) (B,C,E) (B,C,F)
and so on
Getting these combinations is very easy. I followed Algorithm to return all combinations of k elements from n
and it is giving me exact output.
But I cannot use this code as I have another requirement where I will keep deleting the elements from the list in case they satisfy certain condition and hence number of combinations will keep on reducing. So I don't want to get all the combinations using LINQ as it will be hampering performance in my case.
I thought of doing it below way:
List<string> strArr = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H" };
// Loop for selecting combination of two elements at time
for (int i = 0; i < strArr.Count; i++)
{
for (int j = i + 1; j < strArr.Count; j++)
{
// Writing on Console
// Actually do some operation to check whether these two elements in list needs to be removed or not
Console.Write(strArr[i] + strArr[j]);
Console.WriteLine();
// Check whether current combination of 2 elements need to be removed or not
if (<< condition >>)
{
// Remove both the current elements
// Remove current element of outer loop
strArr.RemoveAt(i);
// Remove current element of inner loop
// Subtracting one as list size is reduced by 1
strArr.RemoveAt(j - 1);
//
i--;
break;
}
}
}
bool isRemoved = false;
// Loop for selecting combination of three elements at time
for (int i = 0; i < strArr.Count; i++)
{
for (int j = i + 1; j < strArr.Count; j++)
{
for (int k = j + 1; k < s.Count; k++)
{
// Writing on Console
// Actually do some operation to check whether these three elements in list needs to be removed or not
Console.Write(strArr[i] + strArr[j] + strArr[k]);
Console.WriteLine();
// Check whether current combination of 3 elements need to be removed or not
if (<< condition >>)
{
// Remove all the three elements
// Remove current element of outer loop
strArr.RemoveAt(i);
// Remove current element of inner loop
// Subtracting 1 as list size is reduced by 1
strArr.RemoveAt(j - 1);
// Subtracting 2 as list size is reduced by 2
strArr.RemoveAt(k - 2);
isRemoved = true;
i--;
break;
}
// If elements are removed then exit from loop with variable j
if (isRemoved)
{
break;
}
}
}
}
// Now make loop for selecting combination of four elements at time
// and keep removing the elements depending upon condition
Removing elements will ensure that I get faster performance and I want to do this operation till the end reaches. I'm unable to think how to keep these deep level for loops in recursion. Can anyone help me in adding these endless for loops in recursion?
Thanks for spending your time in writing solution for me but this is not what I want... I will brief the requirement without code.
Let's say I have list of 10 elements.
I want to select all the combinations starting from group of 2 to 9. Total number of possible combinations will be 1012 if total elements are 10.
Now I want to start evaluating all the combinations in group of 2. Let's say first group (A,B). I will evaluate this group based upon certain conditions, and if that combination sastify the condition then I will remove the elements (A,B) from the list of 10 elements. So I will left with 8 elements in list.
Total number of combinations with remaining 8 elements will be 246 now. I didn't try the combinations (A,C) (A,D) and so on.
But I'm still evaluating the combinations in group of 2. Now I will pick remaining combinations in group of 2... Next combination will be (C,D) (C,E)..Let's say all remaining combinations doesn't satisfy condition of removing them from the list. Then I want to start evaluating combinations in group of 3.
First group of 3 will be (C,D,E)... If it will pass the certain condition then I will remove all the 3 elements from the list and I will be left with only 5 elements. Now I want to run my test of combination of 3 on these 5 elements.
after that group of 4 and so on
I hope you understand the use case now.
Can anyone help me out in implementing above use case?
The following solution will iterate over all possible combinations of elements from the input list, starting with combinations of 2 elements and moving upward from there. If the supplied filter function returns true then the elements chosen are removed from consideration; thus the total number of iterations is reduced as more elements are removed. Results are not tracked automatically; it's up to the caller to track results as required. My example usage to follow will demonstrate how to track results.
public static void PermutateElements<T>(
IEnumerable<T> elements,
Predicate<IEnumerable<T>> filterGroup)
{
var chooseFrom = new LinkedList<T>(elements);
var chosen = new List<T>(chooseFrom.Count);
for (int chooseCount = 2; chooseCount < chooseFrom.Count - 1; chooseCount++)
{
Permutate(chooseFrom, chooseCount, filterGroup, chosen, 0);
}
}
static bool Permutate<T>(LinkedList<T> chooseFrom, int chooseCount,
Predicate<IEnumerable<T>> filterPermutation, IList<T> chosen, int skipLast)
{
int loopCount = chooseFrom.Count;
for (int i = 0; i < loopCount; i++)
{
var choosingNode = chooseFrom.First;
chooseFrom.RemoveFirst();
bool removeChosen = false;
if (i < loopCount - skipLast)
{
chosen.Add(choosingNode.Value);
if (chooseCount == 1)
removeChosen = filterPermutation(chosen);
else
removeChosen = Permutate(chooseFrom, chooseCount - 1, filterPermutation, chosen, skipLast + i);
chosen.RemoveAt(chosen.Count - 1);
}
if (!removeChosen)
chooseFrom.AddLast(choosingNode);
else if (chosen.Count > 0)
return true;
}
return false;
}
The example below uses these functions in order to group letters; we want to take the letters A thru Z and put them into arbitrary groups such that each group contains more consonants than vowels, and contains at least one vowel:
HashSet<char> vowels = new HashSet<char>(new char[] { 'A', 'E', 'I', 'O', 'U', 'Y' });
var results = new List<IEnumerable<char>>();
Predicate<IEnumerable<char>> processGroup = delegate(IEnumerable<char> groupElements)
{
int vowelCount = groupElements.Count(x => vowels.Contains(x));
int consonantCount = groupElements.Count(x => !vowels.Contains(x));
if (vowelCount < consonantCount && vowelCount > 0)
{
results.Add(new List<char>(groupElements));
return true;
}
else
return false;
};
var elements = new char[26];
for (int i = 0; i < elements.Length; i++)
elements[i] = (char)('A' + i);
PermutateElements(elements, processGroup);
The results, which took 3131 iterations to perform (much fewer than iterating over all possible combinations without removal), are as follows:
ABC
DEF
GHI
JKO
PQU
VWY
At this point all vowels were used up, so no more legal combinations were possible.
Not sure if this is exactly what you need, but it may be considered an approach.
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
List<Tuple<Expression, Expression>> conditions = new List<Tuple<Expression, Expression>>();
// a complex condition, that the current item contains both "B" and "H"
Expression<Func<IEnumerable<string>, bool>> expression1 = item => item.Contains("B") && item.Contains("H");
// an expression which is used to exclude the elements from the list
Expression<Func<string, bool>> expression2 = j => j != "B" && j != "H";
// associate the condition with the exclusion filter
var condition = new Tuple<Expression, Expression>(expression1, expression2);
conditions.Add(condition);
List<string> strArr = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H" };
IEnumerable<IEnumerable<string>> result = Process(strArr, conditions);
}
private static IEnumerable<IEnumerable<string>> Process(IEnumerable<string> strArr, List<Tuple<Expression, Expression>> conditions)
{
List<IEnumerable<string>> response = new List<IEnumerable<string>>();
int k = 0;
for (int i = 1; i <= strArr.Count(); i++)
{
k++;
var r = strArr.Combinations(Math.Min(strArr.Count(), k));
bool stop=false;
foreach (IEnumerable<string> item in r)
{
if (stop)
{
break;
}
foreach (Tuple<Expression, Expression> condition in conditions)
{
if (Enumerable.Repeat<IEnumerable<string>>(item, 1).Any(Evaluate(condition.Item1) as Func<IEnumerable<string>, bool>))
{
var initialCount = strArr.Count();
strArr = strArr.Where(Evaluate(condition.Item2) as Func<string, bool>);
i -= initialCount - strArr.Count();
stop = true;
break;
}
else
{
foreach (var item1 in r)
{
response.Add(item1);
}
}
}
}
}
return response;
}
public static object Evaluate(Expression e)
{
if (e.NodeType == ExpressionType.Constant)
return ((ConstantExpression)e).Value;
return Expression.Lambda(e).Compile().DynamicInvoke();
}
}
public static class Helper
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c))
);
}
}
}
I've used this answer as a helper. You can also see that Process method is loosely coupled with the set of conditions (just one in this example).
Here is an algorithm i wrote in c++ for solving a similar problem. You should be able to use it for your purposes if you modify it a bit to work in c#.
void r_nCr(const unsigned int &startNum, const unsigned int &bitVal, const unsigned int &testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
{
unsigned int n = (startNum - bitVal) << 1;
n += bitVal ? 1 : 0;
for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
cout << (n >> (i - 1) & 1);
cout << endl;
if (!(n & testNum) && n != startNum)
r_nCr(n, bitVal, testNum);
if (bitVal && bitVal < testNum)
r_nCr(startNum, bitVal >> 1, testNum);
}
You can see an explanation of how it works here.

how to load a comma separated numbers to List<int> in c#

string formIdList = "8256, 8258, 8362, 8120, 8270, 8271, 8272, 8273, 8257, 8279, 8212, 8213, 8214, 8215, 8216, 8217, 8218, 8219, 8231, 8232, 8233, 8234, 8235, 8242, 8248, 8251, 8252, 8254, 8255, 8262, 8263, 8264, 8265, 8266, 8290, 8292, 8293, 8294, 8300, 8320, 8230, 8227, 8226, 8225, 8224, 8223, 8222, 8221, 8291, 8261, 8241, 8228, 8220, 8211, 8208, 8207, 8206, 8205, 8204, 8203, 8202, 8201, 8153, 8151, 8150, 8130, 8122, 8000, 8101, 8102, 8103";
var temp = formIdList.Split(',');
List<int> ids = new List<int>();
I need to load the temp into ids. I can use a for-loop but I'm sure there is a better way.
You could use LINQ:
string formIdList = ...
List<int> ids = formIdList.Split(',').Select(int.Parse).ToList();
List<int> ids = formIdList.Split(',').Select(i=>int.Parse(i)).ToList();
Maybe you should use something like ids.AddRange(temp), did you try it?
Linq is great and all, but you do this with a little less heap-thrashing on your own and have more control over what input you find acceptable. The following will yield the integers from any character enumeration separated by comma's and ignoring all white space.
public static IEnumerable<int> ParseInts(IEnumerable<char> idList)
{
bool valid = false;
int working = 0;
foreach (char c in idList)
{
if (c >= '0' && c <= '9')
{
valid = true;
working = (working*10) + (c - '0');
}
else if (c == ',')
{
if(valid)
yield return working;
valid = false;
working = 0;
}
else if(!Char.IsWhiteSpace(c))
{
throw new ArgumentOutOfRangeException();
}
}
if (valid)
yield return working;
}
Then you can fill your collection of ints easily enough by using the List<int> constructor:
string formIdList = "8256, 8258, 8362";
List<int> ids = new List<int>(ParseInts(formIdList));
Just depends on what you intend to do with this, how often, and how large the input will be. For small arrays parsed infrequently I would use the Linq method you already accepted. For higher volumes you might try this instead.

only return random number when it is unique

My brain is melting today and i cannot think how to do this simple bit of code. numberList is a string of numbers seperated by commas like '2, 34, 10' etc.. when i request a random number i need to check if the string has the number, if it does i want to keep requesting a random number until the random number is definitely not in the string. i cant think what kind of loop i would do to get this to work:
Random r = new Random();
public int RandomPos(int max) {
int i;
do {
i = r.Next(max) + 1;
}
while (!numberList.Contains(i.ToString()));
return i;
}
I'll just explain in text instead of code because I'm too lazy to write the code right now:
Use String.Split to break your list into an array, then (if you need to) parse it into integers.
Use Enumerable.Range(0, max).ToArray() to create a list of all the numbers you could select.
Subtract the first list from the second.
Randomly select an element from the final list.
This has the benefit that you don't need to keep picking things randomly and retrying in a potentially-infinite-but-not-really-in-practice loop.
edit: here's some code
string[] invalid = numberList.Split(", ");
var list = Enumerable.Range(0, max).Where(x => !invalid.Contains(x.ToString())).ToArray();
return list[r.Next(list.Count)];
Remove the !
do
{
i = r.Next(max) + 1;
}
while (numberList.Contains(i.ToString()));
Try it with this:
static string invalidNumbers = "0,1,2,3,4,5";
static Random random = new Random();
static int Randomize()
{
var randomInt = random.Next(0, 10);
if (!invalidNumbers.Split(',').Contains(randomInt.ToString()))
{
return randomInt;
}
else
{
return Randomize();
}
}
Providing a simple answer, you don't need Split(). This assumes no spaces between numbers, modify accordingly:
String modifiedNumberList = "," + numberList + ",";
do {
i = r.Next(max) + 1;
}
while (modifiedNumberList.Contains("," + i.ToString() + ","));
edit: I believe BrokenGlass is also right, you shouldn't have the "!", removed from my solution.
Maybe this is what you want? I used a regular while instead since I think they are easier to read, and the only thing I think you get wrong was the !.
public int RandomPos(int max) {
int i = r.Next(max);
var intList = numberList.Split(',').ToDictionary<string,int>((n) => int.Parse(n));
while(intList.Contains(i))
{
i = r.Next(max);
}
return i;
}
Assuming I need to split the numberList first to if they are in a string. That would make the third row look like:
A modification of #Dave's reply:
static string invalidNumbers = "0,1,2,3,4,5";
static Random random = new Random();
static int Randomize()
{
var randomInt = random.Next(0, 10);
var splitString = invalidNumbers.Split(',');
while (splitString.Contains(randomInt.ToString()))
{
randomInt = random.Next(0, 10);
}
return randomInt;
}
A couple ways to improve this:
1) Use a List<int> or something instead of a string to make your life easier
2) if max is small (say <1000 or something) generate the list of all possible values, order them randomly, and return numbers in sequence from that list.
As the number of "used" numbers approaches "max" you could end up in a very long loop before you get an unused number. For values of max over a couple hundred, this could actually be of consequence. This may or may not be a problem in your situation.
This code will cover all cases:
"1,2,3,4,5"...
"1, 2, 3,4,5"...
private static int GetRandomNumber(string existingNumbers, int max)
{
string[] existingNumbersArray = existingNumbers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<string> existingNumbersList = new List<string>();
foreach (string number in existingNumbersArray)
{
existingNumbersList.Add(number.Trim());
}
while (true)
{
Random rnd = new Random();
int value = rnd.Next(max);
if (!existingNumbersList.Contains(value.ToString()))
{
return value;
}
}
}
You can even take out this part:
string[] existingNumbersArray = existingNumbers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<string> existingNumbersList = new List<string>();
foreach (string number in existingNumbersArray)
{
existingNumbersList.Add(number.Trim());
}
so it will not be called each time you call GetRandomNumber function.
My addition to all the other answers..
const string invalidNumbers = "0,1,2,3,4,5";
Random random = new Random();
int value = 0;
List<int> tmpList = new List<int>();
foreach (var x in invalidNumbers.Split(','))
{
tmpList.Add(Int32.Parse(x));
}
do
{
value = random.Next(0, 10);
}
while (tmpList.Contains(value));
return value
Edit: missunderstood the question for the first post, anyway, here is a recursive solution.
It seems better to keep the numbers in a List, but if it is required to follow the format you asked, here it is:
const int MAX_ATTEMPTS = 10;
Random r = new Random();
string nlist = "2, 34, 10";
public int RandomPos(int max_val)
{
List<string> used = nlist.Replace(" ","").Split(',').ToList();
return _RandomPos(MAX_ATTEMPTS, max_val, used);
}
private int _RandomPos(int tl, int max, List<string> used)
{
if (tl <= 0)
throw new Exception("Could not generate random number. Too many tries.");
else
{
int rnum = r.Next(max);
if (!used.Contains(rnum.ToString()))
{
nlist += ", " + rnum.ToString();
return rnum;
}
else
return _RandomPos(tl - 1, max, used);
}
}
I realize there are a lot of entries, but I don't see any with some decent error checking. That being said, this will offer a few things:
Won't waste any effort when there's nothing to disqualify
Will only select from a range of possible choices
Will flag -1 if a number can't be chosen within the max range and not in the disqualifying list
So here goes:
public int RandomPos(int max)
{
// compile the list of numbers we need to disqualify
List<int> disqualified = numberList.Split(new[]{',',' '},StringSplitOptions.RemoveEmptyEntries).Select(n => int.Parse(n)).ToList();
// Nothing to check against, save the CPU cycles
if (disqualified.Count == 0)
return (new Random(DateTime.Now.Millisecond)).Next(max) + 1;
// make a list of everything that's possible for a choice
List<int> valid = Enumerable.Range(0, max).Where(r => !disqualified.Contains(r)).ToList();
// return either a valid result, or -1 if there are no valid results
return (valid.Count == 0 ? -1 : valid[(new Random(DateTime.Now.Millisecond)).Next() % valid.Count]);
}
.Split will do the work, the following code will work as well, just for fun (replace the line while (!numberList.Contains(i.ToString())); in your code instead of checking i.ToString() check ","+i.ToString()+"," PLUS the beginning and ending. You need to adjust it if you have a space after ","):
while (!numberList.StartsWith(i.ToString()+",")&&
!numberList.Contains(","+i.ToString()+",")&&
!numberList.EndsWith(","+i.ToString()));
// if numberList is large then use HashSet<int> rather than a plain int[] array
int[] nums = numberList.Split(new[] { ',', ' ' },
StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse)
.ToArray();
int i;
while (nums.Contains(i = r.Next(max) + 1));
return i;
(You should also add a check to ensure that you don't end up in an infinite loop if/when numberList contains all the possible values that might be produced by the rng.)

c# string split and combine

i have a string that conatin 5 numbers like
'1,4,14,32,47'
i want to make from this string 5 strings of 4 number in each one
like :
'1,4,14,32'
'1,4,14,47'
'1,4,32,47'
'1,14,32,47'
'4,14,32,47'
what is the simple/faster way to do it
is the way of convert this to array unset every time diffrent entery and combine
them back to string ?
is there a simple way to do it ?
thanks
How about something like
string s = "1,4,14,32,47";
string r = String.Join(",", s.Split(',').Where((x, index) => index != 1).ToArray());
Using string.Split() you can create a string array. Loop through it, so that in each loop iteration you indicate which element should be skipped (on the first pass, ignore the first element, on the second pass, ignore the second element).
Inside that loop, create a new array that contains all elements but the one you want to skip, then use string.Join() to create each result.
Have a look at:
http://msdn.microsoft.com/en-us/magazine/ee310028.aspx Here you'll find an example in F# who will give the correct background in combinations and permutations (that's how is called what you need). There is code too, I think it's easy to translate it in C#
Example of Code in C# (text in Italian but code in English)
For those who need a more generic algorithm, here is one which gives n length subsets of m items:
private void GetPermutations()
{
int permutationLength = 4;
string s = "1,4,14,32,47";
string[] subS = s.Split(',');
int[] indexS = new int[permutationLength];
List<string> result = new List<string>();
IterateNextPerm(permutationLength, indexS, subS, result);
// Result will hold all your genberated data.
}
private void IterateNextPerm(int permutationLength, int[] pIndexes, string[] subS, List<string> result)
{
int maxIndexValue = subS.Count() - 1;
bool isCorrect = true;
for (int index = 0; index < permutationLength - 1; index++)
{
if (pIndexes[index] >= pIndexes[index + 1])
{
isCorrect = false;
break;
}
}
// Print result if correct
if (isCorrect)
{
string strNewPermutation = string.Empty;
for (int index = 0; index < permutationLength; index++)
{
strNewPermutation += subS[pIndexes[index]] + ",";
}
result.Add(strNewPermutation.TrimEnd(','));
}
// Increase last index position
pIndexes[permutationLength - 1]++;
// Check and fix if it's out of bounds
if (pIndexes[permutationLength - 1] > maxIndexValue)
{
int? lastIndexIncreased = null;
// Step backwards and increase indexes
for (int index = permutationLength - 1; index > 0; index--)
{
if (pIndexes[index] > maxIndexValue)
{
pIndexes[index - 1]++;
lastIndexIncreased = index - 1;
}
}
// Normalize indexes array, to prevent unnecessary steps
if (lastIndexIncreased != null)
{
for (int index = (int)lastIndexIncreased + 1; index <= permutationLength - 1; index++)
{
if (pIndexes[index - 1] + 1 <= maxIndexValue)
{
pIndexes[index] = pIndexes[index - 1] + 1;
}
else
{
pIndexes[index] = maxIndexValue;
}
}
}
}
if (pIndexes[0] < maxIndexValue)
{
IterateNextPerm(permutationLength, pIndexes, subS, result);
}
}
I know that it's not the nicest coding, but I've written it right now in the last half an hour so I'm sure there are things to tweek there.
Have fun coding!
var elements = string.Split(',');
var result =
Enumerable.Range(0,elements.Length)
.Reverse()
.Select(
i=>
string.Join(","
Enumerable.Range(0,i).Concat(Enumerable.Range(i+1,elements.Length - i - 1))
.Select(j=>elements[j]).ToArray() // This .ToArray() is not needed in NET 4
)
).ToArray();
your wording is pretty confusing...but the example is clear enough. just split it on commas, then remove one index, then use string.Join(",", list); to put it back together..

Categories

Resources