How to calculate interval between a group of dates? [closed] - c#

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Every dates in the array represent a User interaction.
If there is a gap of > of 20 seconds between these dates I count a new User (lets call this group of dates a session).
Following my script I can count Users correctly.
But I cannot calculate the average time for session.
Could you point me out what am I doing wrong, of any suggestion for a better code is very welcome. Thanks.
namespace test_code
{
class Program
{
static void Main(string[] args)
{
Console.Clear();
DateTime[] dates = {
new DateTime(2014,01,01,0,0,0),
new DateTime(2014,01,01,0,0,5),
new DateTime(2014,01,01,0,0,10), // 15 s USR 1 session
new DateTime(2014,01,01,0,5,0),
new DateTime(2014,01,01,0,5,5), // 05 s USR 2
new DateTime(2014,01,01,0,10,0),
new DateTime(2014,01,01,0,10,1),
new DateTime(2014,01,01,0,10,2), // 03 s USR 3
new DateTime(2014,01,01,1,0,0),
new DateTime(2014,01,01,2,0,0),
new DateTime(2014,01,01,2,0,20) // 20 s USR 4
};
int users = dates.Length > 0 ? 1 : 0;
int gapS = 20; // Gap between date in seconds
double totalTimeGaps = 0;
double totalTime = 0;
for (int i = 0, n = i + 1; i < dates.Length - 1; i++, n++)
{
totalTime += (dates[n] - dates[i]).TotalSeconds;
if ((dates[n] - dates[i]).TotalSeconds > gapS)
{
users++;
totalTimeGaps += (dates[n] - dates[i]).TotalSeconds; // Does not count properly
Console.WriteLine(totalTimeGaps);
}
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Total Users: " + users);
Console.WriteLine("Total Time Avg. Session : " + (totalTime - totalTimeGaps)/users);
Console.ReadKey();
Console.Clear();
}
}
}
EDIT: Last version of working script
using System;
public class Program
{
public static void Main(string[] args)
{
DateTime[] dates = {
new DateTime(2014,01,01,0,0,0),
new DateTime(2014,01,01,0,0,5),
new DateTime(2014,01,01,0,0,10), // 10 s USR 1
new DateTime(2014,01,01,0,5,0),
new DateTime(2014,01,01,0,5,5), // 05 s USR 2
new DateTime(2014,01,01,0,10,0),
new DateTime(2014,01,01,0,10,1),
new DateTime(2014,01,01,0,10,2), // 02 s USR 3
new DateTime(2014,01,01,1,0,0), // 00 s USR 4
new DateTime(2014,01,01,2,0,0),
new DateTime(2014,01,01,2,0,20) // 20 s USR 5
};
int users = dates.Length > 0 ? 1 : 0;
int gapS = 20; // Gap between date in seconds
double totalTimeGaps = 0;
double totalTime = 0;
for (int i = 0, n = i + 1; i < dates.Length - 1; i++, n++)
{
double range = (dates[n] - dates[i]).TotalSeconds;
totalTime += range;
if (range > gapS)
{
users++;
totalTimeGaps += range;
}
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Total Users: " + users);
Console.WriteLine("Total Time Avg. Session : " + (totalTime - totalTimeGaps)/users);
Console.WriteLine("Total Time App." + (totalTime - totalTimeGaps));
}
}

The results I get are exactly as I'd expect when I run your program:
Total Users: 5
Total Time Avg. Session : 7.4
There are five users whose duration is 10s, 5s, 2s, 0s, 20s.
The total time is thus 37 seconds over 5 users which averages to 7.4s/user.
Your problem seems to be not in the code but in your own understanding of the data. You seem to have failed to note that what you record as user 4 is in fact two users and you are miscounting some of the timings (seemingly when there are three times involved).
Your code is correct according to the specs you gave us, you are just validating your code incorrectly.

Here is another approach using one List<DateTime> for every session-group:
List<List<DateTime>> sessions = new List<List<DateTime>> { new List<DateTime>() };
foreach(DateTime dt in dates)
{
if(!sessions.Last().Any())
sessions.Last().Add(dt);
else
{
TimeSpan diff = dt - sessions.Last().Last();
if (diff.TotalSeconds > 20)
sessions.Add(new List<DateTime> { dt });
else
sessions.Last().Add(dt);
}
}
You can use List<double> for every average:
List<double> secondsPerSession = new List<double>();
foreach (var session in sessions)
{
if (session.Count == 1)
secondsPerSession.Add(0);
else
{
double average = session.Skip(1)
.Average(d => (d - session[0]).TotalSeconds);
secondsPerSession.Add(average);
}
}
Output:
for (int i = 0; i < sessions.Count; i++)
Console.WriteLine("{0} [{1}]",
string.Join(",", sessions[i]), secondsPerSession[i]);

The problem is with your condition in you cycle. You count only the total gaps between two users and not the total time what a user has spent in your system. It should look something like this:
for (int i = 0, n = i + 1; i < dates.Length - 1; i++, n++)
{
totalTime += (dates[n] - dates[i]).TotalSeconds;
if ((dates[n] - dates[i]).TotalSeconds > gapS)
{
users++;
Console.WriteLine(totalTimeGaps);
}
else
{
totalTimeGaps += (dates[n] - dates[i]).TotalSeconds; // Does not count properly
}
}
Also if you want to get the average at the end of the cycle you should divide the total amount of time with the total number of users counter.

Related

C# Group by each month

Here's a code.
decimal[] men;
for (b.Pradzia();b.Yra();b.Kitas()) // loops through editions
{
men = new decimal[13]; //
for (a.Pradzia();a.Yra();a.Kitas()) // loops through subscribers
{
if (b.ImtiDuomenisL().Kodas == a.ImtiDuomenisP().Kodas) // if edition code matches subscriber code proceed
{
int j = a.ImtiDuomenisP().LaikotarpioPradžia + a.ImtiDuomenisP().LaikotarpioIlgis; // gets the start of subscription +
// the lenght of it.
for (int i = a.ImtiDuomenisP().LaikotarpioPradžia; i <= j; i++)
{
Dictionary<Leidinys, decimal> suma = new Dictionary<Leidinys, decimal>();
if (j <= 12)
{
men[i] += a.ImtiDuomenisP().Kiekis * b.ImtiDuomenisL().Kaina;
}
else
{
men[j - 12] += a.ImtiDuomenisP().Kiekis * b.ImtiDuomenisL().Kaina;
}
suma.Add(b.ImtiDuomenisL(), men[i]); // adds the edition and the sum of it to the dictionary.
}
}
}
}
What I get from this method is the sum of each edition in each month. Months are in integers for reasons.
For each month I need to determine, which edition got most money. I do not know how.
Assuming your "j" variable is months (Your code is a bit confused) you just need something like this:
int moneyofmonth = 0;
int biggest = 0;
foreach(var month in j)
{
moneyofmonth = //moneyofthismonth using the var "month".
if(moneyofmonth > biggest)
{
biggest = moneyofmonth;
}
}
"biggest" will be the biggest money of all months. If you want so save the month of the biggest, it's just to make a new var inside if saving the "month" variable.

Interest On Balance Calculation Repayments

I am trying to design some code that will calculate a repayment plan for a loan, given a principle amount, term period (12 months) and and interest rate. I want the payments each month to be the same value (the last can differ slightly to account for remainders), and the interest is calculated from the remaining loan balance.
The basic rules are
Interest is dependant on the loan balance.
Loan balance is dependant on the total monthly repayment value.
Capital repayment is dependant on total monthly repayment value and interest.
The total monthly repayment amount can vary but must be the same each month (except the last one which can be different).
Here is the code I currently have, it does continous iterations until it finds the correct total monthly repayment value. This does work sort of but seems very inefficient and it takes the balance horribly into negative when it shouldnt . I'm wondering if anyone knows of a better way of doing this?
static void calcRepaymentPlan()
{
decimal loanAmount = 100000; //100,000
decimal ir = 0.10m;
//day counts
int[] days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//get month/days
int dayCount = days.Sum();
int months = days.Length;
//get interest values
decimal interestdaily = ir / dayCount;
//total each month
decimal ideal_total_repayment = (loanAmount / months); //random number but seems like the best place to start
//some arrays to store balances
decimal[] balances = null;
decimal[] interest = null;
decimal[] capitalrepayment = null;
decimal totalRepaymentsValue = 0;
while (totalRepaymentsValue != loanAmount)
{
//array for balance each month
balances = new decimal[months];
for (int i = 0; i < months; i++)
{
balances[i] = loanAmount - ((i+1) * ideal_total_repayment);
}
//array for interest each month
interest = new decimal[months];
for (int i = 0; i < months; i++)
{
interest[i] = (interestdaily * balances[i]) * days[i];
}
//array for capital each month
capitalrepayment = new decimal[months];
for (int i = 0; i < months; i++)
{
capitalrepayment[i] = ideal_total_repayment - interest[i];
}
//how much of the loan are we paying back?
totalRepaymentsValue = capitalrepayment.Sum();
//how much difference between the loan amount and the total we have got now?
decimal difference = loanAmount - totalRepaymentsValue;
//how much to increment
decimal incr = 1;
//are we within 100? if so make the increment finer
if (difference < 100)
incr = 0.1m;
//if the difference is less than 1, then just add/subtract it from the last repayment
if (difference < 1)
{
capitalrepayment[months - 1] += difference;
balances[months - 1] += difference;
//this should now be the
totalRepaymentsValue = capitalrepayment.Sum();
continue;
}
//change the ideal total repayment
ideal_total_repayment += totalRepaymentsValue < loanAmount ? incr : -incr;
}
Console.WriteLine("--------------------------------------------------------------------------------------");
Console.WriteLine(" Balance Capital Interest Total Repayment");
for (int i = 0; i < months; i++)
{
string balanceStr = balances[i].ToString("#,##0.00");
string capitalStr = capitalrepayment[i].ToString("#,##0.00");
string interestStr = interest[i].ToString("#,##0.00");
string totalStr = (capitalrepayment[i] + interest[i]).ToString("#,##0.00");
Console.WriteLine("{0}). {1} {2} {3} {4}", (i + 1),
balanceStr, capitalStr, interestStr, totalStr);
}
Console.WriteLine("Ideal Total Repayment Value : {0}", ideal_total_repayment);
Console.WriteLine("Total Repaying : {0}", totalRepaymentsValue);
Console.WriteLine("-------------------------------------------------------------------------------------");
}

Max average series in ordered list

I'm trying to get the greatest average values for different duration in a list.
Let's say I have the following data:
var randomList = new List<int>();
var random = new Random(1969);
for (var i = 0; i < 10; i++)
{
randomList.Add(random.Next(0, 500));
}
That produces the following list:
190
279
37
413
90
131
64
129
287
172
I'm trying to get the highest average values for the different sets 0-9.
Set 0 (one item in a row) = 413 (index 3)
Set 1 (two items in a row) = 252 (average index 3,4)
Set 9 (10 items in a row) = 179 (average of the entire list)
I've been beating my head on this a while. I'm trying to find an efficient way to write this so I have the least traversals as possible. In production, I'll have lists with 3500-6000 points.
How do I find the highest average values for the different sets 0-9?
This probably isn't the most efficient way to do it, but it works fine:
Basically, we use a stack to track the items we've traversed. Then to calculate the average for n last items, we peek at n items from the stack.
void Main()
{
var randomList = new List<int>();
var random = new Random(1969);
for (var i = 0; i < 10; i++)
{
randomList.Add(random.Next(0, 500));
}
// Use the values from the original post for validation
randomList = new List<int> { 190, 279, 37, 413, 90, 131, 64, 129, 287, 172 };
const int numSets = 9;
var avgDict = Enumerable.Range(1, numSets).ToDictionary(e => e, e => (double)0);
var s = new Stack<int>();
foreach (var item in randomList)
{
s.Push(item);
for (var i = 1; i <= numSets; i++)
{
if (s.Count >= i)
{
var avg = s.Take(i).Average();
if (avg > avgDict[i])
avgDict[i] = avg;
}
}
}
avgDict.Dump();
}
Yields the result:
1 413
2 251.5
3 243
4 229.75
5 201.8
6 190
7 183.714285714286
8 178.75
9 180
I'm unsure as to the implications of using a Stack for large lists, when we only need 9-10 items. Might be a good case for a custom limited size stack
In your comment, you mentioned Avg(items:0,1,2) vs Avg(items:1,2,3) vs Avg(items:2,3,4)
Not sure if this is what you want but I came up with this.
First, get random number, then get average of 3 numbers. Then, get the largest average value.
static void Main(string[] args)
{
var randomList = new List<int>();
var random = new Random(1969);
int TotalRandomNumber = 10; //Change this accordingly
for (var i = 0; i < TotalRandomNumber ; i++)
{
randomList.Add(random.Next(0, 500));
}
foreach (var item in randomList)
{
Console.WriteLine("Random Number: " + item);
}
var AveNum = new List<double>();
int range = 3; //Change this for different range
for (int i = 1; i < TotalRandomNumber - range; i++)
{
var three = randomList.GetRange(i, range);
double result = three.Average();
Console.WriteLine("Average Number: " + result);
AveNum.Add(result);
}
Console.WriteLine("Largest: " + AveNum.Max());
}

Console Application, App Freezes No Errors

Okay at the moment I my self am new to programming and learning it slowly. At the moment I am taking programming classes to help better understand programming. I have ran in to a problem that has stumped me.
Now while I can do the assignment in a different way and manner as compared to what I provided. My question is, why is this happening? I get no errors, what so ever, the only thing that happens is after the input the Console Fezzes. I want to know what I did wrong.
static void Main(string[] args)
{
double[] Population = new double[6];
string[] Years = { "2", "3", "4", "5", "6", "7" };
double GrowthPercentage = 0.0;
double MathPercentage = 0.0000;
double ActualGrowth = 0.0;
int WhileCounter = 0;
//Ask user for Population of Roarkville
Console.WriteLine("Enter the Population of RoarkVille: ");
//Read Population and store
Population[0] = Convert.ToDouble(Console.ReadLine());
//Ask user for Growth percentage
Console.WriteLine("Enter the Growth percentage ");
//Read Growth Percentage
GrowthPercentage = Convert.ToDouble(Console.ReadLine());
//Calculation of Growth Percentage: Growth Percentage/100 = Math Percentage
MathPercentage = GrowthPercentage / 100;
//ActualGrowth = Population * Math Percentage
//Population2 = ActualGrowth + Population
while (WhileCounter < 5)
{
ActualGrowth = Population[WhileCounter] + MathPercentage;
WhileCounter++;
Population[WhileCounter] = ActualGrowth + Population[WhileCounter--];
}
for (int i = 0; i < Population.Length; i++)
{
Console.WriteLine("Population of 201{0:d}", Years[i]);
Console.WriteLine(Population[i]);
}
//Display 2012 Population
//Display 2013 Population
//Display 2014 Population
//Display 2015 Population
//Display 2016 Population
//Display 2017 Population
Console.ReadLine();
}
so what happen is that when you input on the growth percentage using this code:
while (Counter < 5)
{
ActualGrowth = Population[Counter] + MathPercentage;
Counter++;
Population[Counter] = ActualGrowth + Population[Counter--];
}
for (int i = 0; i < Population.Length; i++)
{
Console.WriteLine("Population of 201{0:d}", Years[i]);
Console.WriteLine(Population[i]);
}
the numbers that will you input will be infinite on the growth percentage:
this one can help you also
while (Counter < 5)
{
ActualGrowth = Population[Counter] + MathPercentage;
Counter++;
Population[Counter] = ActualGrowth + Population[Counter-1];
}
for (int i = 0; i < Population.Length; i++)
{
Console.WriteLine("Population of 201{0:d}", Years[i]);
Console.WriteLine(Population[i]);
}
The ++ operator changes the actual value of the variable, so WhileCounter++ increases the variable by 1
The -- operator does the same, which is not what you want to do in the line
Population[WhileCounter] = ActualGrowth + Population[WhileCounter--];
Instead, use WhileCounter - 1 , like so
Population[WhileCounter] = ActualGrowth + Population[WhileCounter - 1];
WhileCounter++;
Population[WhileCounter] = ActualGrowth + Population[WhileCounter--];
The value of WhileCounter never changes as far as the loop is concerned. In the loop body you increment WhileCounter and proceed to immediately decrement it, so the condition WhileCounter < 5 is always true.
You may as well have written
int WhileCounter = 0;
while(WhileCounter < 5)
{
WhileCounter += 1; // WhileCounter == 1
WhileCounter -= 1; // WhileCounter == 0
}
// aint never gunna happen
You should read up on the following operators and understand what they actually do:
--
++

How to convert a number to a range of prices

I want to calculate the amount to charge my customers, when they buy licenses of my product.
I sell it in ranges of licenses:
1-10 : $50/user
11-20 : $40/user
21-30 : $30/user
31-50 : $20/user
So when someone purchases 136 licenses, I will charge him:
50 x 2 x $20 = $2000
30 x 1 x $30 = $900
6 x $50 = $300
I'm looking for an algorithm on how to process the given number and break it into number of occurrences in a range..
How can I do this in plain C# or LINQ?
------------ EDIT ----------------------------
I started a less confusing question (Algorithm for Fogbugz pricing scheme) and I got the answer I've been looking for.
Thank you all..
If presented with this price structure I would think that it is in the customer's best interest to minimize the cost by buying the package that best suits their need. The following algorithm uses dynamic programming to calculate the minimal possible price to exactly buy a certain number of licenses (you can save money by buying more than you need, although I haven't implemented that):
int getPrice(int n)
{
if (n >= 1 && n <= 10) return 50 * n;
if (n >= 11 && n <= 20) return 40 * n;
if (n >= 21 && n <= 30) return 30 * n;
if (n >= 31 && n <= 50) return 20 * n;
throw new Exception("Impossible");
}
int minimizePrice(int n)
{
int[] minimumPrice = new int[n + 1];
for (int i = 1; i <= n; ++i)
{
minimumPrice[i] = int.MaxValue;
for (int j = Math.Max(0, i - 50); j < i; ++j)
{
minimumPrice[i] = Math.Min(minimumPrice[i],
minimumPrice[j] + getPrice(i - j));
}
}
return minimumPrice[n];
}
For 70 licenses the minimal price is $1400 which can be obtained by buying 2 blocks of 35 licenses. You are suggesting a greedy algorithm. This will confuse your customers. A clever customer will place two orders instead of one large order and save $400.
I'd suggest changing your prices so that there is no upper limit to the number of licenses you can buy at $20 each.
This looks like it would be very similar to algorithms that make change for purchases (which coins to choose). The only difference is that you're comparing against a range instead of a single number.
The code could look something like this:
var val = 136;
var price = 0;
while (val > 0)
{
var range = FindMatchingRange(val); // Use a dictionary, list, or array.
var number = Math.Min(val, range.Max);
price += range.CostPerUser * number;
val -= number;
}
If I were a person who needed 10 licenses, under your suggested pricing plan why would I ever buy just 10 licenses?
10 licenses * $50/license = $500
11 licenses * $40/license = $440
What you would want is plan that lowers the cost for the most recently purchased licenses. So that for person wanting 11 licenses they would pay:
(10 licenses * $50/license) + (1 license * $40/license) = $540
A possible plan would look like this:
first 10 licenses (1-10): $50/user
next 10 licenses (11-20): $40/user
next 10 licenses (21-30): $30/user
all licenses after that (31+) : $20/user
Writing code to compute final cost for any number of users is a simple exercise. The calculation for someone purchasing 136 licenses would look like this:
(10 licenses * $50/license) + (10 licenses * $40/license) + (10 licenses * $30/license) + (106 licenses * $20/license) = $500 + $400 + $300 + $2120 = $3,220.
The original pricing plan is wacky, in my opinion. Take the customer who purchased 130 licenses last year who comes back and wants 10 more. What justification is there for charging them the highest rate? They are a high volume customer, you want to sell them (and they justifiably expect to receive) additional licenses at the lowest "marginal" price.
I made a calculation class for you... just more customer-orientated.
It calculates the cheapest price possible with your defined priceranges.
Example: 136 Licenses
50 Licenses 20$ each ( because: 31-50 : $20/user )
50 Licenses 20$ each ( because: 31-50 : $20/user )
36 Licenses 20$ each ( because: 31-50 : $20/user )
TOTAL: 1720
Example 130 Licenses
50 Licenses 20$ each
50 Licenses 20$ each
30 Licenses 30$ each
TOTAL: 1900
Code for the class:
public class PriceCalculator
{
public List<OrderPackage> CalculateCheapestPrice(Int32 AmountOfLicenses,
List<PriceRange> PriceRanges, out Double Total)
{
List<OrderPackage> result = new List<OrderPackage>();
Total = 0;
Int32 AmountsOfLicensesleft = AmountOfLicenses;
PriceRanges.Sort(ComparePrice);
for (int i = 0; i < PriceRanges.Count; i++)
{
for (int j = PriceRanges[i].MaxAmount; j >= PriceRanges[i].MinAmount; j--)
{
if (j <= AmountsOfLicensesleft)
{
OrderPackage Order = new OrderPackage();
Int32 AmountOfThisPackage = AmountsOfLicensesleft / j;
//Int32 AmountForThisPrice = Convert.ToInt32(Math.Floor(tmp));
Order.PriceRange = PriceRanges[i];
Order.AmountOfLicenses = j;
Total += Order.AmountOfLicenses * Order.PriceRange.PricePerLicense;
for (int k = 0; k < AmountOfThisPackage; k++)
{
result.Add(Order);
}
AmountsOfLicensesleft = AmountsOfLicensesleft - (AmountOfThisPackage * j);
}
}
}
return result;
}
private static int ComparePrice(PriceRange x, PriceRange y)
{
if (x.PricePerLicense == y.PricePerLicense)
return 0;
if (x.PricePerLicense > y.PricePerLicense)
return 1;
if (x.PricePerLicense < y.PricePerLicense)
return -1;
return 0;
}
public class OrderPackage
{
public PriceRange PriceRange { get; set; }
public Int32 AmountOfLicenses { get; set; }
}
public class PriceRange
{
public int MinAmount { get; set; }
public int MaxAmount { get; set; }
public Double PricePerLicense { get; set; }
}
}
Usage example:
private void button1_Click(object sender, EventArgs e)
{
// Preparing PriceRangeDefinitions
List<PriceCalculator.PriceRange> PriceRangeDefinitions = new List<PriceCalculator.PriceRange>();
PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 1, MaxAmount = 10, PricePerLicense = 50 });
PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 11, MaxAmount = 20, PricePerLicense = 40 });
PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 21, MaxAmount = 30, PricePerLicense = 30 });
PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 31, MaxAmount = 50, PricePerLicense = 20 });
// Start the Calculation
PriceCalculator calculator = new PriceCalculator();
Double Total;
List<PriceCalculator.OrderPackage> Packages =
calculator.CalculateCheapestPrice(130, PriceRangeDefinitions, out Total);
// Show Proof of Concept
String ProofOfConcept = String.Empty;
for (int i = 0; i < Packages.Count; i++)
{
ProofOfConcept += Packages[i].AmountOfLicenses.ToString() + " Licenses " +
Packages[i].PriceRange.PricePerLicense.ToString() + "$ each" + Environment.NewLine;
}
ProofOfConcept += Environment.NewLine + "TOTAL: " + Total.ToString();
MessageBox.Show(ProofOfConcept);
}
A KeyValuePair collection or dictionary maybe?

Categories