Related
This question will be mainly opinion based I think, however, I have created my own Base64 ID generator for the visible ID column of my database tables for security reasons, (I saw a video as to why YouTube do it and it made sense although I see other ways of security that probably aren't as problematic). It handles the HIGHLY unlikely event that a duplicate may arise, however, I am curious to know if this is used as a standard for say, YouTube's video IDs.
Program.cs
class Program
{
static void Main(string[] args)
{
var ids = new HashSet<string>();
var count = 0; // for testing only
while (count < 8)
{
ids.Add(Base64Id.GenerateId(ids));
Console.ReadLine();
count++; // for testing only
}
}
}
Base64Id.cs
public static class Base64Id
{
private static int IdSize = 1; // Should be 11
private static readonly string[] AllowedChars = {
"0", "1", "2", "3", "4", "5", "6", "7"//,
//"8", "9", "a", "b", "c", "d", "e", "f",
//"g", "h", "i", "j", "k", "l", "m", "n",
//"o", "p", "q", "r", "s", "t", "u", "v",
//"w", "x", "y", "z", "A", "B", "C", "D",
//"E", "F", "G", "H", "I", "J", "K", "L",
//"M", "N", "O", "P", "Q", "R", "S", "T",
//"U", "V", "W", "X", "Y", "Z", "-", "_"
};
private static Random _random = new Random();
/// <summary>
/// To generate a Base64 ID and check to make sure the ID is not already in use.
/// </summary>
/// <param name="usedIds">List of IDs already in use from the Database or other source.</param>
/// <returns>New Base64 ID</returns>
public static string GenerateId(HashSet<string> usedIds)
{
var autoGenId = "";
do
{
autoGenId = "";
for (var i = 0; i < IdSize; i++)
autoGenId += GetRandomChar();
#if DEBUG
_DEBUG_(usedIds.Count() + 1, autoGenId);
#endif
}
while (IsTaken(autoGenId, usedIds));
return autoGenId;
}
private static string GetRandomChar()
{
var i = _random.Next(0, AllowedChars.Length);
return AllowedChars[i];
}
private static bool IsTaken(string id, HashSet<string> usedIds)
{
var check = usedIds.Any(i => id.Contains(i));
if (check)
return true;
return false;
}
private static void _DEBUG_(int count, string id)
{
Console.WriteLine(String.Format("{0}:\t{1}", count, id));
}
}
I believe that this will work like a charm for my purposes and without any issues, as it does during testing. However, once I narrow it down to 8 chars and with an ID size of length 1, it kicks up serious errors due to the constant looping after only 6 of the 8 expected outputs have happened.
I understand this is from a random number being hit each time and the less that there are to choose from, the more likely the loop must occur.
I know ways to remedy this, however on the scale I plan for, it is crazy to think of, such as having an array/list of every possibility and removing the selected ID.
This is my question;
Do the likes of Youtube know of this problem, and just don't care due to the size of possible IDs.
They just have much greater thought put into the class.
They do not care about the processing cost for such high numbers and handle every small detail.
Or do they use Base64Encode in there code instead of auto-generating it.
I would like to know your opinions and suggestions on how to improve the code or if it even matters which such huge numbers. I have answered with what I think could be possible ways to improve it.
UPDATE
I left two consoles run over the weekend, one using List and the other using HashSet, and the difference between the processed records is on a whole other level. So I have changed the above code to have HashSet instead of List, and set the code up to run automatically.
I believe that for the amount of possible IDs, it is not worth the serious amount of processing it would require to make sure that the ID is unique the further it goes to the end of the list as there are 73,786,976,294,838,206,464 possibilities.
Say for ten possible IDs 1 -> 10, if 2 have been selected already, then the next time, it has a 20% chance of be duplicated, and if 8 have been selected it will be 80% chance, each time. Using probability, this will stack and lower your chances of getting a unique ID to appear.
This is quiet bad once the numbers are low, it took 14539279 iterations the first time and 662984 iterations the second time to get all 8 unique IDs to appear. With bigger numbers I know it will take longer to get to this breaking point, but it will be much worse.
I think that this could be split up into a binary tree once the numbers get bigger as to make the most of it, once each block of say a couple 100k or million reaches 50% or 60% usage, forget the rest and move onto the next range.
I think this could be a good way to try and speed up processing a unique ID into the later stages of the possible list.
I have a predefined string array with the letters from A to Q :
string[] SkippedAreasArray = new string[] {"A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q"};
In a TextBox inside Windows Form the user can enter skippedAreas like this: A,B,C,D... there is validations and restrictions to use only letters and commas so the input is guaranteed to be in this format.
What I do is taking the user input and populate another string array:
string[] SkippedAreasFromForm = new string[17];
...
SkippedAreasFromForm = (txtSkippedAreas.Text).Split(',');
Now comes the tricky part for which I'm seeking assistance. The user must enter Number of areas for example - 3. Which mean he is working only with A, B, and C. If the number of areas was 2 then he can only use A and B if the number of areas was 4 then A, B, C and D are available and so on.
What I need is to check if in the array SkippedAreasFromForm which is populated with the user input there is an area that doesn't match the above criteria.
What this mean in terms of coding - I need to take every element from SkippedAreasFromForm, take it's integer value from the predefined SkippedAreasArray and see if this value is equal or greater (>=) of the value that he entered as `Number of areas. If there is an area which is outside the scope of the selected number an error should be shown.
What I have right now is :
foreach (string tempAreaValue in SkippedAreasFromForm)
{
for (int i = 0; i < SkippedAreasArray.Length; i++)
{
if (tempAreaValue == SkippedAreasArray[i])
{
if ((i + 1) > entity.AreasCnt)
{
MessageBox.Show("You must use areas only within the Number of Areas scope!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
txtSkippedAreas.Focus();
return false;
}
}
}
}
For the few test I made it works. But first - at least for me it seems over complicated. Second - I'm not sure that this algorithm is working exactly as I need to or I'm just having correct results by luck. Third - I'm coding C# for 2months now and this seems to me like an excellent candidate for a LINQ expression - do you think it would be better using LINQ and I would appreciate a help in the transformation.
I think you're just looking for IndexOf:
int index = SkippedAreasArray.IndexOf(tempAreaValue);
if (index >= entity.AreasCnt)
{
...
}
(You might also want to check for index being -1, which would occur if the element wasn't in the list at all. Also, consider duplicates - can the user enter A, A, A?)
If you are looking for the elements in the array from start index to max string elements based on areaNumber, then:
int areaNumber = 4;
var result = SkippedAreasArray.Select((r, i) => new { Value = r, Index = i })
.Where(r => r.Index <= areaNumber - 1)
.Select(r => r.Value)
.ToArray();
For areaNumber 4 you will get "A", "B", "C", "D"
EDIT:
I'm looking if every element from the user input (which is array
A,C,H,Q..) is inside the area determined from the areNumber (from comment)
Suppose your userInputArray is:
string[] userInputArray = new string[] {"A", "C", "H", "Q"};
Then you can use Enumerable.Except in the following manner:
if(result.Except(userInputArray).Any())
{
//Invalid
}
else
{
//Valid
}
To start this is a homework assignment and I'm having a bit of trouble and would like some suggestions. So far I've created this application to take 10 inputted values from a user and store them into an array. I pass the array to the SmallAndLarge method where it displays derermines the smallest and largest of the 10 values using Sort() but now i have to display the smallest and largest of the 10 values the user entered and am having a trouble. Any help at all would be great!!
Also one other problem ive noticed that if the values 1 through 10 are entered 10 will be before 2 and after one when the array is sorted and displayed. Why is this?
namespace SmallAndLarge
{
class Program
{
static void Main()
{
int found = 0;
string[] numbers = new string[10];
for (int i = 0; i < numbers.Length; i++)
{
Console.Write("Enter ten numbers --> ");
numbers[i] = Console.ReadLine();
}
SmallestAndLargest(numbers);
}
public static int SmallestAndLargest(string[] numbers1)
{
int x;
Array.Sort(numbers1);
for (x = 0; x < numbers1.Length; ++x)
Console.Write("{0} ", numbers1[x]);
return x;
}
}
}
You can use Linq to cast and return the Largest/Smallest using Min/Max
string[] numbers = new string[] {"1","2","3","15","30","7" };
var converted = numbers.Select(int.Parse);
int largest = converted.Max();
int smallest = converted.Min();
You have the numbers as strings. That explains why "10" comes before "2", it's for the same reason that "am" comes before "i" in an alphabetic sort. The first character of "10" is '1' which comes before '2'.
So before you sort the array, or search for the max og min value, you need to convert the strings into some numeric type. It could be the int type, for example. You would hold them in an array of type int[], not string[].
To convert from the string given by Console.ReadLine into int, use an overload of int.Parse or int.TryParse methods, for example.
If the user should be allowed to type non-integral numbers like 3.14, you could use decimal instead of int.
It looks like you are iterating over the array. I would do the following:
Create two int variables (small and large, respectively)
Set them both equal to the first element of the array
Loop through the elements;
For each element[i], (convert the element to int using int.Parse() or equivalent).
if element[i] > large, large = element[i]; if element[i] < small, small = element[i]
There is no need for sort if you only need min and max. And I would store the ints into and int array rather than string array, make the conversion when the user inputs the value. That way, when an invalid value is input, your program fails right then.
Here's a very simple, though long winded answer. Add the values to a List and use Min/Max returns. Your already using a for loop to iterate through the list, no reason to do it twice.
static void Main(string[] args)
{
string[] StringValues = new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10","Not Number" };
List<int> ConvertedStrings = new List<int>();
foreach (string S in StringValues)
{
ConvertedStrings = ParseStringToInt(S, ConvertedStrings);
}
Console.WriteLine();
Console.WriteLine("Max Value: " + ConvertedStrings.Max().ToString());
Console.WriteLine("Min Value: " + ConvertedStrings.Min().ToString());
Console.ReadLine();
}
static private List<int> ParseStringToInt(string Input, List<int> ConvertedStrings)
{
int ConvertedValue = 0;
if (int.TryParse(Input, out ConvertedValue))
{
Console.WriteLine(Input + " sucessfully parsed and added...");
ConvertedStrings.Add(ConvertedValue);
return ConvertedStrings;
}
else
{
Console.WriteLine(Input + " failed to parse...");
}
return ConvertedStrings;
}
sa_ddam213 has got the killer answer though :P
I need help understanding how to write a permutation algorithm. (if this is even permutation, they have to be in order and use the same values).
List<string> str = new List<string>{"a", "b", "c", "d"};
How can I get a list of each permutation available in this list? For eg.
a, b, c, d
ab, c, d
ab, cd
abc, d
abcd
a, bc, d
a, bcd
a, b, cd
For some reason I cant find a pattern to start with. I'd also like to be able to disregard permutation when a joined string has a count of like X characters. So if X was 4, in that list, number 5 wouldn't exist and there would be 7 permutations.
private List<string> permute(List<string> values, int maxPermutation)
{
//alittle help on starting it would be great :)
}
I looked and read this, but he does not keep the order.
This is rather straightforward: you have three spots where you could either put a comma or to put nothing. There are eight combinations corresponding to 2^3 binary numbers.
For each number from 0 to 7, inclusive, produce a binary representation. Put a comma in each position where binary representation has 1; do not put comma where there's zero.
for (int m = 0 ; m != 8 ; m++) {
string s = "a";
if ((m & 1) != 0) s += ",";
s += "b";
if ((m & 2) != 0) s += ",";
s += "c";
if ((m & 4) != 0) s += ",";
s += "d";
Console.WriteLine(s);
}
You could take a recursive approach: Take the first letter, build all possible combinations starting with the second one (this is the recursion...) and prepend the first letter to each of them. Then take the first two letters together, recursively build all combinations starting with the third one. And so on ...
As for you additional requirement: If you want to exclude all "combinations" containing a string with X letters, just skip this number when constructing the first string.
The Binary approach above is correct and this is actually a partitioning problem (but not "The Partitioning Problem") and not a permutation problem.
http://en.wikipedia.org/wiki/Partition_of_a_set
Watch out because of the number of partitions grows faster than exponentially (e^e^n) so it will be really slow for large strings.
Try the following code. I haven't tested it, but I think it's what you are looking for.
List<string> str = new List<string>{ "a", "h", "q", "z", "b", "d" };
List<List<string>> combinations = combine(str.OrderBy(s=>s).ToList());
List<List<string>> combine(List<string> items)
{
List<List<string>> all = new List<List<string>>();
// For each index item in the items array
for(int i = 0; i < items.Length; i++)
{
// Create a new list of string
List<string> newEntry = new List<string>();
// Take first i items
newEntry.AddRange(items.Take(i));
// Concatenate the remaining items
newEntry.Add(String.concat(items.Skip(i)));
// Add these items to the main list
all.Add(newEntry);
// If this is not the last string in the list
if(i + 1 < items.Length)
{
// Process sub-strings
all.AddRange(combine(items.Skip(i + 1).ToList()));
}
}
return all;
}
If you need to generate combinations (or permutations or variations), then Combinatorics is a fantastic library.
I have an array for example :
public static string[] elmentnames = { "A", "B", "C", "D", "E","F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R","S", "T", "U", "V", "W", "X", "Y", "Z"};
and I want to select items from index 0 to 15 and put then in a list of string
How?
Presuming the elements are already in the order you want them, you can do it like:
List<string> elementNamesList = elmentnames.Take(15).ToList();
.Take(15) is the first 15 elements. From index 0 to 15 is actually 16 elements, so you can change that to .Take(16) if that's what you meant.
Any of these will work:
var list = elmentnames.Take(16).ToList();
var list = elmentnames.Where((x, i) => i <= 15).ToList();
var array = new string[16];
Array.Copy(elmentnames, array, 16);
var list = new List<string>(array);
You should try creating a for loop that goes threw every element of your current array and ads them to an ArrayList I am not familiar with C# but the concept its the same in every programming language.