Given a maximum travel distance (forward and backwards roads), return the route which uses the maximum travel distance, in case of multiple same routes which uses the maximum travel distance return multiple routes.
Example 1
Forward route : [[1,3000],[2,5000],[3,4000],[4,10000],[5,8000]]
Backward route : [[1,1000],[2,3000],[3,4000]]
Max Distance Traveled: 11000
Result must be: [4,1] and [5,2], as the total traveled distance is 11000 which is less than or equal to max distance.
Example 2
Forward route : [[1,3000],[2,5000],[3,4000],[4,10000]]
Backward route : [[1,2000],[2,3000],[3,4000]]
Max Distance Traveled: 11000
Result must be: [2,3], as the total traveled distance is 9000 which is less than or equal to max distance.
I was able to solve this in O(forLength * backLength) like the below code:
static void Main(string[] args) {
int[][] f = new int[5][];
int[][] b = new int[3][];
f[0] = new int[] { 1, 3000 };
f[1] = new int[] { 2, 5000 };
f[2] = new int[] { 3, 4000 };
f[3] = new int[] { 4, 10000 };
f[4] = new int[] { 5, 8000 };
b[0] = new int[] { 1, 1000 };
b[1] = new int[] { 2, 3000 };
b[2] = new int[] { 3, 4000 };
var result = sol(f, b, 11000);
}
public static List<List<int>> sol(int[][] f, int[][] b,int max) {
List<List<int>> li = new List<List<int>>();
int m = 0;
for (int i = 0; i < f.Length; i++) {
for (int j = 0; j < b.Length; j++) {
if (f[i][1] + b[j][1] <= max) {
li.Add(new List<int>() { f[i][0], b[j][0], f[i][1] + b[j][1] });
if (m < f[i][1] + b[j][1]) {
m = f[i][1] + b[j][1];
}
}
}
}
return li.Where(i => i[2] == m).ToList();
}
Can anyone help me to make it more efficient in terms of time complexity, please?
Maybe I can give you a solution draft:
Let's name the "forward" list fwList, the "backward" list bwList. Each element contains a key and a value, in that order.
Sort both lists in the ascending order using merge sort or heap sort on the value part of the elements (O(N.ln(N)) time complexity).
For each element of bwList (we call it bwElem), find the index in fwList (we call it ID) where the sum is becoming too long (bwElem + fwList[ID] > 11000). Then [bwElem.key, fwList[ID - 1].key] is part of your solution.
The concatenation of the results for each element of bwList should make your list and, if all is clear in my mind, you should have a O(bwLength * (fwLength)^a) time complexity, where a < 1 (I'd even bet on O(bwLength * ln(fwLength))).
I think the algorithm can be optimized using this basis.
Related
I need split list in N sublists with balanced size.
For example, i have a list with a 101 elements, and max elements allowed in every sublist is 100.
The result should be two sublists, one with 50 elements and one with 51 elements.
Actually my code is not working correctly for all cases.
public static void Split()
{
List<int> players = new List<int>();
var totalPlayers = 133;
for (int i = 0; i < totalPlayers; i++)
{
players.Add(i);
}
var maxSizeSubList = 100;
var number = (double)players.Count / maxSizeSubList;
var numberGroupsRound = Math.Ceiling(number);
var playersXGroup = (int) Math.Round(players.Count / numberGroupsRound);
var subLists = SplitList<int>(players, playersXGroup);
}
private static List<List<T>> SplitList<T>(List<T> locations, int nSize = 30)
{
var list = new List<List<T>>();
for (int i = 0; i < locations.Count; i += nSize)
{
list.Add(locations.GetRange(i, Math.Min(nSize, locations.Count - i)));
}
return list;
}
In this case im getting 3 sublist with, 66, 66 and 1
Result Image
Its dot core 3.1, thanks.
Your are rounding off, instead of up:
var playersXGroup = (int)Math.Round(players.Count / numberGroupsRound);
should be
var playersXGroup = (int)Math.Ceiling(players.Count / numberGroupsRound);
I want to programm a level system for a small game.
The level system would be tied to the score and the levels would get further appart
only 2 score values are given
lvl, score
0, 50 (from 0 - 50)
1, 100 (from 51 to 100)
2, 150
3, 250
4, 400
5, 650
...
How could I elegantly calculate witch level I am in with a given score and 2 start values (50 and 100 in the example)
Or is it best to just calculate the score values in a list or array
With out any formula you can simply compute the whole table in a flash (under 0.0002 sec on a core2). Summing int is pretty fast. That's only 36 computation before hitting the max on int32.
var scoreTable = new []{50, 100, 150, 250, 400, 650, 1050, 1700, 2750, 4450, 7200, 11650, 18850,
30500, 49350, 79850, 129200, 209050, 338250, 547300, 885550, 1432850, 2318400, 3751250,
6069650, 9820900, 15890550, 25711450, 41602000, 67313450, 108915450, 176228900,
285144350, 461373250, 746517600, 1207890850, 1954408450};
For the math to create the table, let's be simple:
var thresholds = new List<int> {50, 100};
var index = 1;
while(true){
var temp = thresholds[cpt] + thresholds[cpt - 1];
if (temp < 0) break;
thresholds.Add(temp);
}
And to know the level:
var score = 51;
// Index is Zero-based numbering. Count is One-based. Index = Count -1;
var level = scoreTable.Where(x => x < score ).Count() - 1;
Binary.Search:
public class SupComparer : IComparer
{
public int Compare(object x, object y)
{
var t1 = UInt64.Parse(x.ToString());
var t2 = UInt64.Parse(y.ToString());
return t1.CompareTo(t2) > 0 ? 1 : 0;
}
}
var cp = new SupComparer();
var level = Array.BinarySearch(scoreTable, score, (IComparer)cp);
There's actually a formula to calculate Fibonacci numbers. That can be transformed into an algorithm to find the index of any given Fibonacci number. There's an example of how to do this in C# here.
You need to adapt that formula for use with your initial conditions of 50 and 100.
I asked a question over on Mathematics SE for help adjusting the original forumula and they suggested using
It's pretty easy to implement this as a C# method.
public int GetScoreIndex(int score)
{
const double phi = 1.61803398875;
const double rad5 = 2.2360679775;
var preLog = (score / 50) * rad5 + (1/2);
var log = Math.Log(preLog, phi);
var floor = (int) Math.Floor(log);
var index = floor - 1;
return index;
}
I have 5 fields, I want them all to have a generated number between 0 and 100. But, the sum of the 5 fields should be 100.
When I want to give a random number for one field I would do the following:
Random rnd = new Random();
int x= rnd.Next(1, 10);
But how should I do that for multiple fields that needs to have a sum of 100 together?
You can use the following approach:
generate 4 random integers in [0, 100]
sort them, let's denote the sorted values as 0 ≤ x1 ≤ x2 ≤ x3 ≤ x4 ≤ 100
use the following 5 values as the random numbers with sum 100:
N1 = x1
N2 = x2 - x1
N3 = x3 - x2
N4 = x4 - x3
N5 = 100 - x4
It basically corresponds to randomly choosing 4 sectioning points on the [0, 100] interval, and using the lengths of the 5 resulting intervals as the random numbers:
const int k = 5;
const int sum = 100;
Random rnd = new Random();
int[] x = new int[k + 1];
// the endpoints of the interval
x[0] = 0;
x[k] = sum;
// generate the k - 1 random sectioning points
for (int i = 1; i < k; i++) {
x[i] = rnd.Next(0, sum + 1);
}
// sort the sectioning points
Array.Sort(x);
// obtain the k numbers with sum s
int[] N = new int[k];
for (int i = 0; i < k; i++) {
N[i] = x[i + 1] - x[i];
}
In order to make your distribution uniform, you could try the following aproach:
Generate some random numbers.
Normalize them.
Correct the last field to get exactly the expected sum, if needed.
The code:
const int ExpectedSum = 100;
Random rnd = new Random();
int[] fields = new int[5];
// Generate 4 random values and get their sum
int sum = 0;
for (int i = 0; i < fields.Length - 1; i++)
{
fields[i] = rnd.Next(ExpectedSum);
sum += fields[i];
}
// Adjust the sum as if there were 5 random values
int actualSum = sum * fields.Length / (fields.Length - 1);
// Normalize 4 random values and get their sum
sum = 0;
for (int i = 0; i < fields.Length - 1; i++)
{
fields[i] = fields[i] * ExpectedSum / actualSum;
sum += fields[i];
}
// Set the last value
fields[fields.Length - 1] = ExpectedSum - sum;
Live example: https://dotnetfiddle.net/5yXwOP
To achieve a truly random distribution, with every element having the chance to be 100 with a total sum of 100, you can use the following solution:
public static int[] GetRandomDistribution(int sum, int amountOfNumbers)
{
int[] numbers = new int[amountOfNumbers];
var random = new Random();
for (int i = 0; i < sum; i++)
{
numbers[random.Next(0, amountOfNumbers)]++;
}
return numbers;
}
static void Main(string[] args)
{
var result = GetRandomDistribution(100, 5);
}
It increases a random number by one until the sum is reached. This should fulfill all your criterias.
After thinking about it, I prefer the following solution, because it's less likely to generate an equal distribution:
public static int[] GetRandomDistribution2(int sum, int amountOfNumbers)
{
int[] numbers = new int[amountOfNumbers];
var random = new Random();
for (int i = 0; i < amountOfNumbers; i++)
{
numbers[i] = random.Next(sum);
}
var compeleteSum = numbers.Sum();
// Scale the numbers down to 0 -> sum
for (int i = 0; i < amountOfNumbers; i++)
{
numbers[i] = (int)(((double)numbers[i] / compeleteSum) * sum);
}
// Due to rounding the number will most likely be below sum
var resultSum = numbers.Sum();
// Add +1 until we reach "sum"
for (int i = 0; i < sum - resultSum; i++)
{
numbers[random.Next(0, amountOfNumbers)]++;
}
return numbers;
}
For Example.
int sum=100;
int i = 5;
Random rnd = new Random();
while (true)
{
int cur;
--i;
if (i == 0) {
Console.WriteLine(sum + " ");
break;
} else
cur=rnd.Next(1, sum);
sum -= cur;
Console.WriteLine(cur + " ");
}
Live Example: https://dotnetfiddle.net/ltIK40
or
Random rnd = new Random();
int x= rnd.Next(1, 10);
int y= rnd.Next(x,x+10);
int y2=rnd.Next(y,y+10);
int y3=rnd.Next(y2,y2+10);
int y4=100-(x+y+y2+y3);
My approach is this:
var rnd = new Random();
var numbers = Enumerable.Range(0, 5).Select(x => rnd.Next(0, 101)).ToArray().OrderBy(x => x).ToArray();
numbers = numbers.Zip(numbers.Skip(1), (n0, n1) => n1 - n0).ToArray();
numbers = numbers.Concat(new[] { 100 - numbers.Sum() }).ToArray();
This is as uniform as I think is possible.
Create your first random number. After that you take the difference between the value of num1 and 100 as the max def of rnd. But to guarantee that their sum is 100, you have to check at the last num if the sum of all nums is 100. If not the value of your last num is the difference that their sum and 100.
And to simply your code and get a clean strcuture, put that code in a loop and instead of single numbers work with an int[5] array.
private int[] CreateRandomNumbersWithSum()
{
int[] nums = new int[5];
int difference = 100;
Random rnd = new Random();
for (int i = 0; i < nums.Length; i++)
{
nums[i] = rnd.Next(0, difference);
difference -= nums[i];
}
int sum = 0;
foreach (var num in nums)
sum += num;
if (sum != 100)
{
nums[4] = 100 - sum;
}
return nums;
}
I think this is a very simple solution:
public void GenerateRandNr(int total)
{
var rnd = new Random();
var nr1 = rnd.Next(0, total);
var nr2 = rnd.Next(0, total - nr1);
var nr3 = rnd.Next(0, total - nr1 - nr2);
var nr4 = rnd.Next(0, total - nr1 - nr2 - nr3);
var nr5 = total - nr1 - nr2 - nr3 - nr4;
}
EDIT:
Just tested it, works fine for me:
The solution is that it's not the numbers that need to be random so much as the distribution needs to be random. The randomness of the numbers will be a side effect of their random distribution.
So you would start with five random numbers in a given range. The exact range doesn't matter as long as the range is the same for all five, although a broader range allows for more variation. I'd use Random.NextDouble() which returns random numbers between 0 and 1.
Each of those individual numbers divided by the sum of those numbers represents a distribution.
For example, say your random numbers are .4, .7, .2, .5, .2. (Using fewer digits for simplicity.)
The total of those numbers is 2. So now the distributions are each of those numbers divided by the total.
.4 / 2 = .20
.7 / 2 = .35
.2 / 2 = .10
.5 / 2 = .25
.2 / 2 = .10
You'll notice that those distributions will equal 100% or really close to it if there are a lot more decimal places.
The output is going to be each of those distributions times the target number, in this case, 100. In other words, each of those numbers represents a piece of 100.
So multiplying each of those distributions times the target, we get 20, 35, 10, 25, and 100, which add up to 100.
The trouble is that because of rounding your numbers won't always perfectly add up to 100. To fix that you might add one to the smallest number if the sum is less than 100, or subtract one from the largest number of the the sum is greater than 100. Or you could choose to add or subtract on one of the numbers at random.
Here's a class to create the distributions. (I'm just playing around so I haven't exactly optimized this to death.)
public class RandomlyDistributesNumbersTotallingTarget
{
public IEnumerable<int> GetTheNumbers(int howManyNumbers, int targetTotal)
{
var random = new Random();
var distributions = new List<double>();
for (var addDistributions = 0; addDistributions < howManyNumbers; addDistributions++)
{
distributions.Add(random.NextDouble());
}
var sumOfDistributions = distributions.Sum();
var output = distributions.Select(
distribution =>
(int)Math.Round(distribution / sumOfDistributions * targetTotal, 0)).ToList();
RoundUpOutput(output, targetTotal);
return output;
}
private void RoundUpOutput(List<int> output, int targetTotal)
{
var difference = targetTotal - output.Sum();
if (difference !=0)
{
var indexToAdjust =
difference > 0 ? output.IndexOf(output.Min()) : output.IndexOf(output.Max());
output[indexToAdjust]+= difference;
}
}
}
And here's a not-perfectly-scientific unit test that tests it many times over and ensures that the results always total 100.
[TestMethod]
public void OutputTotalsTarget()
{
var subject = new RandomlyDistributesNumbersTotallingTarget();
for (var x = 0; x < 10000; x++)
{
var output = subject.GetTheNumbers(5, 100);
Assert.AreEqual(100, output.Sum());
}
}
Some sample outputs:
5, 30, 27, 7, 31
15, 7, 26, 27, 25
10, 11, 23, 2, 54
The numbers are always going to average to 20, so while 96, 1, 1, 1 is a hypothetical possibility they're going to tend to hover closer to 20.
Okay. Having been burned by my previous attempt at this seemingly trivial problem, I decided to have another go. Why not normalise all the numbers after generation? This guarantees randomness, and avoids O(n log n) performance from a sort. It also has the advantage that even with my basic maths, I can work out that the numbers are uniformly distributed.
public static int[] UniformNormalization(this Random r, int valueCount, int valueSum)
{
var ret = new int[valueCount];
long sum = 0;
for (int i = 0; i < valueCount; i++)
{
var next = r.Next(0, valueSum);
ret[i] = next;
sum += next;
}
var actualSum = 0;
for (int i = 0; i < valueCount; i++)
{
actualSum += ret[i] = (int)((ret[i] * valueSum) / sum);
}
//Fix integer rounding errors.
if (valueSum > actualSum)
{
for (int i = 0; i < valueSum - actualSum; i++)
{
ret[r.Next(0, valueCount)]++;
}
}
return ret;
}
This should also be one of the fastest solutions.
I want to filter a list of double values
I want number of items in this list that are greater or equal
than a random value
When I use random function inside the lambda expression the number of items
founds doesn't correspond to the amount expected. When I put random function outside lamda expression, the code is working right
here is not working version vs working version
not working code:
List<double> vecteur = new List<double> { 0.45, 0.5, 1 };
List<int> lstcompteurs = new List<int> { 0,0,0};
Random r = new Random();
for (int i = 0; i < 1000; i++) {
int index = vecteur.FindIndex(a => a > r.NextDouble());
lstcompteurs[index]++;
}
foreach (int cpt in lstcompteurs) {
Console.WriteLine(cpt);
}
Console.Read();
output :
448
288
264
we notice that 288 represents much more than 5% of the sample.
working code:
List<double> vecteur = new List<double> { 0.45, 0.5, 1 };
List<int> lstcompteurs = new List<int> { 0,0,0};
Random r = new Random();
for (int i = 0; i < 1000; i++) {
double b = r.NextDouble();
int index = vecteur.FindIndex(a => a > b);
lstcompteurs[index]++;
}
foreach (int cpt in lstcompteurs) {
Console.WriteLine(cpt);
}
Console.Read();
output:
443
48
509
As you see 48 represents indeed 5% of the sample.
I do not understand what is going on with the first version of my code
The problem is in this line:
int index = vecteur.FindIndex(a => a > r.NextDouble());
Finding the index will call r.NextDouble() again every time LINQ is doing a check (quite a lot times). a > r.NextDouble() is nothing else than a (anonymous) function LINQ is using to check if the index is a match. This means every comparison is done with a new random value. If you find the index like
double b = r.NextDouble();
int index = vecteur.FindIndex(a => a > b);
you are always using the same value to do the comparision.
So I have a list of ulong prime numbers, with variable lengths.
Ex: 2,5,7,3
And I want to create every multiplying combination, excluding ALL the numbers multiplied together.
(2*5*7*3 in this case).
Ex: 2,3,5,6,7,10,14,15,21,30,35,42,70,105.
I have tried a couple solutions using the "foreach" loop, but I just can't seem to get it. It seems too easy. What might be an efficient way to going about this?
My main issue that I run into is that when I add a new value to the list, the "foreach" loop causes an error because I changed it. I can't think of a way around that other than do a whole bunch of new lists. It seems to me like there should be a really simple, clean solution that I am just overcomplicating.
I tried the "Build-up" approaching, multiplying the base factors together, to create larger ones and so on, and the "Build-down" approaching, starting with the large number, and then factoring it (shown in my example). This is what I tried:
List<ulong> c, f;
ulong i;
//Some code then sets c to the factors (2, 3, 5, and 7)
//i is then set to the c set multiplied together (2*3*5*7)
//if the c is a factor, divide and add it to the new list f (the final list)
foreach (ulong u in c)
{
if (i % u == 0)
{
f.Add(i/u);
Console.WriteLine(i / u);
}
}
// Keep on dividing the numbers down, until you get the original factors added to the list
for (int j = 0; j < f.Count -1; j++)
{
foreach (ulong u in c)
{
foreach (ulong v in f)
{
if (v % u == 0)
{
if (v / u != 1 && !f.Contains(v / u))
{
f.Add(v / u);
}
}
}
}
}
Expected Output with input (2 5 7 3):
2
5
3
7
2 * 3 = 6
2 * 5 = 10
2 * 7 = 14
5 * 7 = 35
5 * 3 = 15
7 * 3 = 21
2 * 5 * 7 = 70
2 * 5 * 3 = 30
2 * 3 * 7 = 42
5 * 7 * 3 = 105
This works to get the numbers you want:
Func<IEnumerable<ulong>, IEnumerable<ulong>> f = null;
f = xs =>
{
if (xs.Any())
{
return f(xs.Skip(1))
.SelectMany(x =>
new [] { xs.First() * x, x });
}
else
{
return new ulong[] { 1 };
}
};
You use it like this:
var primes = new ulong[] { 2, 5, 7, 3 };
var results = f(primes)
.OrderBy(x => x)
.ToArray();
results = results
.Skip(1)
.Take(results.Length - 2)
.ToArray();
I had to do the Skip/Take to get rid of the two cases you wanted to avoid.
The result I get is:
If you'd like it as an (almost) one-liner here it is:
Func<IEnumerable<ulong>, IEnumerable<ulong>> f = null;
f = xs => xs.Any() ? f(xs.Skip(1)).SelectMany(x => new [] { xs.First() * x, x }) : new ulong[] { 1 };
The following code should give you exactly what you need with any number of items in your list
class Program
{
static void Main(string[] args)
{
MultiplyFactors(new List<ulong>() { 2, 3, 5, 7 });
Console.ReadLine();
}
public static void MultiplyFactors(List<ulong> numbers)
{
var factorCombinations = CreateSubsets(numbers.ToArray());
foreach (var factors in factorCombinations)
{
// set initial result to identity value. (any number multiplied by itself is 1)
ulong result = 1;
// multiply all factors in combination together
for (int i = 0; i < factors.Count(); i++)
{
result *= factors[i];
}
// Output for Display
Console.WriteLine(String.Format("{0}={1}", String.Join("x", factors), result));
}
}
private static List<T[]> CreateSubsets<T>(T[] originalArray)
{
// From http://stackoverflow.com/questions/3319586/getting-all-possible-permutations-from-a-list-of-numbers
var subsets = new List<T[]>();
for (int i = 0; i < originalArray.Length; i++)
{
int subsetCount = subsets.Count;
subsets.Add(new T[] {originalArray[i]});
for (int j = 0; j < subsetCount; j++)
{
T[] newSubset = new T[subsets[j].Length + 1];
subsets[j].CopyTo(newSubset, 0);
newSubset[newSubset.Length - 1] = originalArray[i];
subsets.Add(newSubset);
}
}
return subsets;
}
}
This will give you the following results for your inputs of 2,3,5,7.
2=2
3=3
2x3=6
5=5
2x5=10
3x5=15
2x3x5=30
7=7
2x7=14
3x7=21
2x3x7=42
5x7=35
2x5x7=70
3x5x7=105
2x3x5x7=210
This could have been through recursion but this method was probably just as simple. The trick is creating your list of subsets. Once you have that, you simply multiply all of the elements of each subset together.