Predicting data based on averages - c#

I have GPS data entering a serial port on my PC every second. I have successfully processed the GPS data and the latitude and longitude are stored in separate arrays as floating point numbers.
double[] dlat = new double[100000]; //contains the latitude data
double[] dlon = new double[100000]; //contains the longitude data
Most of the time the latitude and longitude numbers remain the same as the GPS position only changes every 5 meters. When the either the latitude or longitude value in the arrays changes I want my program to predict based on averages the latitude or longitude for the the data points stored in between the changes. For example:
Let's say this is the contents of the latitude array:
2,2,2,2,2,17
I would want my program to change what is in the array to:
2,5,8,11,14,17
I've tried tackling the problem but my method doesn't work :-/ I am new to C#; there must be a better way of doing this. Here is the snippet of my code that attempts to do the prediction (the bit after ---GPS coordinate prediction--- is the bit that doesn't work):
string RxString;// where the raw serial data is stored
string mag;
double[] dmag = new double[100000];//magnetic data stored here
string lat;
double[] dlat = new double[100000];//latitude data stored here
string lon;
double[] dlon = new double[100000];//longitude data stored here
double average;//average step between change in latiude
int i; //pointer double array data;
int count;//counter for prediction code
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)//activates when port is open and data in buffer
{
RxString = serialPort1.ReadTo("\r\n");//read raw data from serial port into string
this.Invoke(new EventHandler(DisplayText));//invoke allows it to call function diplay text*/
if(RxString.StartsWith("G"))
{
lat = RxString.Split(',')[0].Substring(4);// extract latitude
this.Invoke(new EventHandler(DisplayText1));//invoke allows it to call function diplay text
dlat[i] = Convert.ToDouble(lat);//convert and store in double array
this.Invoke(new EventHandler(Form1_Load));//invoke allows it to call function
lon = RxString.Split(',')[2];// extract longitude
this.Invoke(new EventHandler(DisplayText2));//invoke allows it to call function diplay text
dlon[i] = Convert.ToDouble(lon);//covert and store in double array
this.Invoke(new EventHandler(Form1_Load));//invoke allows it to call function
mag = RxString.Split(',')[3].Substring(6).Trim();// extract magnetic data
this.Invoke(new EventHandler(DisplayText3));//invoke allows it to call function diplay text
dmag[i] = Convert.ToDouble(mag);//convert and store in double array
this.Invoke(new EventHandler(Form1_Load));//invoke allows it to call function
i++;
RxString = null;
/* -------------------------GPS coordinate prediction--------------------------------------------- */
if (i > 0)
{
if (dlat[i] == dlat[i - 1])
{
count++;
}
if (dlat[i] != dlat[i - 1])
{
double average = (dlat[i] - dlat[i - 1]) / (count);//average data step beween changed values
int firstAv = i - (count - 1);//position of first average
int lastAv = i - 1;//position of last average
for (int j = firstAv; j <= lastAv; i++)
{
dlat[j] = dlat[j - 1] + average;
}
count = 0;
}
}
if (i==0) count = 1;
}

The following works:
using System;
using System.Text;
namespace Practice
{
public class Hello
{
static double[] ldat = {2.0,2.0,2.00,2.0,2.0,17.0};
static double[] ldat2 = {2.0,3.0,4.00,4.0,7.0,19.0};
static double[] ldat3 = {0.0, 0.0, -5.0, -5.0, -11.0, -11.0, -20};
public static void Main(string[] args)
{
test(ldat);
test(ldat2);
test(ldat3);
}
public static void test(double[] array){
//Use Code from here.....
int firstEqualIndex = -1;
for(int i = 1; i < array.Length ; i ++)
{
if (i > 0)
{
if(array[i] == array[i - 1])
{
if(firstEqualIndex == -1)
{
firstEqualIndex = i - 1;
}
}
else //They are not equal
{
//Figure out the average.
if(firstEqualIndex >= 0)
{
double average = (array[i] - array[firstEqualIndex]) / (Double)((i - firstEqualIndex));
int k = 0;
for(int j = firstEqualIndex; j < i; j++)
{
array[j] += average * k;
k++;
}
firstEqualIndex = -1;
}
}
}
}
//..... to here.
StringBuilder builder = new StringBuilder();
foreach (double entry in array)
{
// Append each int to the StringBuilder overload.
builder.Append(entry).Append(", ");
}
string result = builder.ToString();
Console.WriteLine(result);
}
}
}
This test outputs
2, 5, 8, 11, 14, 17,
2, 3, 4, 5.5, 7, 19,
0, -2.5, -5, -8, -11, -15.5, -20,
Sorry about all the edits I am trying to make sure that the method works with additional test cases.
EDIT: Added a test for negative case.

I would formulate this problem in terms of signal processing. So if you have a signal f(t) (which could be your discretized latitude array for example), you want to create a new signal g(t) defined by
g(t) = E[f(z) | t-0.5*w <= z <= t+0.5*w]
where E is denoting the expected value (or average) and w is the width of your filter.
One of the benefits of modeling the problem this way is that you have a very concrete way of specifying your movement model. That is, how are you going to transform the data [0, 0, 0, 0, 1, 1, 1, 1]?
Should it be [0, 0, 0, 1/3, 2/3, 1, 1, 1]?
Or should it be [0, 1/7, 2/7, 3/7, 4/7, 5/7, 6/7, 1]?
Given that you know how much time passes between your samples, you can choose a w duration that specifies the model you want.
Another benefit is that, if you want a nonlinear movement model, you can easily extend to that too. In the example I gave above, I used a box filter to do the smoothing, but you could use something else to take into account the physical limitations on the acceleration/deceleration of whatever it is you're tracking. A filter shaped more like a Gaussian curve could accomplish that.

Related

Conversion of Hex string to decimal

Guys i need some help converting this c program to python since i am not a c guy whatsoever. I'm attempting to convert this hex string. I was told the following hex string was a series of position and load integer values. The developers exact words were:
0x00703D450080474500704A4500E04B4500E04B4500D04A4500E0484500B0464500C044450090434500404345009043450000444500F0434500F0424500F0404500403E4500703B45000039450020374500F03545006035450090354500A0364500E0384500403C450090404500C0444500A04745008047450030434500D0394500702B4500A0184500A002450080D5440000A54400006C440040144400008943000050C100808AC3004003C400803EC4008077C4002096C40040ADC400A0BFC40040CCC40020D3C40040D5C40020D4C40040D1C40000CEC400C0CAC40080C7C40020C4C40000C1C40020BEC40040BCC40060BBC40060BBC400C0BBC400E0BBC40060BBC40080BAC400C0B9C400C0B9C400C0BAC400C0BCC40040BFC400E0C1C40020C4C400A0C5C40060C5C40080C2C400E0BAC40000ADC4002097C400C072C400402AC40080B4C3000014C200808443000007440080444400C07C440040994400E0B3440000CE4400A0E6440000FD440050084500E0104500901845001020450090274500302F4500A0364500703D45295C5A42B81E55423D0A5242C3F54E4233334C4248E149427B144842A47046425C8F44420AD7414214AE3D4214AE3742CDCC2F42E17A2642A4701C425C8F12428FC20942E17A0242A470F94114AEEF419A99E541CDCCD8419A99C741B81EB14100009641EC517041EC5134413333FB40EC51A040C3F5384052B8BE3F3333333F0AD7A33E7B142E3EAE47E13D8FC2753D0AD7233C000000000AD7233D7B142E3EA470BD3E9A99193F295C4F3F8FC2753F713D8A3FF6289C3F9A99B93FCDCCEC3F1F851B4048E14A40000080409A999940C3F5B0408FC2C54048E1DA40CDCCF440D7A30C415C8F2641CDCC484148E1724133339141F628AA416666C241D7A3D841F628EC413333FD416666064233330E42CDCC16428FC22042B81E2C425C8F3842333345423D0A514200005B42A4706242333367425C8F6942F6286A42CDCC69423D0A69426666684285EB67421F856742F628674214AE66427B146642A470654248E16442EC5164420AD76342AE476342D7A362420AD76142C3F5604200006042C3F55E428FC25D42AE475C42295C5A42
"The hex string is a series of 2 byte integers in pairs of Position and Load. The first 2 bytes is Position1, the next 2=Load1, next 2=Position2, next 2=Load2, etc...
A byte is 2 Hex characters. "
Here is the c# that was provided to me by the developer with little context behind it
public class PositionLoadPoint
{
public float Position { get; private set; }
public float Load { get; private set; }
public PositionLoadPoint(float position, float load)
{
Position = position;
Load = load;
}
}
This method should return a list of points from an array of bytes:
public static IList<PositionLoadPoint> GetPositionLoadPoints(byte[] bytes)
{
IList<PositionLoadPoint> result = new List<PositionLoadPoint>();
int midIndex = bytes.Length / 2;
for (int i = 0; i < midIndex; i += 4)
{
byte[] load = new byte[4];
byte[] position = new byte[4];
Array.Copy(bytes, i, load, 0, 4);
Array.Copy(bytes, midIndex + i, position, 0, 4);
var point = new PositionLoadPoint(BitConverter.ToSingle(load, 0),
BitConverter.ToSingle(position, 0));
result.Add(point);
}
return result;
}
I'm struggling with this and its driving me crazy because i believe it should be crazy simple. Here is my python that i wrote, but i do not believe the results are correct since the plot is sporatic!
#INSERT LIBRARIES
import matplotlib.pyplot as plt
hex_string = '00703D450080474500704A4500E04B4500E04B4500D04A4500E0484500B0464500C044450090434500404345009043450000444500F0434500F0424500F0404500403E4500703B45000039450020374500F03545006035450090354500A0364500E0384500403C450090404500C0444500A04745008047450030434500D0394500702B4500A0184500A002450080D5440000A54400006C440040144400008943000050C100808AC3004003C400803EC4008077C4002096C40040ADC400A0BFC40040CCC40020D3C40040D5C40020D4C40040D1C40000CEC400C0CAC40080C7C40020C4C40000C1C40020BEC40040BCC40060BBC40060BBC400C0BBC400E0BBC40060BBC40080BAC400C0B9C400C0B9C400C0BAC400C0BCC40040BFC400E0C1C40020C4C400A0C5C40060C5C40080C2C400E0BAC40000ADC4002097C400C072C400402AC40080B4C3000014C200808443000007440080444400C07C440040994400E0B3440000CE4400A0E6440000FD440050084500E0104500901845001020450090274500302F4500A0364500703D45295C5A42B81E55423D0A5242C3F54E4233334C4248E149427B144842A47046425C8F44420AD7414214AE3D4214AE3742CDCC2F42E17A2642A4701C425C8F12428FC20942E17A0242A470F94114AEEF419A99E541CDCCD8419A99C741B81EB14100009641EC517041EC5134413333FB40EC51A040C3F5384052B8BE3F3333333F0AD7A33E7B142E3EAE47E13D8FC2753D0AD7233C000000000AD7233D7B142E3EA470BD3E9A99193F295C4F3F8FC2753F713D8A3FF6289C3F9A99B93FCDCCEC3F1F851B4048E14A40000080409A999940C3F5B0408FC2C54048E1DA40CDCCF440D7A30C415C8F2641CDCC484148E1724133339141F628AA416666C241D7A3D841F628EC413333FD416666064233330E42CDCC16428FC22042B81E2C425C8F3842333345423D0A514200005B42A4706242333367425C8F6942F6286A42CDCC69423D0A69426666684285EB67421F856742F628674214AE66427B146642A470654248E16442EC5164420AD76342AE476342D7A362420AD76142C3F5604200006042C3F55E428FC25D42AE475C42295C5A42'
#convert all two digit hex to decimal and place in list
hex_list = []
for i in range(0,len(hex_string),2):
hex_list.append(int(hex_string[i:i+2],16))
#GROUP TWO CONSECUTIVE DECIMALS IN HEX_LIST TOGETHER UNTIL ALL DECIMALS ARE GROUPED INTO PAIRS WITHIN A LIST
DEC_list_pair = []
for i in range(0,len(hex_list),2):
DEC_list_pair.append(hex_list[i:i+2])
#Create a x and y axis using the DEC_list_pair_no_duplicates list
x_axis = []
y_axis = []
for i in range(0,len(DEC_list_pair)):
x_axis.append(DEC_list_pair[i][0])
y_axis.append(DEC_list_pair[i][1])
#plot x_axis and y_axis
plt.plot(x_axis, y_axis)
plt.show()
Looks like the description doesn't match the C# code.
int midIndex = bytes.Length / 2;
for (int i = 0; i < midIndex; i += 4)
{
// Reading into 'load' from offset i from front of the array
Array.Copy(bytes, i, load, 0, 4);
// Reading into 'position' from offset i from midIndex of the array
Array.Copy(bytes, midIndex + i, position, 0, 4);
var point = new PositionLoadPoint(BitConverter.ToSingle(load, 0),
BitConverter.ToSingle(position, 0));
All the loads are coming from the front of the array, all the postions are coming after "midIndex".

Random number with Probabilities in C#

I have converted this Java program into a C# program.
using System;
using System.Collections.Generic;
namespace RandomNumberWith_Distribution__Test
{
public class DistributedRandomNumberGenerator
{
private Dictionary<Int32, Double> distribution;
private double distSum;
public DistributedRandomNumberGenerator()
{
distribution = new Dictionary<Int32, Double>();
}
public void addNumber(int val, double dist)
{
distribution.Add(val, dist);// are these two
distSum += dist; // lines correctly translated?
}
public int getDistributedRandomNumber()
{
double rand = new Random().NextDouble();//generate a double random number
double ratio = 1.0f / distSum;//why is ratio needed?
double tempDist = 0;
foreach (Int32 i in distribution.Keys)
{
tempDist += distribution[i];
if (rand / ratio <= tempDist)//what does "rand/ratio" signify? What does this comparison achieve?
{
return i;
}
}
return 0;
}
}
public class MainClass
{
public static void Main(String[] args)
{
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(2, 0.3d);
drng.addNumber(3, 0.5d);
//=================
// start simulation
int testCount = 1000000;
Dictionary<Int32, Double> test = new Dictionary<Int32, Double>();
for (int i = 0; i < testCount; i++)
{
int random = drng.getDistributedRandomNumber();
if (test.ContainsKey(random))
{
double prob = test[random]; // are these
prob = prob + 1.0 / testCount;// three lines
test[random] = prob; // correctly translated?
}
else
{
test.Add(random, 1.0 / testCount);// is this line correctly translated?
}
}
foreach (var item in test.Keys)
{
Console.WriteLine($"{item}, {test[item]}");
}
Console.ReadLine();
}
}
}
I have several questions:
Can you check if the marked-by-comment lines are correct or need explanation?
Why doesn't getDistributedRandomNumber() check if the sum of the distribution 1 before proceeding to further calculations?
The method
public void addNumber(int val, double dist)
Is not correctly translated, since you are missing the following lines:
if (this.distribution.get(value) != null) {
distSum -= this.distribution.get(value);
}
Those lines should cover the case when you call the following (based on your example code):
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(1, 0.5d);
So calling the method addNumber twice with the same first argument, the missing code part looks if the first argument is already present in the dictionary and if so it will remove the "old" value from the dictionary to insert the new value.
Your method should look like this:
public void addNumber(int val, double dist)
{
if (distribution.TryGetValue(val, out var oldDist)) //get the old "dist" value, based on the "val"
{
distribution.Remove(val); //remove the old entry
distSum -= oldDist; //substract "distSum" with the old "dist" value
}
distribution.Add(val, dist); //add the "val" with the current "dist" value to the dictionary
distSum += dist; //add the current "dist" value to "distSum"
}
Now to your second method
public int getDistributedRandomNumber()
Instead of calling initializing a new instance of Random every time this method is called you should only initialize it once, so change the line
double rand = new Random().NextDouble();
to this
double rand = _random.NextDouble();
and initialize the field _random outside of a method inside the class declaration like this
public class DistributedRandomNumberGenerator
{
private Dictionary<Int32, Double> distribution;
private double distSum;
private Random _random = new Random();
... rest of your code
}
This will prevent new Random().NextDouble() from producing the same number over and over again if called in a loop.
You can read about this problem here: Random number generator only generating one random number
As I side note, fields in c# are named with a prefix underscore. You should consider renaming distribution to _distribution, same applies for distSum.
Next:
double ratio = 1.0f / distSum;//why is ratio needed?
Ratio is need because the method tries its best to do its job with the information you have provided, imagine you only call this:
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
int random = drng.getDistributedRandomNumber();
With those lines you told the class you want to have the number 1 in 20% of the cases, but what about the other 80%?
And that's where the ratio variable comes in place, it calculates a comparable value based on the sum of probabilities you have given.
eg.
double ratio = 1.0f / distSum;
As with the latest example drng.addNumber(1, 0.2d); distSum will be 0.2, which translates to a probability of 20%.
double ratio = 1.0f / 0.2;
The ratio is 5.0, with a probability of 20% the ratio is 5 because 100% / 5 = 20%.
Now let's have a look at how the code reacts when the ratio is 5
double tempDist = 0;
foreach (Int32 i in distribution.Keys)
{
tempDist += distribution[i];
if (rand / ratio <= tempDist)
{
return i;
}
}
rand will be to any given time a value that is greater than or equal to 0.0, and less than 1.0., that's how NextDouble works, so let's assume the following 0.254557522132321 as rand.
Now let's look what happens step by step
double tempDist = 0; //initialize with 0
foreach (Int32 i in distribution.Keys) //step through the added probabilities
{
tempDist += distribution[i]; //get the probabilities and add it to a temporary probability sum
//as a reminder
//rand = 0.254557522132321
//ratio = 5
//rand / ratio = 0,0509115044264642
//tempDist = 0,2
// if will result in true
if (rand / ratio <= tempDist)
{
return i;
}
}
If we didn't apply the ratio the if would be false, but that would be wrong, since we only have a single value inside our dictionary, so no matter what the rand value might be the if statement should return true and that's the natur of rand / ratio.
To "fix" the randomly generated number based on the sum of probabilities we added. The rand / ratio will only be usefull if you didn't provide probabilites that perfectly sum up to 1 = 100%.
eg. if your example would be this
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(2, 0.3d);
drng.addNumber(3, 0.5d);
You can see that the provided probabilities equal to 1 => 0.2 + 0.3 + 0.5, in this case the line
if (rand / ratio <= tempDist)
Would look like this
if (rand / 1 <= tempDist)
Divding by 1 will never change the value and rand / 1 = rand, so the only use case for this devision are cases where you didn't provided a perfect 100% probability, could be either more or less.
As a side note, I would suggest changing your code to this
//call the dictionary distributions (notice the plural)
//dont use .Keys
//var distribution will be a KeyValuePair
foreach (var distribution in distributions)
{
//access the .Value member of the KeyValuePair
tempDist += distribution.Value;
if (rand / ratio <= tempDist)
{
return i;
}
}
Your test routine seems to be correctly translated.

NAudio FFT returns small and equal magnitude values for all frequencies

I'm working on a project with NAudio 1.9 and I want to compute an fft for an entire song, i.e split the song in chunks of equal size and compute fft for each chunk. The problem is that NAudio FFT function returns really small and equal values for any freq in the freq spectrum.
I searched for previous related posts but none seemed to help me.
The code that computes FFT using NAudio:
public IList<FrequencySpectrum> Fft(uint windowSize) {
IList<Complex[]> timeDomainChunks = this.SplitInChunks(this.audioContent, windowSize);
return timeDomainChunks.Select(this.ToFrequencySpectrum).ToList();
}
private IList<Complex[]> SplitInChunks(float[] audioContent, uint chunkSize) {
IList<Complex[]> splittedContent = new List<Complex[]>();
for (uint k = 0; k < audioContent.Length; k += chunkSize) {
long size = k + chunkSize < audioContent.Length ? chunkSize : audioContent.Length - k;
Complex[] chunk = new Complex[size];
for (int i = 0; i < chunk.Length; i++) {
//i've tried windowing here but didn't seem to help me
chunk[i].X = audioContent[k + i];
chunk[i].Y = 0;
}
splittedContent.Add(chunk);
}
return splittedContent;
}
private FrequencySpectrum ToFrequencySpectrum(Complex[] timeDomain) {
int m = (int) Math.Log(timeDomain.Length, 2);
//true = forward fft
FastFourierTransform.FFT(true, m, timeDomain);
return new FrequencySpectrum(timeDomain, 44100);
}
The FrequencySpectrum:
public struct FrequencySpectrum {
private readonly Complex[] frequencyDomain;
private readonly uint samplingFrequency;
public FrequencySpectrum(Complex[] frequencyDomain, uint samplingFrequency) {
if (frequencyDomain.Length == 0) {
throw new ArgumentException("Argument value must be greater than 0", nameof(frequencyDomain));
}
if (samplingFrequency == 0) {
throw new ArgumentException("Argument value must be greater than 0", nameof(samplingFrequency));
}
this.frequencyDomain = frequencyDomain;
this.samplingFrequency = samplingFrequency;
}
//returns magnitude for freq
public float this[uint freq] {
get {
if (freq >= this.samplingFrequency) {
throw new IndexOutOfRangeException();
}
//find corresponding bin
float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);
Complex c = this.frequencyDomain[checked((uint) k)];
return (float) Math.Sqrt(c.X * c.X + c.Y * c.Y);
}
}
}
for a file that contains a sine wave of 440Hz
expected output: values like 0.5 for freq=440 and 0 for the others
actual output: values like 0.000168153987f for any freq in the spectrum
It seems that I made 4 mistakes:
1) Here I'm asumming that sampling freq is 44100. This was not the reason my code wasn't working, though
return new FrequencySpectrum(timeDomain, 44100);
2) Always make a visual representation of your output data! I must learn this lesson... It seems that for a file containing a 440Hz sine wave I'm getting the right result but...
3) The frequency spectrum is a little shifted from what I was expecting because of this:
int m = (int) Math.Log(timeDomain.Length, 2);
FastFourierTransform.FFT(true, m, timeDomain);
timeDomain is an array of size 44100 becaused that's the value of windowSize (I called the method with windowSize = 44100), but FFT method expects a window size with a value power of 2. I'm saying "Here, NAudio, compute me the fft of this array that has 44100 elements, but take into account only the first 32768". I didn't realize that this was going to have serious implications on the result:
float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);
Here this.FftWindowSize is a property based on the size of the array, not on m. So, after visualizing the result I found out that magnitude of 440Hz freq was actually corresponding to the call:
spectrum[371]
instead of
spectrum[440]
So, my mistake was that the window size of fft (m) was not corresponding to the actual length of the array (FrequencySpectrum.FftWindowSize).
4) The small values that I was receiving for the magnitudes came from the fact that the audio file on which I was testing my code wasn't recorded with enough gain.

