I'm wondering if there is a sweet way I can do this in LINQ or something but I'm trying to most evenly distribute the letters of the alphabet across X parts where X is a whole number > 0 && <= 26. For example here might be some possible outputs.
X = 1 : 1 partition of 26
X = 2 : 2 partitions of 13
X = 3 : 2
partitions of 9 and one partition of 8
etc....
Ultimately I don't want to have any partitions that didn't end up getting at least one and I'm aiming to have them achieve the most even distribution that the range of difference between partition sizes is as small as posssible.
This is the code I tried orginally:
char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
int pieces = (int)Math.Round((double)alphabet.Count() / numberOfParts);
for (int i = 0; i < numberOfParts.Count; ++i) {
char[] subset = i == numberOfParts.Count - 1 ? alphabet.Skip(i * pieces).ToArray()
: alphabet.Skip(i * pieces).Take(pieces).ToArray();
... // more code following
This seemed to be working fine at first but I realized in testing that there is a problem when X is 10. Based on this logic I'm getting 8 groups of 3 and one group of 2, leaving the 10th group 0 which is no good as I'm going for the most even distribution.
The most ideal distribution for 10 in this case would be 6 groupings of 3 and 4 groupings of 2. Any thoughts on how this might be implemented?
In general, the easiest way to implement the logic is using the modulo operator, %. Get familiar with this operator; it's very useful for the situations where it helps. There are a number of ways to write the actual code to do the distribution of letters, using arrays or not as you wish etc., but this short expression should give you an idea:
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(letter) % partitionCount
This expression gives the zero-based index of the partition in which to deposit an upper-case letter. The string is just shown for convenience, but could be an array or some other way of representing the alphabet. You could loop over the alphabet, using logic similar to the above to choose where to deposit each letter. Up to you would be where to put the logic: inside a loop, in a method, etc.
There's nothing magical about modular arithmetic; it just "wraps around" after the end of the set of usable numbers is reached. A simple context in which we've all encountered this is in division; the % operator is essentially just giving a division remainder. Now that you understand what the % operator is doing, you could easily write your own code to do the same thing, in any language.
Putting this all together, you could write a utility, class or extension method like this one--note the % to calculate the remainder, and that simple integer division discards it:
/// <summary>
/// Returns partition sized which are as close as possible to equal while using the indicated total size available, with any extra distributed to the front
/// </summary>
/// <param name="totalSize">The total number of elements to partition</param>
/// <param name="partitionCount">The number of partitions to size</param>
/// <param name="remainderAtFront">If true, any remainder will be distributed linearly starting at the beginning; if false, backwards from the end</param>
/// <returns>An int[] containing the partition sizes</returns>
public static int[] GetEqualizedPartitionSizes(int totalSize, int partitionCount, bool remainderAtFront = true)
{
if (totalSize < 1)
throw new ArgumentException("Cannot partition a non-positive number (" + totalSize + ")");
else if (partitionCount < 1)
throw new ArgumentException("Invalid partition count (" + partitionCount + ")");
else if (totalSize < partitionCount)
throw new ArgumentException("Cannot partition " + totalSize + " elements into " + partitionCount + " partitions");
int[] partitionSizes = new int[partitionCount];
int basePartitionSize = totalSize / partitionCount;
int remainder = totalSize % partitionCount;
int remainderPartitionSize = basePartitionSize + 1;
int x;
if (remainderAtFront)
{
for (x = 0; x < remainder; x++)
partitionSizes[x] = remainderPartitionSize;
for (x = remainder; x < partitionCount; x++)
partitionSizes[x] = basePartitionSize;
}
else
{
for (x = 0; x < partitionCount - remainder; x++)
partitionSizes[x] = basePartitionSize;
for (x = partitionCount - remainder; x < partitionCount; x++)
partitionSizes[x] = remainderPartitionSize;
}
return partitionSizes;
}
I feel like the simplest way to achieve this is to perform a round robin distribution on each letter. Cycle through each letter of the alphabet and add to it, then repeat. Have a running count that determines what letter you will be putting your item in, then when it hits >26, reset it back to 0!
What I did in one app I had to distribute things in groups was something like this
var numberOfPartitions = GetNumberOfPartitions();
var numberOfElements = GetNumberOfElements();
while (alphabet.Any())
{
var elementsInCurrentPartition = Math.Ceil((double)numberOfPartitions / numberOfElements)
for (int i = 0; i < elementsInCurrentPartition; i++)
{
//fill your partition one element at a time and remove the element from the alphabet
numberOfElements--;
}
numberOfPartitions--;
}
This won't give you the exact result you expected (i.e. ideal result for 10 partitions) but it's pretty close.
p.s. this isn't tested :)
A pseudocode algorithm I have now tested:
Double count = alphabet.Count()
Double exact = count / numberOfParts
Double last = 0.0
Do Until last >= count
Double next = last + exact
ranges.Add alphabet, from:=Round(last), to:=Round(next)
last = next
Loop
ranges.Add can ignore empty ranges :-)
Here is a LinqPad VB.NET implementation of this algorithm.
So a Linq version of this would be something like
Double count = alphabet.Count();
Double exact = count / numberOfParts;
var partitions = Enumerable.Range(0, numberOfParts + 1).Select(n => Round((Double)n * exact));
Here is a LinqPad VB.NET implementation using this Linq query.
(sorry for formatting, mobile)
First, you need something like a batch method:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int groupSize)
{
var tempSource = source.Select(n => n);
while (tempSource.Any())
{
yield return tempSource.Take(groupSize);
tempSource = tempSource.Skip(groupSize);
}
}
Then, just call it like this:
var result = alphabet.Batch((int)Math.Ceiling(x / 26.0));
Related
I have the following problem here:My input is several lines of 2 digit numbers and I need to make a new number using the second digit of the first number and the first of the next one.
Example:
int linesOfNumbers = Convert.ToInt32(Console.ReadLine());
for(int i = 0,i<linesOfNumbers,i++)
{
int numbers = Conver.ToInt32(Console.ReadLine());
//that's for reading the input
}
I know how to separate the numbers into digits.My question is how to merge them.
For example if your input is 12 and 21 the output should be 22.
I like oRole's answer, but I think they're missing a couple things with the example input that you provided in your comment. I'll also point out some of the errors in the code that you have.
First off, if you're only given the input 12,23,34,45, then you don't need to call Console.ReadLine within your for loop. You've already gotten the input, you don't need to get any more (from what you've described).
Secondly, unless you're doing mathematical operations, there is no need to store numerical data as ints, keep it as a string, especially in this case. (What I mean is that you don't store Zip Codes in a database as a number, you store it as a string.)
Now, onto the code. You had the right way to get your data:
var listOfNumbers = Console.ReadLine();
At that point, listOfNumbers is equal to "12,23,34,45". If you iterate on that variable as a string, you'll be taking each individual character, including the commas. To get each of the numbers to operate on, you'll need to use string.Split.
var numbers = listOfNumbers.Split(',');
This turns that list into four different two character numbers (in string form). Now, you can iterate over them, but you don't need to worry about converting them to numbers as you're operating on the characters in each string. Also, you'll need a results collection to put everything into.
var results = new List<string>();
// Instead of the regular "i < numbers.Length", we want to skip the last.
for (var i = 0; i < numbers.Length - 1; i++)
{
var first = numbers[i];
var second = numbers[i + 1]; // This is why we skip the last.
results.Add(first[1] + second[0]);
}
Now your results is a collection of the numbers "22", "33", and "44". To get those back into a single string, you can use the helper method string.Join.
Console.WriteLine(string.Join(",", results));
You could use the string-method .Substring(..) to achieve what you want.
If you want to keep int-conversion in combination with user input, you could do:
int numA = 23;
int numB = 34;
int resultAB = Convert.ToInt16(numA.ToString().Substring(1, 1) + numB.ToString().Substring(0, 1));
Another option would be to take the users input as string values and to convert them afterwards like that:
string numC = "12";
string numD = "21";
int resultCD = Convert.ToInt16(numC.Substring(1, 1) + numD.Substring(0, 1));
I hope this code snippet will help you combining your numbers. The modulo operator (%) means: 53 / 10 = 5 Rest 3
This example shows the computation of the numbers 34 and 12
int firstNumber = 34 - (34 % 10) // firstNumber = 30
int secondNumber = 12 % 10; // secondNumber = 2
int combined = firstNumber + secondNumber; // combined = 32
EDIT (added reading and ouput code):
boolean reading = true;
List<int> numbers = new ArrayList();
while(reading)
{
try
{
int number = Convert.ToInt32(Console.ReadLine());
if (number > 9 && number < 100) numbers.Add(number);
else reading = false; // leave reading process if no 2-digit-number
}
catch (Exception ex)
{
// leave reading process by typing a character instead of a number;
reading = false;
}
}
if (numbers.Count() > 1)
{
List<int> combined = new ArrayList();
for (int i = 1; i <= numbers.Count(); i++)
{
combined.Add((numbers[i-1] % 10) + (numbers[i] - (numbers[i] % 10)));
}
//Logging output:
foreach (int combination in combined) Console.WriteLine(combination);
}
As you mention, if you already have both numbers, and they are always valid two digit integers, following code should work for you.
var num1 = 12;
var num2 = 22;
var result = (num2 / 10)*10 + (num1 % 10);
num2/10 returns the first digit of second number, and num1 % 10 returns the second digit of the first number.
The % and / signs are your savior.
If you want the 'ones' digit of a number (lets call it X), simply do X%10 - the remainder will be whatever number is in the 'ones' digit. (23%10=3)
If, instead, the number is two digits and you want the 'tens' digit, divide it by ten. (19/10=1).
To merge them, multiply the number you want to be in the 'tens' digit by ten, and add the other number to it (2*10+2=22)
There are other solutions like substring, etc and many one have already given it above. I am giving the solution VIA LINQ, note that this isn't efficient and it's recommended only for learning purpose here
int numA = 12;
int numB = 21 ;
string secondPartofNumA = numA.ToString().Select(q => new string(q,1)).ToArray()[1]; // first digit
string firstPartofNumB = numB.ToString().Select(q => new string(q,1)).ToArray()[0]; // second digit
string resultAsString = secondPartofNumA + firstPartofNumB;
int resultAsInt = Convert.ToInt32(resultAsString);
Console.WriteLine(resultAsString);
Console.WriteLine(resultAsInt);
I need to find all number subset to get a number N by summing its elements. I don't know how to get through this type of combination problem. In this combination, order matters for different numbers.
example for the number N=4
1 + 1 + 1 + 1
2 + 1 + 1
1 + 2 + 1
1 + 1 + 2
2 + 2
3 + 1
1 + 3
Zeros are not important for me. So how can I get such number sets as an array for an exact number?
What you're looking for are called integer compositions, or ordered partitions.
Compositions can be generated recursively (in lexicographic order, if I'm not mistaken) as follows:
public static IEnumerable<List<int>> Compositions(int n)
{
if (n < 0)
throw new ArgumentOutOfRangeException(nameof(n));
return GenerateCompositions(n, new List<int>());
}
private static IEnumerable<List<int>> GenerateCompositions(int n, List<int> comp)
{
if (n == 0)
{
yield return new List<int>(comp); // important: must make a copy here
}
else
{
for (int k = 1; k <= n; k++)
{
comp.Add(k);
foreach (var c in GenerateCompositions(n - k, comp))
yield return c;
comp.RemoveAt(comp.Count - 1);
}
}
}
Not tested! This was transcribed from a Python implementation. If anyone would like to make corrections or update the code with more idiomatic C#, feel free.
Also, as #aah noted, the number of compositions of n is 2^(n-1), so this becomes unwieldy even for modest n.
If order doesn't matter, there are simply 2^(N-1) possibilities. (Your example doesn't have 2 + 2 or 4)
You can then represent any sequence by its binary representation. To generate, imagine N 1's in a row, so there are N-1 "spaces" between them. Choosing any subset of spaces, you merge any 1's that are adjacent via a chosen space. You can verify this is 1-1 to all possible sets by expanding any such sequence and inserting these spaces.
Okay, so this is my challenge taken from CodeEval. I have to read numbers from a file that is formatted in a standard way, it has a pair of numbers separated by a comma on each line (x, n). I have to read in the pair values and process them, then print out the smallest multiple of n which is greater than or equal to x, where n is a power of 2.
EXACT REQUIREMENT: Given numbers x and n, where n is a power of 2, print out the smallest multiple of n which is greater than or equal to x. Do not use division or modulo operator.
I have come up with a number of solutions, but none of them satisfy the computer's conditions to let me pass the challenge. I only get a partial completion with scores that vary from 30 to 80 (from 100).
I'm assuming that my solutions do not pass the speed but more likely the memory-usage requirements.
I would greatly appreciate it if anyone can enlighten me and offer some better, more efficient solutions.
Here are two of my solutions:
var filePath = #"C:\Users\myfile.txt";
int x;
int n;
using (var reader = new StreamReader(filePath))
{
string numsFile = string.Empty;
while ((numsFile = reader.ReadLine()) != null)
{
var nums = numsFile.Split(',').ToArray();
x = int.Parse(nums[0]);
n = int.Parse(nums[1]);
Console.WriteLine(DangleNumbers(x, n));
}
}
<<<>>>
var fileNums = File.ReadAllLines(filePath);
foreach (var line in fileNums)
{
var nums = line.Split(',').ToArray();
x = int.Parse(nums[0]);
n = int.Parse(nums[1]);
Console.WriteLine(DangleNumbers(x, n));
}
Method to check numbers
public static int DangleNumbers(int x, int n)
{
int m = 2;
while ((n * m) < x)
{
m += 2;
}
return m * n;
}
I'm fairly new to C# and programming but these two ways I found to get the best score from several others I have tried. I'm thinking that it's not too optimal for a new string to be created on each iteration, nor do I know how to use a StringBuilder and get the values into an Int from it.
Any pointers in the right direction would be appreciated as I would really like to get this challenge passed.
The smallest multiple of n that is larger or equal to x is likely this:
if(x <= n)
{
return n;
}
else
{
return x % n == 0 ? x : (x/n + 1) * n;
}
As x and n are integers, the result of x/n will be truncated (or effectively rounded down). So the next integer larger than x that is a multiple of n is (x/n + 1) * n
Since you missed the requirements, the modulo version was the most obvious choice. Though you still got your method wrong. m = 2 would not result in the smallest being returned but it could actually be the double of the smallest if n is already larger than x.
x = 7, n = 8 would get you 16 instead of 8.
Also adding 2 to m would result in a similar problem.
x = 5, n = 2 would get you 8 instead of 6.
use the following method instead:
public static int DangleNumbers(int x, int n)
{
int result = n;
while(result < x)
result += n;
return result;
}
Still capable of begin optimized but at least right according to the (now) stated constraints.
I have tried to improve the solution with some suggestions from you guys and take the variables outside the loop and drop the ToArray() call which was redundant.
static void Main(string[] args)
{
var filePath = #"C:\Users\sorin\Desktop\sorvas.txt";
int x;
int n;
string[] nums;
using (var reader = new StreamReader(filePath))
{
string numsFile = string.Empty;
while ((numsFile = reader.ReadLine()) != null)
{
nums = numsFile.Split(',');
x = int.Parse(nums[0]);
n = int.Parse(nums[1]);
Console.WriteLine(DangleNumbers(x, n));
}
}
}
public static int DangleNumbers(int x, int n)
{
int m = 2;
while ((n * m) < x)
{
m += 2;
}
return m * n;
}
So it looks like this. The thing is that even if now the numbers have slightly improved, I got a lower score.
May it be their system to blame ?
Using the first option of reading line by line (rather than reading all lines) is clearly going to use less memory (except potentially in the case where the file is very small (eg "1,1") in which case the overhead of the reader may cause problems but at that point the memory used is probably irrelevant.
Likewise declaring the variables outside the loop is generally better but in this case since the objects are value types I'm not sure it makes a difference.
Lastly the most efficient way of doing your DangleNumbers method is probably using bitwise logic operators and the fact that n is always a power of 2. Here is my attempt:
public static int DangleNumbers3(int x, int n)
{
return ((x-1) & ~(n-1))+n;
}
Essentially it relies on the fact that in binary a power of n is always a 1 followed by zero or more zeros. Thus a multiple of n will always end in that same number of zeros. So if n has M zeros after the one then you can take the binary form of x and if it already ends in M zeros then you have your answer. Otherwise you zero out the last M digits at which point you have the multiple of n that is just under x and then you add 1.
In the code ~(n-1) is a bitmask that has M zeros at the end and the leading digits are all 1. Thus when you AND it with a number it will zero out the trailing digits. I apply this to (x-1) to avoid having to do the check for if it is already the answer and have special cases.
It is important to note that this only works because of the special form of n as a power of 2. This method avoids the need for any loops and thus should run much faster (it has five operations total and no branching at all compared to other looping methods which will tend to have at the very least an operation and a comparison per loop.
I have a number like 601511616
If all number's length is multiple of 3, how can a split the number into an array without making a string
Also, how can I count numbers in the int without making a string?
Edit: Is there a way to simply split the number, knowing it's always in a multiple of 3... good output should look like this: {616,511,601}
You can use i % 10 in order to get the last digit of integer.
Then, you can use division by 10 for removing the last digit.
1234567 % 10 = 7
1234567 / 10 = 123456
Here is the code sample:
int value = 601511616;
List<int> digits = new List<int>();
while (value > 0)
{
digits.Add(value % 10);
value /= 10;
}
// digits is [6,1,6,1,1,5,1,0,6] now
digits.Reverse(); // Values has been inserted from least significant to the most
// digits is [6,0,1,5,1,1,6,1,6] now
Console.WriteLine("Count of digits: {0}", digits.Count); // Outputs "9"
for (int i = 0; i < digits.Count; i++) // Outputs "601,511,616"
{
Console.Write("{0}", digits[i]);
if (i > 0 && i % 3 == 0) Console.Write(","); // Insert comma after every 3 digits
}
IDEOne working demonstration of List and division approach.
Actually, if you don't need to split it up but only need to output in 3-digit groups, then there is a very convenient and proper way to do this with formatting.
It will work as well :)
int value = 601511616;
Console.WriteLine("{0:N0}", value); // 601,511,616
Console.WriteLine("{0:N2}", value); // 601,511,616.00
IDEOne working demonstration of formatting approach.
I can't understand your question regarding how to split a number into an array without making a string - sorry. But I can understand the question about getting the count of numbers in an int.
Here's your answer to that question.
Math.Floor(Math.Log10(601511616) + 1) = 9
Edit:
Here's the answer to your first question..
var n = 601511616;
var nArray = new int[3];
for (int i = 0, numMod = n; i < 3; numMod /= 1000, i++)
nArray[i] = numMod%1000;
Please keep in mind there's no safety in this operation.
Edit#3
Still not perfect, but a better example.
var n = 601511616;
var nLength = (int)Math.Floor(Math.Log10(n) + 1)/ 3;
var nArray = new int[nLength];
for (int i = 0, numMod = n; i < nLength; numMod /= 1000, i++)
nArray[i] = numMod%1000;
Edit#3:
IDEOne example http://ideone.com/SSz3Ni
the output is exactly as the edit approved by the poster suggested.
{ 616, 511, 601 }
Using Log10 to calculate the number of digits is easy, but it involves floating-point operations which is very slow and sometimes incorrect due to rounding errors. You can use this way without calculating the value size first. It doesn't care if the number of digits is a multiple of 3 or not.
int value = 601511616;
List<int> list = new List<int>();
while (value > 0) // main part to split the number
{
int t = value % 1000;
value /= 1000;
list.Add(t);
}
// Convert back to an array only if it's necessary, otherwise use List<T> directly
int[] splitted = list.ToArray();
This will store the splitted numbers in reverse order, i.e. 601511616 will become {616, 511, 601}. If you want the numbers in original order, simply iterate the array backwards. Alternatively use Array.Reverse or a Stack
Since you already know they are in multiples of 3, you can just use the extracting each digit method but use 1000 instead of 10. Here is the example
a = 601511616
b = []
while(a):
b.append(a%1000)
a = a//1000
print(b)
#[616, 511, 601]
I have a situation where by I need to create tens of thousands of unique numbers. However these numbers must be 9 digits and cannot contain any 0's. My current approach is to generate 9 digits (1-9) and concatenate them together, and if the number is not already in the list adding it into it. E.g.
public void generateIdentifiers(int quantity)
{
uniqueIdentifiers = new List<string>(quantity);
while (this.uniqueIdentifiers.Count < quantity)
{
string id = string.Empty;
id += random.Next(1,10);
id += random.Next(1,10);
id += random.Next(1,10);
id += " ";
id += random.Next(1,10);
id += random.Next(1,10);
id += random.Next(1,10);
id += " ";
id += random.Next(1,10);
id += random.Next(1,10);
id += random.Next(1,10);
if (!this.uniqueIdentifiers.Contains(id))
{
this.uniqueIdentifiers.Add(id);
}
}
}
However at about 400,000 the process really slows down as more and more of the generated numbers are duplicates. I am looking for a more efficient way to perform this process, any help would be really appreciated.
Edit: - I'm generating these - http://www.nhs.uk/NHSEngland/thenhs/records/Pages/thenhsnumber.aspx
As others have mentioned, use a HashSet<T> instead of a List<T>.
Furthermore, using StringBuilder instead of simple string operations will gain you another 25%. If you can use numbers instead of strings, you win, because it only takes a third or fourth of the time.
var quantity = 400000;
var uniqueIdentifiers = new HashSet<int>();
while (uniqueIdentifiers.Count < quantity)
{
int i=0;
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
i = i*10 + random.Next(1,10);
uniqueIdentifiers.Add(i);
}
It takes about 270 ms on my machine for 400,000 numbers and about 700 for 1,000,000. And this even without any parallelism.
Because of the use of a HashSet<T> instead of a List<T>, this algorithm runs in O(n), i.e. the duration will grow linear. 10,000,000 values therefore take about 7 seconds.
This suggestion may or may not be popular.... it depends on people's perspective. Because you haven't been too specific about what you need them for, how often, or the exact number, I will suggest a brute force approach.
I would generate a hundred thousand numbers - shouldn't take very long at all, maybe a few seconds? Then use Parallel LINQ to do a Distinct() on them to eliminate duplicates. Then use another PLINQ query to run a regex against the remainder to eliminate any with zeroes in them. Then take the top x thousand. (PLINQ is brilliant for ripping through large tasks like this). If needed, rinse and repeat until you have enough for your needs.
On a decent machine it will just about take you longer to write this simple function than it will take to run it. I would also query why you have 400K entries to test when you state you actually need "tens of thousands"?
The trick here is that you only need ten thousand unique numbers. Theoretically you could have almost 9,0E+08 possibilities, but why care if you need so many less?
Once you realize that you can cut down on the combinations that much then creating enough unique numbers is easy:
long[] numbers = { 1, 3, 5, 7 }; //note that we just take a few numbers, enough to create the number of combinations we might need
var list = (from i0 in numbers
from i1 in numbers
from i2 in numbers
from i3 in numbers
from i4 in numbers
from i5 in numbers
from i6 in numbers
from i7 in numbers
from i8 in numbers
from i9 in numbers
select i0 + i1 * 10 + i2 * 100 + i3 * 1000 + i4 * 10000 + i5 * 100000 + i6 * 1000000 + i7 * 10000000 + i8 * 100000000 + i9 * 1000000000).ToList();
This snippet creates a list of more than a 1,000,000 valid unique numbers pretty much instantly.
Try avoiding checks making sure that you always pick up a unique number:
static char[] base9 = "123456789".ToCharArray();
static string ConvertToBase9(int value) {
int num = 9;
char[] result = new char[9];
for (int i = 8; i >= 0; --i) {
result[i] = base9[value % num];
value = value / num;
}
return new string(result);
}
public static void generateIdentifiers(int quantity) {
var uniqueIdentifiers = new List<string>(quantity);
// we have 387420489 (9^9) possible numbers of 9 digits in base 9.
// if we choose a number that is prime to that we can easily get always
// unique numbers
Random random = new Random();
int inc = 386000000;
int seed = random.Next(0, 387420489);
while (uniqueIdentifiers.Count < quantity) {
uniqueIdentifiers.Add(ConvertToBase9(seed));
seed += inc;
seed %= 387420489;
}
}
I'll try to explain the idea behind with small numbers...
Suppose you have at most 7 possible combinations. We choose a number that is prime to 7, e.g. 3, and a random starting number, e.g. 4.
At each round, we add 3 to our current number, and then we take the result modulo 7, so we get this sequence:
4 -> 4 + 3 % 7 = 0
0 -> 0 + 3 % 7 = 3
3 -> 3 + 3 % 7 = 6
6 -> 6 + 6 % 7 = 5
In this way, we generate all the values from 0 to 6 in a non-consecutive way. In my example, we are doing the same, but we have 9^9 possible combinations, and as a number prime to that I choose 386000000 (you just have to avoid multiples of 3).
Then, I pick up the number in the sequence and I convert it to base 9.
I hope this is clear :)
I tested it on my machine, and generating 400k unique values took ~ 1 second.
Meybe this will bee faster:
//we can generate first number wich in 9 base system will be between 88888888 - 888888888
//we can't start from zero becouse it will couse the great amount of 1 digit at begining
int randNumber = random.Next((int)Math.Pow(9, 8) - 1, (int)Math.Pow(9, 9));
//no we change our number to 9 base, but we add 1 to each digit in our number
StringBuilder builder = new StringBuilder();
for (int i=(int)Math.Pow(9,8); i>0;i= i/9)
{
builder.Append(randNumber / i +1);
randNumber = randNumber % i;
}
id = builder.ToString();
Looking at the solutions already posted, mine seems fairly basic. But, it works, and generates 1million values in approximate 1s (10 million in 11s).
public static void generateIdentifiers(int quantity)
{
HashSet<int> uniqueIdentifiers = new HashSet<int>();
while (uniqueIdentifiers.Count < quantity)
{
int value = random.Next(111111111, 999999999);
if (!value.ToString().Contains('0') && !uniqueIdentifiers.Contains(value))
uniqueIdentifiers.Add(value);
}
}
use string array or stringbuilder, wjile working with string additions.
more over, your code is not efficient because after generating many id's your list may hold new generated id, so that the while loop will run more than you need.
use for loops and generate your id's from this loop without randomizing. if random id's are required, use again for loops and generate more than you need and give an generation interval, and selected from this list randomly how much you need.
use the code below to have a static list and fill it at starting your program. i will add later a second code to generate random id list. [i'm a little busy]
public static Random RANDOM = new Random();
public static List<int> randomNumbers = new List<int>();
public static List<string> randomStrings = new List<string>();
private void fillRandomNumbers()
{
int i = 100;
while (i < 1000)
{
if (i.ToString().Contains('0') == false)
{
randomNumbers.Add(i);
}
}
}
I think first thing would be to use StringBuilder, instead of concatenation - you'll be pleasantly surprised.
Antoher thing - use a more efficient data structure, for example HashSet<> or HashTable.
If you could drop the quite odd requirement not to have zero's - then you could of course use just one random operation, and then format your resulting number the way you want.
I think #slugster is broadly right - although you could run two parallel processes, one to generate numbers, the other to verify them and add them to the list of accepted numbers when verified. Once you have enough, signal the original process to stop.
Combine this with other suggestions - using more efficient and appropriate data structures - and you should have something that works acceptably.
However the question of why you need such numbers is also significant - this requirement seems like one that should be analysed.
Something like this?
public List<string> generateIdentifiers2(int quantity)
{
var uniqueIdentifiers = new List<string>(quantity);
while (uniqueIdentifiers.Count < quantity)
{
var sb = new StringBuilder();
sb.Append(random.Next(11, 100));
sb.Append(" ");
sb.Append(random.Next(11, 100));
sb.Append(" ");
sb.Append(random.Next(11, 100));
var id = sb.ToString();
id = new string(id.ToList().ConvertAll(x => x == '0' ? char.Parse(random.Next(1, 10).ToString()) : x).ToArray());
if (!uniqueIdentifiers.Contains(id))
{
uniqueIdentifiers.Add(id);
}
}
return uniqueIdentifiers;
}