I have wanted to try some challenges on Codility and started from beginning. All assignments were relatively easy up to the one called MaxCounters. I do not believe that this one is especially hard although it is the first one marked as not painless.
I have read the task and started coding in C# language:
public static int[] maxPart(int N, int[] A){
int[] counters = new int[N];
for(int i = 0; i < A.Length; i++){
for(int j = 0; j < counters.Length; j++){
if(A[i] == counters[j] && (counters[j] >= 1 && counters[j] <= N )){
counters [j] = counters [j] + 1;
}
if(A[i] == N + 1 ){
int tmpMax = counters.Max ();
for(int h = 0; h < counters.Length; h++){
counters [h] = tmpMax;
}
}
}
}
return counters;
}
Having 3 loops of course makes it really slow, but lets leave it for later. My concern is how I understood this like this and all the other people see it like on this question here.
From the assignment's description.
it has 2 actions:
increase(X) − counter X is increased by 1,
max counter − all counters are set to the maximum value of any
counter.
which occur under conditions:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max counter.
Both conditions are stated in the code above. Obviusly it is wrong but I am confused, and I do not know how could I understand it differently.
Why is this code wrong, what am I missing from task description?
One of the top rated answers looks like this:
public int[] solution(int N, int[] A) {
int[] result = new int[N];
int maximum = 0;
int resetLimit = 0;
for (int K = 0; K < A.Length; K++)
{
if (A[K] < 1 || A[K] > N + 1)
throw new InvalidOperationException();
if (A[K] >= 1 && A[K] <= N)
{
if (result[A[K] - 1] < resetLimit) {
result[A[K] - 1] = resetLimit + 1;
} else {
result[A[K] - 1]++;
}
if (result[A[K] - 1] > maximum)
{
maximum = result[A[K] - 1];
}
}
else
{
// inefficiency here
//for (int i = 0; i < result.Length; i++)
// result[i] = maximum;
resetLimit = maximum;
}
}
for (int i = 0; i < result.Length; i++)
result[i] = Math.max(resetLimit, result[i]);
return result;
}
This code results with 100% on Codility.
Question:
I would like to know how the author knew from the task to use result[A[K] - 1]? What would resetLimit represent?
Maybe I completely misunderstood the question due to my English I am not sure. I just can not go over it.
EDIT:
Based on my code provided, how did I misunderstood the assignment? Generally I am asking for explanation of the problem. Whether to explain what needs to be done, or take the code as correct result and provide and explanation why is this done this way?
In my opinion you somehow mixed the index of the counter (values in A) and the value of the counter (values in counter). So there is no magic in using A[i]-1 - it is the value X from the problem description (adjusted to 0-based index).
My naive approach would be, the way I understood the problem (I hope it makes clear, what your code is doing wrong):
public static int[] maxPart(int N, int[] A){
int[] counters = new int[N];
for(int i = 0; i < A.Length; i++){
int X=A[i];
if(X>=1 && X<=N){ // this encodes increment(X), with X=A[i]
counters [X-1] = counters [X-1] + 1; //-1, because our index is 0-based
}
if(X == N + 1 ){// this encodes setting all counters to the max value
int tmpMax = counters.Max ();
for(int h = 0; h < counters.Length; h++){
counters [h] = tmpMax;
}
}
}
}
return counters;
}
Clearly, this would be too slow as the complexity isO(n^2) with n=10^5 number of operations (length of the array A), in the case of the following operation sequence:
max counter, max counter, max counter, ....
The top rated solution solves the problem in a lazy manner and does not update all values explicitly every time a max counter operation is encountered, but just remembers which minimal value all counters must have after this operation in resetLimit. Thus, every time he must increment a counter, he looks up whether its value must be updated due to former max counter operations and makes up for all max counter operation he didn't execute on this counter
if(result[A[K] - 1] < resetLimit) {
result[A[K] - 1] = resetLimit + 1;
}
His solution runs in O(n) and is fast enough.
Here is my solution in JavaScript.
const maxCounters = (N, A) => {
for (let t = 0; t < A.length; t++) {
if (A[t] < 1 || A[t] > N + 1) {
throw new Error('Invalid input array A');
}
}
let lastMaxCounter = 0; // save the last max counter is applied to all counters
let counters = []; // counters result
// init values by 0
for (let i = 0; i < N; i++) {
counters[i] = 0;
}
let currentMaxCounter = 0; // save the current max counter each time any counter is increased
let maxApplied = false;
for (let j = 0; j < A.length; j++) {
const val = A[j];
if (1 <= val && val <= N) {
if (maxApplied && counters[val - 1] < lastMaxCounter) {
counters[val - 1] = lastMaxCounter;
}
counters[val - 1] = counters[val - 1] + 1;
if (currentMaxCounter < counters[val - 1]) {
currentMaxCounter = counters[val - 1];
}
} else if (val === N + 1) {
maxApplied = true;
lastMaxCounter = currentMaxCounter;
}
}
// apply the lastMaxCounter to all counters
for (let k = 0; k < counters.length; k++) {
counters[k] = counters[k] < lastMaxCounter ? lastMaxCounter : counters[k];
}
return counters;
};
Here is C# solution give me 100% score
public int[] solution(int N, int[] A) {
int[] operation = new int[N];
int max = 0, globalMax = 0;
foreach (var item in A)
{
if (item > N)
{
globalMax = max;
}
else
{
if (operation[item - 1] < globalMax)
{
operation[item - 1] = globalMax;
}
operation[item - 1]++;
if (max < operation[item - 1])
{
max = operation[item - 1];
}
}
}
for (int i = 0; i < operation.Length; i++)
{
if (operation[i] < globalMax)
{
operation[i] = globalMax;
}
}
return operation;
}
Here is a pretty elegant soulution in Swift:
public func solution(_ N : Int, _ A : inout [Int]) -> [Int] {
var globalMax = 0
var currentMax = 0
var maximums: [Int: Int] = [:]
for x in A {
if x > N {
globalMax = currentMax
continue
}
let newValue = max(maximums[x] ?? globalMax, globalMax) + 1
currentMax = max(newValue, currentMax)
maximums[x] = newValue
}
var result: [Int] = []
for i in 1...N {
result.append(max(maximums[i] ?? globalMax, globalMax))
}
return result
}
Try this Java snippet. Its more readable and neater, you don't need to worry about bounds check and might evacuate your first findings related to the more efficient approach you have found, btw the max is on the main forloop not causing any overhead.
public final int[] solution(int N, int[] A)
{
int condition = N + 1;
int currentMax = 0;
int lastUpdate = 0;
int[] counters = new int[N];
for (int i = 0; i < A.length; i++)
{
int currentValue = A[i];
if (currentValue == condition)
{
lastUpdate = currentMax;
}
else
{
int position = currentValue - 1;
if (counters[position] < lastUpdate)
{
counters[position] = lastUpdate + 1;
}
else
{
counters[position]++;
}
if (counters[position] > currentMax)
{
currentMax = counters[position];
}
}
}
for (int i = 0; i < N; i++)
{
if (counters[i] < lastUpdate)
{
counters[i] = lastUpdate;
}
}
return counters;
}
Inspired by Andy's solution, here is a solution in Python that is O(N + M) and gets a score of 100. The key is to avoid the temptation of updating all the counters every time A[K] > 5. Instead you keep track of a global max and reset an individual counter to global max just before you have to increment it. At the end, you set the remaining un-incremented counters to global max. See the comments in the code below:
def solution(N,A):
max = 0
global_max = 0
counters = [0] * N
for operation in A:
if operation > N:
#don't update counters.
#Just keep track of global max until you have to increment one of the counters.
global_max = max
else:
#now update the target counter with global max
if global_max > counters[operation - 1]:
counters[operation - 1] = global_max
#increment the target counter
counters[operation - 1] += 1
#update max after having incremented the counter
if counters[operation - 1] > max:
max = counters[operation - 1]
for i in range(N):
#if any counter is smaller than global max, it means that it was never
#incremented after the global_max was reset. Its value can now be updated
#to global max.
if counters[i] < global_max:
counters[i] = global_max
return counters
Here's a C# solution that gave me 100% score.
The idea is to simply not update the max counters on the spot but rather do it when you actually get to that counter, and then even out any counters that were not set to the max in another loop.
class Solution
{
public int[] solution(int N, int[] A)
{
var result = new int[N];
var latestMax = 0;
var currentMax = 0;
for (int i = 0; i < A.Length; i++)
{
var currentValue = A[i];
if (currentValue >= 1 && currentValue <= N)
{
if (result[currentValue - 1] < currentMax)
{
result[currentValue - 1] = currentMax;
}
result[currentValue - 1]++;
if (result[currentValue - 1] > latestMax)
{
latestMax = result[currentValue - 1];
}
}
else if (currentValue == N + 1)
{
currentMax = latestMax;
}
}
for (int i = 0; i < result.Length; i++)
{
if (result[i] < currentMax)
{
result[i] = currentMax;
}
}
return result;
}
}
Related
I'm writing a program to find the largest palindromic number made from product of 3-digit numbers. Firstly,i Create a method which has ability to check if it is a palindromic number. Here is my code :
static int check(string input_number)
{
for (int i = 0; i < input_number.Length/2; i++)
if (input_number[i] != input_number[input_number.Length - i])
return 0;
return 1;
}
After that, it's my main code:
static void Main(string[] args)
{
int k = 0;
for (int i = 0; i < 999; i++)
for (int j = 0; j < 999; j++)
{
k = i * j;
if (check(k.ToString()) == 1)
Console.Write(k + " ");
}
}
But when it has a problem that the length of input_number is zero. So my code doesn't run right way. What can I do to solve the length of input_number?
You have a few bugs in your code:
1. 3-digit-numbers range from `100` to `999`, not from `0` to `998` as your loops currently do.
So your Main method should look like this:
static void Main(string[] args)
{
int k = 0;
for (int i = 100; i <= 999; i++)
for (int j = 100; j <= 999; j++)
{
k = i * j;
if (check(k.ToString()) == 1)
Console.Write(k + " ");
}
}
Now all pairs of three digit numbers are checked. But to improve performance you can let j start at i, because you already checked e.g. 213 * 416 and don't need to check 416 * 213 anymore:
for (int i = 100; i <= 999; i++)
for (int j = i; j <= 999; j++) // start at i
And since you want to find the largest, you may want to start at the other end:
for (int i = 999; i >= 100; i--)
for (int j = 999; j >= 100; j--)
But that still does not guarantee that the first result will be the largest. You need to collect the result and sort them. Here is my LINQ suggestion for your Main:
var results = from i in Enumerable.Range(100, 900)
from j in Enumerable.Range(i, 1000-i)
let k = i * j
where (check(k.ToString() == 1)
orderby k descending
select new {i, j, k};
var highestResult = results.FirstOrDefault();
if (highestResult == null)
Console.WriteLine("There are no palindromes!");
else
Console.WriteLine($"The highest palindrome is {highestResult.i} * {highestResult.j} = {highestResult.k}");
2. Your palindrome-check is broken
You compare the character at index i to input_number[input_number.Length - i], which will throw an IndexOutOfRangeException for i = 0. Strings are zero-based index, so index of the last character is Length-1. So change the line to
if (input_number[i] != input_number[input_number.Length - i - 1])
Finally, I suggest to make the check method of return type bool instead of int:
static bool check(string input_number)
{
for (int i = 0; i < input_number.Length/2; i++)
if (input_number[i] != input_number[input_number.Length - i - 1])
return false;
return true;
}
This seems more natural to me.
You can use method below. Because you are trying to find the largest number you start from 999 and head backwards, do multiplication and check if its palindrome.
private void FindProduct()
{
var numbers = new List<int>();
for (int i = 999; i > 99; i--)
{
for (int j = 999; j > 99; j--)
{
var product = i * j;
var productString = product.ToString();
var reversed = product.Reverse();
if (product == reversed)
{
numbers.Add(product);
}
}
}
Console.WriteLine(numbers.Max());
}
1/ DateTime before = DateTime.Now;
2/ shellSort(List1);
3/ DateTime after = DateTime.Now;
4/ Console.WriteLine(after - before);
5/
6/ before = DateTime.Now;
7/ insertionSort(List2);
8/ after = DateTime.Now;
9/ Console.WriteLine(after - before);
I am trying to compare the run time of two different sorting algorithms. List1 here is equal to List2. I was expecting shell sort to be faster than insertion sort but although first WriteLine differs, it usually prints something like this = 00:00:00.0035037. The second one however, either prints 00:00:00 or something smaller than the first print. I thought maybe the insertion sort was better suited for List's current state however even when i swap the line 7 and line 2 i still get the same result. What is causing this? Why is the second executed function runs faster? Or am i using the Dates completely wrong?
Edit : I used Stopwatch instead of DateTime Class as advised in another post. The result is pretty much the same. The second one usually runs faster but every now and then it's slower than the first one. I also used a pre-written shellsort code to see if my implementation was bad but that was also a dead end.
As requested, shellsort and insertionsort implementations
static void shellSort(List<int> numbers) // Implementation i found online
{
int i, j, increment, temp;
increment = 3;
while (increment > 0)
{
for (i = 0; i < numbers.Count ; i++)
{
j = i;
temp = numbers[i];
while ((j >= increment) && (numbers[j - increment] > temp))
{
numbers[j] = numbers[j - increment];
j = j - increment;
}
numbers[j] = temp;
}
if (increment / 2 != 0)
increment = increment / 2;
else if (increment == 1)
increment = 0;
else
increment = 1;
}
}
public static void insertionSort(List<int> numbers)
{
int i = 0;
while (i != numbers.Count)
{
int k = i;
while (k != 0 && numbers[k] < numbers[k - 1])
{
int temp = numbers[k - 1];
numbers[k - 1] = numbers[k];
numbers[k] = temp;
k--;
}
i++;
}
}
Also this was my implementation of shellsort
public static void shellSort(List<int> Liste)
{
int n = Liste.Count;
int gap = (Liste.Count - 1) / 2;
while (gap > 0)
{
int i = 0;
for(int k = gap; k < n; k++) {
int p = i;
int m = k;
while (p >= 0)
{
if (Liste[p] > Liste[m])
{
int temp = Liste[p];
Liste[p] = Liste[m];
Liste[m] = temp;
m = p;
}
else
break;
p = p - gap;
}
i++;
}
gap = gap / 2;
}
}
I want to ask how I can reorder the digits in an Int32 so they result in the biggest possible number.
Here is an example which visualizes what I am trying to do:
2927466 -> 9766422
12492771 -> 97742211
I want to perform the ordering of the digits without using the System.Linq namespace and without converting the integer into a string value.
This is what I got so far:
public static int ReorderInt32Digits(int v)
{
int n = Math.Abs(v);
int l = ((int)Math.Log10(n > 0 ? n : 1)) + 1;
int[] d = new int[l];
for (int i = 0; i < l; i++)
{
d[(l - i) - 1] = n % 10;
n /= 10;
}
if (v < 0)
d[0] *= -1;
Array.Sort(d);
Array.Reverse(d);
int h = 0;
for (int i = 0; i < d.Length; i++)
{
int index = d.Length - i - 1;
h += ((int)Math.Pow(10, index)) * d[i];
}
return h;
}
This algorithm works flawlessly but I think it is not very efficient.
I would like to know if there is a way to do the same thing more efficiently and how I could improve my algorithm.
You can use this code:
var digit = 2927466;
String.Join("", digit.ToString().ToCharArray().OrderBy(x => x));
Or
var res = String.Join("", digit.ToString().ToCharArray().OrderByDescending(x => x) );
Not that my answer may or may not be more "efficient", but when I read your code you calculated how many digits there are in your number so you can determine how large to make your array, and then you calculated how to turn your array back into a sorted integer.
It would seem to me that you would want to write your own code that did the sorting part without using built in functionality, which is what my sample does. Plus, I've added the ability to sort in ascending or descending order, which is easy to add in your code too.
UPDATED
The original algorithm sorted the digits, now it sorts the digits so that the end result is the largest or smallest depending on the second parameter passed in. However, when dealing with a negative number the second parameter is treated as opposite.
using System;
public class Program
{
public static void Main()
{
int number1 = 2927466;
int number2 = 12492771;
int number3 = -39284925;
Console.WriteLine(OrderDigits(number1, false));
Console.WriteLine(OrderDigits(number2, true));
Console.WriteLine(OrderDigits(number3, false));
}
private static int OrderDigits(int number, bool asc)
{
// Extract each digit into an array
int[] digits = new int[(int)Math.Floor(Math.Log10(Math.Abs(number)) + 1)];
for (int i = 0; i < digits.Length; i++)
{
digits[i] = number % 10;
number /= 10;
}
// Order the digits
for (int i = 0; i < digits.Length; i++)
{
for (int j = i + 1; j < digits.Length; j++)
{
if ((!asc && digits[j] > digits[i]) ||
(asc && digits[j] < digits[i]))
{
int temp = digits[i];
digits[i] = digits[j];
digits[j] = temp;
}
}
}
// Turn the array of digits back into an integer
int result = 0;
for (int i = digits.Length - 1; i >= 0; i--)
{
result += digits[i] * (int)Math.Pow(10, digits.Length - 1 - i);
}
return result;
}
}
Results:
9766422
11224779
-22345899
See working example here... https://dotnetfiddle.net/RWA4XV
public static int ReorderInt32Digits(int v)
{
var nums = Math.Abs(v).ToString().ToCharArray();
Array.Sort(nums);
bool neg = (v < 0);
if(!neg)
{
Array.Reverse(nums);
}
return int.Parse(new string(nums)) * (neg ? -1 : 1);
}
This code fragment below extracts the digits from variable v. You can modify it to store the digits in an array and sort/reverse.
int v = 2345;
while (v > 0) {
int digit = v % 10;
v = v / 10;
Console.WriteLine(digit);
}
You can use similar logic to reconstruct the number from (sorted) digits: Multiply by 10 and add next digit.
I'm posting this second answer because I think I got the most efficient algorithm of all (thanks for the help Atul) :)
void Main()
{
Console.WriteLine (ReorderInt32Digits2(2927466));
Console.WriteLine (ReorderInt32Digits2(12492771));
Console.WriteLine (ReorderInt32Digits2(-1024));
}
public static int ReorderInt32Digits2(int v)
{
bool neg = (v < 0);
int mult = neg ? -1 : 1;
int result = 0;
var counts = GetDigitCounts(v);
for (int i = 0; i < 10; i++)
{
int idx = neg ? 9 - i : i;
for (int j = 0; j < counts[idx]; j++)
{
result += idx * mult;
mult *= 10;
}
}
return result;
}
// From Atul Sikaria's answer
public static int[] GetDigitCounts(int n)
{
int v = Math.Abs(n);
var result = new int[10];
while (v > 0) {
int digit = v % 10;
v = v / 10;
result[digit]++;
}
return result;
}
I need to optimise code that counts pos/neg values and remove non-qualified values by time.
I have queue of values with time-stamp attached.
I need to discard values which are 1ms old and count negative and positive values. here is pseudo code
list<val> l;
v = q.dequeue();
deleteold(l, v.time);
l.add(v);
negcount = l.count(i => i.value < 0);
poscount = l.count(i => i.value >= 0);
if(negcount == 10) return -1;
if(poscount == 10) return 1;
I need this code in c# working with max speed. No need to stick to the List. In fact arrays separated for neg and pos values are welcome.
edit: probably unsafe arrays will be the best. any hints?
EDIT: thanks for the heads up.. i quickly tested array version vs list (which i already have) and the list is faster: 35 vs 16 ms for 1 mil iterations...
Here is the code for fairness sake:
class Program
{
static int LEN = 10;
static int LEN1 = 9;
static void Main(string[] args)
{
Var[] data = GenerateData();
Stopwatch sw = new Stopwatch();
for (int i = 0; i < 30; i++)
{
sw.Reset();
ArraysMethod(data, sw);
Console.Write("Array: {0:0.0000}ms ", sw.ElapsedTicks / 10000.0);
sw.Reset();
ListMethod(data, sw);
Console.WriteLine("List: {0:0.0000}ms", sw.ElapsedTicks / 10000.0);
}
Console.ReadLine();
}
private static void ArraysMethod(Var[] data, Stopwatch sw)
{
int signal = 0;
int ni = 0, pi = 0;
Var[] n = new Var[LEN];
Var[] p = new Var[LEN];
for (int i = 0; i < LEN; i++)
{
n[i] = new Var();
p[i] = new Var();
}
sw.Start();
for (int i = 0; i < DATALEN; i++)
{
Var v = data[i];
if (v.val < 0)
{
int x = 0;
ni = 0;
// time is not sequential
for (int j = 0; j < LEN; j++)
{
long diff = v.time - n[j].time;
if (diff < 0)
diff = 0;
// too old
if (diff > 10000)
x = j;
else
ni++;
}
n[x] = v;
if (ni >= LEN1)
signal = -1;
}
else
{
int x = 0;
pi = 0;
// time is not sequential
for (int j = 0; j < LEN; j++)
{
long diff = v.time - p[j].time;
if (diff < 0)
diff = 0;
// too old
if (diff > 10000)
x = j;
else
pi++;
}
p[x] = v;
if (pi >= LEN1)
signal = 1;
}
}
sw.Stop();
}
private static void ListMethod(Var[] data, Stopwatch sw)
{
int signal = 0;
List<Var> d = new List<Var>();
sw.Start();
for (int i = 0; i < DATALEN; i++)
{
Var v = data[i];
d.Add(new Var() { time = v.time, val = v.val < 0 ? -1 : 1 });
// delete expired
for (int j = 0; j < d.Count; j++)
{
if (v.time - d[j].time < 10000)
d.RemoveAt(j--);
else
break;
}
int cnt = 0;
int k = d.Count;
for (int j = 0; j < k; j++)
{
cnt += d[j].val;
}
if ((cnt >= 0 ? cnt : -cnt) >= LEN)
signal = 9;
}
sw.Stop();
}
static int DATALEN = 1000000;
private static Var[] GenerateData()
{
Random r = new Random(DateTime.Now.Millisecond);
Var[] data = new Var[DATALEN];
Var prev = new Var() { val = 0, time = DateTime.Now.TimeOfDay.Ticks};
for (int i = 0; i < DATALEN; i++)
{
int x = r.Next(20);
data[i] = new Var() { val = x - 10, time = prev.time + x * 1000 };
}
return data;
}
class Var
{
public int val;
public long time;
}
}
To get negcount and poscount, you are traversing the entire list twice.
Instead, traverse it once (to compute negcount), and then poscount = l.Count - negcount.
Some ideas:
Only count until max(negcount,poscount) becomes 10, then quit (no need to count the rest). Only works if 10 is the maximum count.
Count negative and positive items in 1 go.
Calculate only negcount and infer poscount from count-negcount which is easier to do than counting them both.
Whether any of them are faster than what you have now, and which is fastest, depends among other things on what the data typically looks like. Is it long? Short?
Some more about 3:
You can use trickery to avoid branches here. You don't have to test whether the item is negative, you can add its negativity to a counter. Supposing the item is x and it is an int, x >> 31 is 0 for positive x and -1 for negative x. So counter -= x >> 31 will give negcount.
Edit: unsafe arrays can be faster, but shouldn't be in this case, because the loop would be of the form
for (int i = 0; i < array.Length; i++)
do something with array[i];
Which is optimized by the JIT compiler.
I have a scenario where I need to show a different page to a user for the same url based on a probability distribution,
so for e.g. for 3 pages the distribution might be
page 1 - 30% of all users
page 2 - 50% of all users
page 3 - 20% of all users
When deciding what page to load for a given user, what technique can I use to ensure that the overall distribution matches the above?
I am thinking I need a way to choose an object at "random" from a set X { x1, x2....xn } except that instead of all objects being equally likely the probability of an object being selected is defined beforehand.
Thanks for the input everyone, after doing some prototyping, this is what I ended up using
private static int RandomIndexWithPercentage(Random random, int[] percentages) {
if (random == null) {
throw new ArgumentNullException("random");
}
if (percentages == null || percentages.Length == 0) {
throw new ArgumentException("percentages cannot be null or empty", "percentages");
}
if(percentages.Sum() != 100) {
throw new ArgumentException("percentages should sum upto 100");
}
if (percentages.Any(n => n < 0)) {
throw new ArgumentException("percentages should be non-negative");
}
var randomNumber = random.Next(100);
var sum = 0;
for (int i = 0; i < percentages.Length; ++i) {
sum += percentages[i];
if (sum > randomNumber) {
return i;
}
}
//This should not be reached, because randomNumber < 100 and sum will hit 100 eventually
throw new Exception("Unexpected");
}
Generate a number 0-9. If the number is less than 3, give them page one. If it's less than 8, give them page two, or else give them page three.
Some code, to get you started:
private int ChoosePage()
{
int[] weights = new int[] { 3, 5, 2 };
int sum = 0;
int i;
for (i = 0; i < weights.Length; i++)
sum += weights[i];
int selection = (new Random()).Next(sum);
int count = 0;
for (i = 0; i < weights.Length - 1; i++)
{
count += weights[i];
if (selection < count)
return i;
}
return weights.Length - 1;
}
Note that weights doesn't have to add up to anything in particular. If sum = 100, then weight[i] is th percent chance of getting page i. If it doesn't, however, it's just relative - if weight[i] is twice weight[j], then page i will get twice as many hits as page j. This is nice because you can arbitrarily increase or decrease page traffic without recalculating anything. Alternatively, you could make sure the sum is always N, and hardcode N in, rather than summing all the values every time. There are a lot more optimizations you could do, I'm sure.
This is my code and works carefully.
public static int GetRandomIndexByPercent(params byte[] percentages)
{
int randomNumber = new Random().Next(1, percentages.Sum(a => a) + 1);
for (int sum = 0, i = 0; i < percentages.Length - 1; i++)
{
sum += percentages[i];
if (randomNumber <= sum) return i;
}
return percentages.Length - 1;
}
Also for test it, you can use this code:
for (int j = 0; j < 5; j++)
{
var items = new int[] { 40, 20, 10, 30 };
var items2 = new int[items.Length];
for (int i = 1; i <= 100000; i++)
items2[GetRandomIndexByPercent(items)]++;
for (int i = 0; i < items2.Length; i++)
Console.WriteLine(items[i] + " > " + (items2[i] / 1000.0));
Console.WriteLine("\n\n");
}
Console.ReadKey();