Determine if a number can be made with prepicked numbers and times

I have 2 arrays one of the types of numbers that will be used and the 2nd array is how many times that number can be used. I have a letter that determines what kind of method will be used I need to figure out how many times I can use a certain number from an array to determine a letter+number The ‘number’ is what I have to make with all the available numbers I can use. If the number cannot be made I would like to just say number cant be made or anything but allow the program to move on.
Here is what I have
int[] picksToUse = { 100, 50, 20, 10, 5, 1 };
int[] timesToUse = { 10, 10, 10, 10, 10, 10 };
string choice = Console.ReadLine();
string input = "";
if(choice.Length > 2)
{
input = choice.Substring(choice.IndexOf("$") + 1);
}
if(...){
}
else if (choice.Equals("D"))
{
int amt = Convert.ToInt32(input);
// code here to determine if number can be made with above choices
Dispense(amt, timesToUse);
}
Assuming picksToUse and timesToUse are exactly the same as you declared them, here's a way to know if you have enough of everything in stock to "pay up". It's a boolean function, which uses recursion. You would call it with the amount needed as parameter, and it would tell you right there if you have enough of everything.
Private Function HasCashInStock(amount As Integer, Optional index As Integer = 0) As Boolean
Dim billsNeeded As Integer = amount \ picksToUse(index)
If billsNeeded > timesToUse(index) Then
Return False
End If
amount -= picksToUse(index) * billsNeeded
If amount = 0 Then
Return True
End If
Return HasCashInStock(amount, index + 1)
End Function
The \ is an integer division operator (in VB.NET, at least - I'm shamelessly letting you translate this code). If you're not familiar with the integer division operator, well, when you use it with integer it gets rid of the floating numbers.
3 / 2 is not valid on integers, because it would yield 1.5.
3 \ 2 is valid on integers, and will yield 1.
That's all there is to it, really. Oh yeah, and recursion. I like recursion, but others will tell you to avoid it as much as you can. What can I say, I think that a nice recursive function has elegance.
You can also totally copy this function another time, modify it and use it to subtracts from your timesToUse() array once you know for sure that there's enough of everything to pay up.
If HasCashInStock(HereIsTheAmountAsInteger) Then
GivesTheMoney(HereIsTheAmountAsInteger)
End If
Having two functions is not the leanest code but it would be more readable. Have fun!
This is what I used to finish my project.
public static bool Validate(int amount, int[] total, int[] needed)
{
int[] billCount = total;
int[] cash = { 100, 50, 20, 10, 5, 1 };
int total = amount;
bool isValid = true;
for (int i = 0; i < total.Length; i++)
{
if(total >= cash[i])
{
billCount[i] = billCount[i] - needed[i];
}
if(billCount[i] < 0)
{
isValid = false;
break;
}
}
return isValid;
}
I got some working code. I did it with a class. I remember when I couldn't see what good classes were. Now I can't brush my teeth without a class. :-)
I force myself to do these problems to gain a little experience in C#. Now I have 3 things I like about C#.
class ATM
{
public int Denomination { get; set; }
public int Inventory { get; set; }
public ATM(int denom, int inven)
{
Denomination = denom;
Inventory = inven;
}
}
List<int> Bills = new List<int>();
List<ATM> ATMs = new List<ATM>();
private void OP2()
{
int[] picksToUse = { 100, 50, 20, 10, 5, 1 };
foreach (int d in picksToUse )
{
ATM atm = new ATM(d, 10);
ATMs.Add(atm);
}
//string sAmtRequested = Console.ReadLine();
string sAmtRequested = textBox1.Text;
if (int.TryParse(sAmtRequested, out int AmtRequested))
{
int RunningBalance = AmtRequested;
do
{
ATM BillReturn = GetBill(RunningBalance);
if (BillReturn is null)
{
MessageBox.Show("Cannot complete transaction");
return;
}
RunningBalance -= BillReturn.Denomination;
} while (RunningBalance > 0);
}
else
{
MessageBox.Show("Non-numeric request.");
return;
}
foreach (int bill in Bills)
Debug.Print(bill.ToString());
Debug.Print("Remaining Inventory");
foreach (ATM atm in ATMs)
Debug.Print($"For Denomination {atm.Denomination} there are {atm.Inventory} bills remaining");
}
private ATM GetBill(int RequestBalance)
{
var FilteredATMs = from atm in ATMs
where atm.Inventory > 0
orderby atm.Denomination descending
select atm;
foreach (ATM bill in FilteredATMs)
{
if (RequestBalance >= bill.Denomination )
{
bill.Inventory -= 1;
Bills.Add(bill.Denomination);
return bill;
}
}
return null;
}

