Reputation: 139
Question - I have table X which have random rows (it could be 10 rows, 100 rows and so on). Now I have percentage of weight lets suppose 33% 40% and 27%
and let name it
A=33%
B=40%
C=27%
so i have add one more column which have percentage of random row
****Row** |--Weight
row1 | A
row2 | C
row3 | B
.
.
.
row100 |B
Let suppose table have 1000 row then weight should be assign random like
A= 330
B=400
c=270
What I Made- For below program I have to distribute segment on basis of value. For example, in below code I am iterating value to 1000, but it will distribute value like
A=300
B=400
C=300
instead of
A= 250, B=450 C=300. As weight are 25%,45%,30%
It should be generic for any n number, for example, in this code n =1000 (iteration):
static void Main(string[] args)
{
//var t = Console.ReadLine().ToObservable();
List<string> li = new List<string>();
//t.Subscribe(m => Console.Write(m));
for (int i = 1; i <= 1000; i++)
{
li.Add(GetSegment(i, "2.5,6.5,10.0", "A,B,C"));
}
Console.WriteLine("A Contains {0}",li.Count(x => x.Contains("A")));
Console.WriteLine("B Contains {0}", li.Count(x => x.Contains("B")));
Console.WriteLine("C Contains {0}", li.Count(x => x.Contains("C")));
Console.ReadLine();
}
public static string GetSegment(long seed, string raw_segments, string segname)
{
var segmentsValue = raw_segments.Split(',').Select(entry => (double.Parse(entry))).ToArray();
var segmentName = segname.Split(',').Select(entry => entry).ToArray();
double theNumber = seed % 10;
double index1 = segmentsValue.Where(entry => entry > theNumber).First();
int index = Array.IndexOf(segmentsValue, index1);
return segmentName[index].ToString();
}
Upvotes: 0
Views: 701
Reputation: 134105
So you have some number of objects and you want to assign them randomly to three bins, based on some set distribution. For example, you want 33% in bin A, 40% in bin B, and the remaining 27% in bin C.
If your distribution doesn't have to be exact (i.e. given 1,000 items, bin A must contain exactly 330 items), then this is very easy: for each row you generate a random number between 0 and 1,000, and assign assign the row to the appropriate bin. For example:
int[] ranges = new int[]{330, 730, 1000};
var rnd = new Random();
for (var i = 0; i < 1000; ++i)
{
var r = rnd.Next(1000);
if (r < ranges[0])
Console.WriteLine("bin A");
else if (r < ranges[1])
Console.WriteLine("bin B");
else
Console.WriteLine("bin C");
}
On average over many runs, that will give you 33% in bin A, 40% in bin B, and 27% in bin C. But for any individual run the number of items in each bin will vary somewhat. For example, in one run you might end up with 327, 405, 268.
With a little work, you could adapt that method so that it doesn't over-assign any bin. Basically, when a bin fills up, remove it from the list of ranges. You'd need your list of ranges to be dynamic so that you could remove items and keep working, but it would allow you to exactly fill each bin.
If the number of items is small enough, you could create an array with the numbers from 0 to N, shuffle it, and assign the numbers that way. For example:
// builds an array of numbers from 0 to 999.
var numbers = Enumerable.Range(0, 1000).ToArray();
Shuffle(numbers);
Use the Fisher-Yates shuffle to shuffle the array. See https://gist.github.com/mikedugan/8249637 (among many others) for an implementation.
You now have an array containing the numbers from 0 to 999 in random order. This is like pre-assigning a unique random number to each of your records. So when you go through your list of records you look up its corresponding random number in the numbers array. For example:
for (var i = 0; i < 1000; ++i)
{
var value = numbers[i];
char bin;
if (value < 330) bin = 'A';
else if (value < 730) bin = 'B';
else bin = 'C';
Console.WriteLine("Record {0} goes to bin {1}", i, bin);
}
Upvotes: 1