selection based on percentage weighting

I have a set of values, and an associated percentage for each:
a: 70% chance
b: 20% chance
c: 10% chance
I want to select a value (a, b, c) based on the percentage chance given.
how do I approach this?
my attempt so far looks like this:
r = random.random()
if r <= .7:
return a
elif r <= .9:
return b
else:
return c
I'm stuck coming up with an algorithm to handle this. How should I approach this so it can handle larger sets of values without just chaining together if-else flows.
(any explanation or answers in pseudo-code are fine. a python or C# implementation would be especially helpful)
Here is a complete solution in C#:
public class ProportionValue<T>
{
public double Proportion { get; set; }
public T Value { get; set; }
}
public static class ProportionValue
{
public static ProportionValue<T> Create<T>(double proportion, T value)
{
return new ProportionValue<T> { Proportion = proportion, Value = value };
}
static Random random = new Random();
public static T ChooseByRandom<T>(
this IEnumerable<ProportionValue<T>> collection)
{
var rnd = random.NextDouble();
foreach (var item in collection)
{
if (rnd < item.Proportion)
return item.Value;
rnd -= item.Proportion;
}
throw new InvalidOperationException(
"The proportions in the collection do not add up to 1.");
}
}
Usage:
var list = new[] {
ProportionValue.Create(0.7, "a"),
ProportionValue.Create(0.2, "b"),
ProportionValue.Create(0.1, "c")
};
// Outputs "a" with probability 0.7, etc.
Console.WriteLine(list.ChooseByRandom());
For Python:
>>> import random
>>> dst = 70, 20, 10
>>> vls = 'a', 'b', 'c'
>>> picks = [v for v, d in zip(vls, dst) for _ in range(d)]
>>> for _ in range(12): print random.choice(picks),
...
a c c b a a a a a a a a
>>> for _ in range(12): print random.choice(picks),
...
a c a c a b b b a a a a
>>> for _ in range(12): print random.choice(picks),
...
a a a a c c a c a a c a
>>>
General idea: make a list where each item is repeated a number of times proportional to the probability it should have; use random.choice to pick one at random (uniformly), this will match your required probability distribution. Can be a bit wasteful of memory if your probabilities are expressed in peculiar ways (e.g., 70, 20, 10 makes a 100-items list where 7, 2, 1 would make a list of just 10 items with exactly the same behavior), but you could divide all the counts in the probabilities list by their greatest common factor if you think that's likely to be a big deal in your specific application scenario.
Apart from memory consumption issues, this should be the fastest solution -- just one random number generation per required output result, and the fastest possible lookup from that random number, no comparisons &c. If your likely probabilities are very weird (e.g., floating point numbers that need to be matched to many, many significant digits), other approaches may be preferable;-).
Knuth references Walker's method of aliases. Searching on this, I find http://code.activestate.com/recipes/576564-walkers-alias-method-for-random-objects-with-diffe/ and http://prxq.wordpress.com/2006/04/17/the-alias-method/. This gives the exact probabilities required in constant time per number generated with linear time for setup (curiously, n log n time for setup if you use exactly the method Knuth describes, which does a preparatory sort you can avoid).
Take the list of and find the cumulative total of the weights: 70, 70+20, 70+20+10. Pick a random number greater than or equal to zero and less than the total. Iterate over the items and return the first value for which the cumulative sum of the weights is greater than this random number:
def select( values ):
variate = random.random() * sum( values.values() )
cumulative = 0.0
for item, weight in values.items():
cumulative += weight
if variate < cumulative:
return item
return item # Shouldn't get here, but just in case of rounding...
print select( { "a": 70, "b": 20, "c": 10 } )
This solution, as implemented, should also be able to handle fractional weights and weights that add up to any number so long as they're all non-negative.
Let T = the sum of all item weights
Let R = a random number between 0 and T
Iterate the item list subtracting each item weight from R and return the item that causes the result to become <= 0.
def weighted_choice(probabilities):
random_position = random.random() * sum(probabilities)
current_position = 0.0
for i, p in enumerate(probabilities):
current_position += p
if random_position < current_position:
return i
return None
Because random.random will always return < 1.0, the final return should never be reached.
import random
def selector(weights):
i=random.random()*sum(x for x,y in weights)
for w,v in weights:
if w>=i:
break
i-=w
return v
weights = ((70,'a'),(20,'b'),(10,'c'))
print [selector(weights) for x in range(10)]
it works equally well for fractional weights
weights = ((0.7,'a'),(0.2,'b'),(0.1,'c'))
print [selector(weights) for x in range(10)]
If you have a lot of weights, you can use bisect to reduce the number of iterations required
import random
import bisect
def make_acc_weights(weights):
acc=0
acc_weights = []
for w,v in weights:
acc+=w
acc_weights.append((acc,v))
return acc_weights
def selector(acc_weights):
i=random.random()*sum(x for x,y in weights)
return weights[bisect.bisect(acc_weights, (i,))][1]
weights = ((70,'a'),(20,'b'),(10,'c'))
acc_weights = make_acc_weights(weights)
print [selector(acc_weights) for x in range(100)]
Also works fine for fractional weights
weights = ((0.7,'a'),(0.2,'b'),(0.1,'c'))
acc_weights = make_acc_weights(weights)
print [selector(acc_weights) for x in range(100)]
today, the update of python document give an example to make a random.choice() with weighted probabilities:
If the weights are small integer ratios, a simple technique is to build a sample population with repeats:
>>> weighted_choices = [('Red', 3), ('Blue', 2), ('Yellow', 1), ('Green', 4)]
>>> population = [val for val, cnt in weighted_choices for i in range(cnt)]
>>> random.choice(population)
'Green'
A more general approach is to arrange the weights in a cumulative distribution with itertools.accumulate(), and then locate the random value with bisect.bisect():
>>> choices, weights = zip(*weighted_choices)
>>> cumdist = list(itertools.accumulate(weights))
>>> x = random.random() * cumdist[-1]
>>> choices[bisect.bisect(cumdist, x)]
'Blue'
one note: itertools.accumulate() needs python 3.2 or define it with the Equivalent.
I think you can have an array of small objects (I implemented in Java although I know a little bit C# but I am afraid can write wrong code), so you may need to port it yourself. The code in C# will be much smaller with struct, var but I hope you get the idea
class PercentString {
double percent;
String value;
// Constructor for 2 values
}
ArrayList<PercentString> list = new ArrayList<PercentString();
list.add(new PercentString(70, "a");
list.add(new PercentString(20, "b");
list.add(new PercentString(10, "c");
double percent = 0;
for (int i = 0; i < list.size(); i++) {
PercentString p = list.get(i);
percent += p.percent;
if (random < percent) {
return p.value;
}
}
If you are really up to speed and want to generate the random values quickly, the Walker's algorithm mcdowella mentioned in https://stackoverflow.com/a/3655773/1212517 is pretty much the best way to go (O(1) time for random(), and O(N) time for preprocess()).
For anyone who is interested, here is my own PHP implementation of the algorithm:
/**
* Pre-process the samples (Walker's alias method).
* #param array key represents the sample, value is the weight
*/
protected function preprocess($weights){
$N = count($weights);
$sum = array_sum($weights);
$avg = $sum / (double)$N;
//divide the array of weights to values smaller and geq than sum/N
$smaller = array_filter($weights, function($itm) use ($avg){ return $avg > $itm;}); $sN = count($smaller);
$greater_eq = array_filter($weights, function($itm) use ($avg){ return $avg <= $itm;}); $gN = count($greater_eq);
$bin = array(); //bins
//we want to fill N bins
for($i = 0;$i<$N;$i++){
//At first, decide for a first value in this bin
//if there are small intervals left, we choose one
if($sN > 0){
$choice1 = each($smaller);
unset($smaller[$choice1['key']]);
$sN--;
} else{ //otherwise, we split a large interval
$choice1 = each($greater_eq);
unset($greater_eq[$choice1['key']]);
}
//splitting happens here - the unused part of interval is thrown back to the array
if($choice1['value'] >= $avg){
if($choice1['value'] - $avg >= $avg){
$greater_eq[$choice1['key']] = $choice1['value'] - $avg;
}else if($choice1['value'] - $avg > 0){
$smaller[$choice1['key']] = $choice1['value'] - $avg;
$sN++;
}
//this bin comprises of only one value
$bin[] = array(1=>$choice1['key'], 2=>null, 'p1'=>1, 'p2'=>0);
}else{
//make the second choice for the current bin
$choice2 = each($greater_eq);
unset($greater_eq[$choice2['key']]);
//splitting on the second interval
if($choice2['value'] - $avg + $choice1['value'] >= $avg){
$greater_eq[$choice2['key']] = $choice2['value'] - $avg + $choice1['value'];
}else{
$smaller[$choice2['key']] = $choice2['value'] - $avg + $choice1['value'];
$sN++;
}
//this bin comprises of two values
$choice2['value'] = $avg - $choice1['value'];
$bin[] = array(1=>$choice1['key'], 2=>$choice2['key'],
'p1'=>$choice1['value'] / $avg,
'p2'=>$choice2['value'] / $avg);
}
}
$this->bins = $bin;
}
/**
* Choose a random sample according to the weights.
*/
public function random(){
$bin = $this->bins[array_rand($this->bins)];
$randValue = (lcg_value() < $bin['p1'])?$bin[1]:$bin[2];
}
Here is my version that can apply to any IList and normalize the weight. It is based on Timwi's solution : selection based on percentage weighting
/// <summary>
/// return a random element of the list or default if list is empty
/// </summary>
/// <param name="e"></param>
/// <param name="weightSelector">
/// return chances to be picked for the element. A weigh of 0 or less means 0 chance to be picked.
/// If all elements have weight of 0 or less they all have equal chances to be picked.
/// </param>
/// <returns></returns>
public static T AnyOrDefault<T>(this IList<T> e, Func<T, double> weightSelector)
{
if (e.Count < 1)
return default(T);
if (e.Count == 1)
return e[0];
var weights = e.Select(o => Math.Max(weightSelector(o), 0)).ToArray();
var sum = weights.Sum(d => d);
var rnd = new Random().NextDouble();
for (int i = 0; i < weights.Length; i++)
{
//Normalize weight
var w = sum == 0
? 1 / (double)e.Count
: weights[i] / sum;
if (rnd < w)
return e[i];
rnd -= w;
}
throw new Exception("Should not happen");
}
I've my own solution for this:
public class Randomizator3000
{
public class Item<T>
{
public T value;
public float weight;
public static float GetTotalWeight<T>(Item<T>[] p_itens)
{
float __toReturn = 0;
foreach(var item in p_itens)
{
__toReturn += item.weight;
}
return __toReturn;
}
}
private static System.Random _randHolder;
private static System.Random _random
{
get
{
if(_randHolder == null)
_randHolder = new System.Random();
return _randHolder;
}
}
public static T PickOne<T>(Item<T>[] p_itens)
{
if(p_itens == null || p_itens.Length == 0)
{
return default(T);
}
float __randomizedValue = (float)_random.NextDouble() * (Item<T>.GetTotalWeight(p_itens));
float __adding = 0;
for(int i = 0; i < p_itens.Length; i ++)
{
float __cacheValue = p_itens[i].weight + __adding;
if(__randomizedValue <= __cacheValue)
{
return p_itens[i].value;
}
__adding = __cacheValue;
}
return p_itens[p_itens.Length - 1].value;
}
}
And using it should be something like that (thats in Unity3d)
using UnityEngine;
using System.Collections;
public class teste : MonoBehaviour
{
Randomizator3000.Item<string>[] lista;
void Start()
{
lista = new Randomizator3000.Item<string>[10];
lista[0] = new Randomizator3000.Item<string>();
lista[0].weight = 10;
lista[0].value = "a";
lista[1] = new Randomizator3000.Item<string>();
lista[1].weight = 10;
lista[1].value = "b";
lista[2] = new Randomizator3000.Item<string>();
lista[2].weight = 10;
lista[2].value = "c";
lista[3] = new Randomizator3000.Item<string>();
lista[3].weight = 10;
lista[3].value = "d";
lista[4] = new Randomizator3000.Item<string>();
lista[4].weight = 10;
lista[4].value = "e";
lista[5] = new Randomizator3000.Item<string>();
lista[5].weight = 10;
lista[5].value = "f";
lista[6] = new Randomizator3000.Item<string>();
lista[6].weight = 10;
lista[6].value = "g";
lista[7] = new Randomizator3000.Item<string>();
lista[7].weight = 10;
lista[7].value = "h";
lista[8] = new Randomizator3000.Item<string>();
lista[8].weight = 10;
lista[8].value = "i";
lista[9] = new Randomizator3000.Item<string>();
lista[9].weight = 10;
lista[9].value = "j";
}
void Update ()
{
Debug.Log(Randomizator3000.PickOne<string>(lista));
}
}
In this example each value has a 10% chance do be displayed as a debug =3
Based loosely on python's numpy.random.choice(a=items, p=probs), which takes an array and a probability array of the same size.
public T RandomChoice<T>(IEnumerable<T> a, IEnumerable<double> p)
{
IEnumerator<T> ae = a.GetEnumerator();
Random random = new Random();
double target = random.NextDouble();
double accumulator = 0;
foreach (var prob in p)
{
ae.MoveNext();
accumulator += prob;
if (accumulator > target)
{
break;
}
}
return ae.Current;
}
The probability array p must sum to (approx.) 1. This is to keep it consistent with the numpy interface (and mathematics), but you could easily change that if you wanted.

Categories

Resources