Hi I am trying to write and then read a 3d array of strings to a file. The array is declared as theatre[5, 5, 9]. I have been looking but cant find anything I understand. It is basically just to switch between pages in a wp8 app.
How can I do this?
Any help is much appreciated.
Thanks.
Edit: It seems you can simply use BinaryFormatter.Serialize() directly on your array as-is. It goes something like this:
using System.Runtime.Serialization.Formatters.Binary;
...
// writing
FileStream fs = File.Open("...");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, theArray);
// reading
string[,,] theArray;
FileStream fs = File.Open("...");
BinaryFormatter bf = new BinaryFormatter();
theArray = (string[,,])bf.Deserialize(fs);
First solution (try this if BinaryFormatter fails):
You can translate between 3D and 1D as follows:
struct Vector {
public int x;
public int y;
public int z;
Vector(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
Vector GetIndices3d(int i, Vector bounds)
{
Vector indices = new Vector();
int zSize = bounds.x * bounds.y;
indices.x = (i % zSize) % bounds.x;
indices.y = (i % zSize) / bounds.x;
indices.z = i / zSize;
return indices;
}
int GetIndex1d(Vector indices, Vector bounds)
{
return (indices.z * (bounds.x * bounds.y)) +
(indices.y * bounds.x) +
indices.x;
}
Then it's just a matter of turning the 3D array to a 1D array and serializing it to a file. Do the opposite for reading.
string[] Get1dFrom3d(string[,,] data)
{
Vector bounds = new Vector(data.GetLength(0), data.GetLength(1), data.GetLength(2));
string[] result = new string[data.Length];
Vector v;
for (int i = 0; i < data.Length; ++i)
{
v = GetIndices3d(i, bounds);
result[i] = data[v.x, v.y, v.z];
}
return result;
}
string[,,] Get3dFrom1d(string[] data, Vector bounds)
{
string[,,] result = new string[bounds.x, bounds.y, bounds.z];
Vector v;
for (int i = 0; i < data.Length; ++i)
{
v = GetIndices3d(i, bounds);
result[v.x, v.y, v.z] = data[i];
}
return result;
}
Serializing the data to a file depends on the content of the data. You can choose a seperator character that does not appear in any of the data, and concatenate the strings using the separator.
If it's not possible to determine a distinct seperator character, you can choose one at your own convenience, and preprocess the strings such that the separator is escaped where it naturally appears in the data. This is usually done by inserting the seperator where it appears in the string such that it appears twise. Then handle this when reading the file (ie: pairs of separators = natural occurence of character data).
Another approach would be to turn everything into hexadecimal, and use some arbitrary separator. This will more or less double the file size.
From your last comment, it seems like you don't need a 3D array, even by relying on the quickest/dirtiest approach, you can/should use a 2D one. But you can avoid the second dimension by creating a custom class with the properties you want. Sample code:
seat[] theatre = new seat[5]; //5 seats
int count0 = -1;
do
{
count0 = count0 + 1; //Seat number
theatre[count0] = new seat();
theatre[count0].X = 1; //X value for Seat count0
theatre[count0].Y = 2; //Y value for Seat count0
theatre[count0].Prop1 = "prop 1"; //Prop1 for Seat count0
theatre[count0].Prop2 = "prop 2"; //Prop2 for Seat count0
theatre[count0].Prop3 = "prop 3"; //Prop3 for Seat count0
} while (count0 < theatre.GetLength(0) - 1);
Where seat is defined by the following code:
class seat
{
public int X { get; set; }
public int Y { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
Related
I am trying to serialize a 2D array of strings to send over a network.
I have a couple ideas but I seem to be blocked by api or efficiency. I really don't want to serialize and send a small 2D array across a network 10 times a second when I only need it when I collide with something and ask for the information.
I have a list of items a player has in his cart. I want to make it okay to steal from that cart, so the other player has to be able to see what's in the cart.
If I use onphotonserializeview I thought I could convert the flatten the 2d array of strings into a 1D array of strings with a delimiter and stream.sendnext the delimited line and reconstitute on the other side, or some variation of that functionality.
public void serialize_merchandise_list() {
int width = 0;
int length = 0;
while((width < cart_list_width) && (length < cart_list_width)) {
width++;
if (width >= cart_list_width) {
length++;
width = 0;
}
// convert to bytes and then to string and append
System.Text.ASCIIEncoding.ASCII.GetBytes(merchandise_items[width, length]));
}
}
But doing that unnecessarily is an operation time nightmare, I want it on demand.
I was going to do a punrpc but I can't get a return value or ref parameter. What is the best approach here?
I want:
collide with cart
do you want to steal from cart --> yes --> show list
else --> go on your way
Afaik a one-dimensional array of strings would be no problem for Photon.
So I guess you could just flatten the array and additionally send the second size - lets name it element size (in contrary to the first one - the element count) as parameters.
Like e.g. lets say your array looks like
string[,] array2D = new string[4,2]{{"a", "b"}, {"c","d"}, {"e","f"}, {"g","h"}};
Then you would know the second (elementSize) dimension using either hardcoded or GetLength(int)
var elementSize = array2D.GetLength(1);
and you would simply flatten the array using
string[] arrayFlat = array2D.Cast<string>().ToArray();
So you would send these two informations via photon (I don't know photon in detail) but afaik you can do this one time without having to send it continiously. The two parameters would be
int elementSize, string[] arrayFlat
in this case with the values
2, string[8]{"a","b","c","d","e","f","g","h"}
So together:
public void SendArray2D()
{
int elementSize = array2D.GetLength(1);
string[] arrayFlat = array2D.Cast<string>().ToArray();
photonView.RPC(nameof(ReceiveArray2D), RpcTarget.All, elementSize, (object) arrayFlat);
}
Then on the receiver part you are getting
int elementSize = 2, string[] arrayFlat = string[8]{"a","b","c","d","e","f","g","h"}
and have to convert it back. You already know the second dimension elementSize = 2 so in order to get also the first one you simply do
var elementCount = arrayFlat.Length / elementSize; // = 4
so you know the 2D array you will have to fill would be
string[,] array2D = new string[elementCount, elementSize]; // = 4,2
and then you can simply iterate it and do something like
for (var x = 0; x < elementCount; x++)
{
for (int y = 0; y < elementSize; y++)
{
array2D[x, y] = arrayFlat[x * elementSize + y];
}
}
So together
[PunRpc]
public void ReceiveArray2D(int elementSize, string[] arrayFlat)
{
var elementCount = arrayFlat.Length / elementSize;
array2D = new string[elementCount, elementSize];
for (var x = 0; x < elementCount; x++)
{
for (int y = 0; y < elementSize; y++)
{
array2D[x, y] = arrayFlat[x * elementSize + y];
}
}
// Could also e.g. call some event like
OnReceivedArray2D?.Invoke (array2D);
}
public event Action<string[,]> OnReceivedArray2D;
So you could attach listeners to the event via
reference.OnReceivedArray2D += SomeHandler;
private void SomeHandler(string [,])
{
// ...
}
Or alternatively you could implement a class that stores your data in a flat array but lets you access it like if it would be a 2D array (in general a 2D array is anyway stored in memory as a flat one)
[Serializable]
public class SerializableArray2D
{
public readonly string[] ArrayFlat;
public readonly ElementSize;
public SerializableArray2D(int elementSize, string[] arrayFlat)
{
ElementSize = elementSize;
ArrayFlat = arrayFlat;
}
public SerializableArray2D(string[,] array2D) : this(array2D.GetLength(1), array2D.Cast<string>().ToArray()) { }
public SerializableArray2D(int elementSize, int elementCount) : this(elementSize, new string[elementCount]){}
public string this[int x, int y]
{
get { return ArrayFlat[x * ElementSize + y]; }
set { ArrayFlat[x * ElementSize + y] = value; }
}
}
usage would be e.g. Initialize it
var array2D = new string[4,2]{{"a", "b"}, {"c","d"}, {"e","f"}, {"g","h"}};
var serializableArray2D = new SerializableArray2D(array2D);
For accessing specific indices like in a 2D array
string example = serializableArray2D[1,1];
serializableArray2D[3,0] = "example";
For sending it
photonView.RPC(nameof(Receive), RpcTarget.All, serializableArray2D.ElementSize, (object)serializableArray2D.ArrayFlat);
And when receiving it
[PunRPC]
public void Receive(int elementSize, string[] arrayFlat)
{
serializableArray2D = new SerializableArray2D(elementSize, arrayFlat);
// Again e.g. call an event
OnReceivedSerializableArray2D?.Invoke(serializableArray2D);
}
public event Action<SerializableArray2D> OnReceivedSerializableArray2D;
This is an assignment from university. I have to do a Radix sort on car registration plates (ABC 123) in two ways 1) array 2) linked list. The most interesting thing is that sorting MUST BE done in the file. For example, from now on we will talk only about array. I generate car numbers and put them in array, then with binary write I write all generated car reg plates to the file. After that I give the newly generated file to Radix Sort and he need to do the magic. I will show you the code that I have at the moment, but it's not actually a 'real' radix sort, because my mind cannot understand how would I implement radix sort in file. ( I have implemented radix sort for normal array and linked list, but when it is done INSIDE a file it is mind blowing). I just wanted to ask if any of you would have any tips or ideas on how I could improve the sorting algorithm, because it is hella slow. Thank you.
PROGRAM.CS
public static void CountingSort(DataArray items, int exp)
{
UTF8Encoding encoder = new UTF8Encoding();
Byte[] forChange = new byte[16];
double first, second;
int i, j;
NumberPlate plate1;
NumberPlate plate2;
for (int z = 0; z < items.Length; z++)
{
i = 0;
j = 1;
while (j < items.Length)
{
BitConverter.GetBytes(items[i]).CopyTo(forChange, 0);
BitConverter.GetBytes(items[j]).CopyTo(forChange, 8);
string firstPlate = encoder.GetString(forChange, 1, 7);
string[] partsFirst = firstPlate.Split(' ');
plate1 = new NumberPlate(partsFirst[0], partsFirst[1]);
string secondPlate = encoder.GetString(forChange, 9, 7);
string[] partsSecond = secondPlate.Split(' ');
plate2 = new NumberPlate(partsSecond[0], partsSecond[1]);
first = plate1.GetPlateCode() / exp % 10;
second = plate2.GetPlateCode() / exp % 10;
if (first > second)
{
items.Swap(j, BitConverter.ToDouble(forChange, 0), BitConverter.ToDouble(forChange, 8));
}
i++;
j++;
}
}
}
public static void Radix_Sort(DataArray items)
{
for (int exp = 1; exp < Math.Pow(10, 9); exp *= 10)
{
CountingSort(items, exp);
}
}
public static void Test_File_Array_List(int seed)
{
int n = 5;
string filename;
filename = #"mydataarray.txt";
//filename = #"mydataarray.dat";
MyFileArray myfilearray = new MyFileArray(filename, n);
using (myfilearray.fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite))
{
Console.WriteLine("\n FILE ARRAY \n");
myfilearray.Print(n);
Radix_Sort(myfilearray);
myfilearray.Print(n);
}
}
ARRAY.CS
public override double this[int index]
{
get
{
Byte[] data = new Byte[8];
fs.Seek(8 * index, SeekOrigin.Begin);
fs.Read(data, 0, 8);
double result = BitConverter.ToDouble(data, 0);
return result;
}
}
public override void Swap(int j, double a, double b)
{
Byte[] data = new Byte[16];
BitConverter.GetBytes(b).CopyTo(data, 0);
BitConverter.GetBytes(a).CopyTo(data, 8);
fs.Seek(8 * (j - 1), SeekOrigin.Begin);
fs.Write(data, 0, 16);
}
If the assignment mentions array and linked list, then it would seem that the file is only used to read the data into the array or linked list, then then sort is done, and the result is written to a file.
For a file based radix sort, for each digit (right to left), 10 temp files are created, the data is read and appended to file "digit" based on the digit, then the 10 temp files are closed, then concatenated into a single working file for the next radix sort step. For each letter, 26 temp files would be used.
My task is to implement a random number generator using LCG algorithm.
The task is to generate 1000 axes (x,y) between [-1, 1] and print them on a pane.
If the point is inside the circle of radius 1.0, it will be printed as Red.
Otherwise, Blue.
I used the parameters suggested by Numerical Recipes suggested in this YouTube video. I am following the coding style used in this link.
I am using ZedGraph to show my plots.
Why are the random numbers not properly scattered on the pane?
And, where are the blue points?
Random number generator class:
class MyRandom
{
long m = 4294967296;// modulus
long a = 1664525; // multiplier
long c = 1013904223; // increment
public long nextRandomInt(long seed)
{
return (((a * seed + c) % m));
}
private double nextRandomDouble(long seed)
{
return (2 * (nextRandomInt(seed) / m)) - 1;
}
public double nextRandomDouble(double seed)
{
double new_seed = seed + 1.0;
new_seed = new_seed / 2.0;
new_seed = new_seed * m;
long long_seed = Convert.ToInt64(new_seed);
double new_s = nextRandomInt(long_seed);
new_s = new_s / m;
new_s = new_s * 2;
new_s = new_s - 1;
return new_s;
}
}
Output
Additional Source Code:
Driver Program:
class Program
{
static void Main(string[] args)
{
int N = 1000;
double radius = 1.0;
List<double> rx = new List<double>(); rx.Add(0.0);
List<double> ry = new List<double>(); ry.Add(1.0);
MyRandom r = new MyRandom();
for (int i = 0; i < N; i++)
{
double x = r.nextRandomDouble(rx[rx.Count - 1]);
double y = r.nextRandomDouble(ry[ry.Count - 1]);
rx.Add(x);
ry.Add(y);
}
PlotForm form = new PlotForm();
ZedGraphControl zgControl = form.ZedGrapgControl;
//// get a reference to the GraphPane
GraphPane gPane = zgControl.GraphPane;
gPane.Title.Text = "Random Numbers";
gPane.XAxis.Type = AxisType.Linear;
PointPairList insideCircleList = new PointPairList();
PointPairList outsideCircleList = new PointPairList();
for (int i = 0; i < N; i++)
{
double x = rx[i];
double y = ry[i];
if ((x * x + y * y) < radius)
{
insideCircleList.Add(x, y);
}
else
{
outsideCircleList.Add(x, y);
}
}
LineItem redCurve = gPane.AddCurve("Inside", insideCircleList, Color.Red, SymbolType.Circle);
redCurve.Line.IsVisible = false;
redCurve.Symbol.Fill.Type = FillType.Solid;
LineItem blueCurve = gPane.AddCurve("Outside", outsideCircleList, Color.Blue, SymbolType.Circle);
blueCurve.Line.IsVisible = false;
zgControl.AxisChange();
form.ShowDialog();
Console.ReadLine();
}
}
WinForms Code:
public partial class PlotForm : Form
{
public ZedGraph.ZedGraphControl ZedGrapgControl { get; set; }
public PlotForm()
{
InitializeComponent();
ZedGrapgControl = this.zgc;
}
}
seed should not be a parameter of the random function, but a field within a random class. You set it once, and it changes with every random call.
But to answer your question, you don't save new_seed anywhere. It has to be saved so that it can be used in the next random call. So, your seed is just incremented by one in every new call, and this makes the graph a straight line.
Try using regular C# Random class (https://learn.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8), it doesn't seem worth it to "roll your own" in this case.
I think this will be good enough for your purpose.
The current implementation of the Random class is based on a modified
version of Donald E. Knuth's subtractive random number generator
algorithm.
https://learn.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8#remarks
If that doesn't meet your requirements you can look into:
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rngcryptoserviceprovider?view=netframework-4.8
how can I generate a "random constant colour" for a given string at runtime?
So a given string value will always have the same colour but different strings will have different colours.
Like how gmail assigns colours to the sender names.
Thanks
Responses to comments:
Thinking to generate the colour from a hashcode.
The colours won't be stored but generated from a hashcode.
I don't know any dedicated method for this, but here is a simple method generating Hexadecimal values with MD5 based on a given string:
using System.Security.Cryptography;
using System.Text;
static string GetColor(string raw)
{
using (MD5 md5Hash = MD5.Create())
{
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(raw));
return BitConverter.ToString(data).Replace("-", string.Empty).Substring(0, 6);
}
}
Examples:
example#example.com
-> 23463B
info#google.com
-> 3C9015
stack#exchange.com
-> 7CA5E8
Edit:
I didn't tested it enough, so you may want to tweak it a little bit to get more different and unique values.
Edit2:
If you want transparency, check out this question/answer. By setting the Substring to Substring(0,8) , you should return a string with the alpha channel.
Similar to what the other answers are suggesting (hash the string in some form then use that hash to pick the color), but instead of using the hash to directly calculate the color use it as the index to an array of "Acceptable" colors.
class ColorPicker
{
public ColorPicker(int colorCount)
{
//The ".Skip(2)" makes it skip pure white and pure black.
// If you want those two, take out the +2 and the skip.
_colors = ColorGenerator.Generate(colorCount + 2).Skip(2).ToArray();
}
private readonly Color[] _colors;
public Color StringToColor(string message)
{
int someHash = CalculateHashOfStringSomehow(message);
return _colors[someHash % _colors.Length];
}
private int CalculateHashOfStringSomehow(string message)
{
//TODO: I would not use "message.GetHashCode()" as you are not
// guaranteed the same value between runs of the program.
// Make up your own algorithom or use a existing one that has a fixed
// output for a given input, like MD5.
}
}
This prevents issues like getting a white color when you plan on showing the text with a white background and other similar problems.
To populate your Color[] see this answer for the ColorGenerator class or just make your own pre-defined list of colors that look good on whatever background they will be used on.
Appendix:
In case the link goes down, here is a copy of the ColorGenerator class
public static class ColorGenerator
{
// RYB color space
private static class RYB
{
private static readonly double[] White = { 1, 1, 1 };
private static readonly double[] Red = { 1, 0, 0 };
private static readonly double[] Yellow = { 1, 1, 0 };
private static readonly double[] Blue = { 0.163, 0.373, 0.6 };
private static readonly double[] Violet = { 0.5, 0, 0.5 };
private static readonly double[] Green = { 0, 0.66, 0.2 };
private static readonly double[] Orange = { 1, 0.5, 0 };
private static readonly double[] Black = { 0.2, 0.094, 0.0 };
public static double[] ToRgb(double r, double y, double b)
{
var rgb = new double[3];
for (int i = 0; i < 3; i++)
{
rgb[i] = White[i] * (1.0 - r) * (1.0 - b) * (1.0 - y) +
Red[i] * r * (1.0 - b) * (1.0 - y) +
Blue[i] * (1.0 - r) * b * (1.0 - y) +
Violet[i] * r * b * (1.0 - y) +
Yellow[i] * (1.0 - r) * (1.0 - b) * y +
Orange[i] * r * (1.0 - b) * y +
Green[i] * (1.0 - r) * b * y +
Black[i] * r * b * y;
}
return rgb;
}
}
private class Points : IEnumerable<double[]>
{
private readonly int pointsCount;
private double[] picked;
private int pickedCount;
private readonly List<double[]> points = new List<double[]>();
public Points(int count)
{
pointsCount = count;
}
private void Generate()
{
points.Clear();
var numBase = (int)Math.Ceiling(Math.Pow(pointsCount, 1.0 / 3.0));
var ceil = (int)Math.Pow(numBase, 3.0);
for (int i = 0; i < ceil; i++)
{
points.Add(new[]
{
Math.Floor(i/(double)(numBase*numBase))/ (numBase - 1.0),
Math.Floor((i/(double)numBase) % numBase)/ (numBase - 1.0),
Math.Floor((double)(i % numBase))/ (numBase - 1.0),
});
}
}
private double Distance(double[] p1)
{
double distance = 0;
for (int i = 0; i < 3; i++)
{
distance += Math.Pow(p1[i] - picked[i], 2.0);
}
return distance;
}
private double[] Pick()
{
if (picked == null)
{
picked = points[0];
points.RemoveAt(0);
pickedCount = 1;
return picked;
}
var d1 = Distance(points[0]);
int i1 = 0, i2 = 0;
foreach (var point in points)
{
var d2 = Distance(point);
if (d1 < d2)
{
i1 = i2;
d1 = d2;
}
i2 += 1;
}
var pick = points[i1];
points.RemoveAt(i1);
for (int i = 0; i < 3; i++)
{
picked[i] = (pickedCount * picked[i] + pick[i]) / (pickedCount + 1.0);
}
pickedCount += 1;
return pick;
}
public IEnumerator<double[]> GetEnumerator()
{
Generate();
for (int i = 0; i < pointsCount; i++)
{
yield return Pick();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static IEnumerable<Color> Generate(int numOfColors)
{
var points = new Points(numOfColors);
foreach (var point in points)
{
var rgb = RYB.ToRgb(point[0], point[1], point[2]);
yield return Color.FromArgb(
(int)Math.Floor(255 * rgb[0]),
(int)Math.Floor(255 * rgb[1]),
(int)Math.Floor(255 * rgb[2]));
}
}
}
3 integer variables, r,g and b.
Loop through each character in the string in steps of 3 and add the character code.
r += n + 0
g += n + 1
b += n + 2
after the loop take r,g, and b modulo 255 and create a color using Color.FromARGB.
No guarantees the color will be pretty though, and some strings may happen to have colors very close to each other.
I see some pretty good answeers but though it whould contribute with a little fun solutuion to generate colors from string, the Hash version looks like the best way to go but if this gives any one some inspiration to bould off, have at it
ConsoleKeyInfo ch = new ConsoleKeyInfo();
while (ch.KeyChar != 'e')
{
Console.WriteLine("type string to seed color");
string s = Console.ReadLine(); // gets text from input, in this case the command line
double d=0;
foreach(char cha in s.ToCharArray())
{
d=+ (int)cha; // get the value and adds it
}
d= (255/(Math.Pow(0.2,-0.002 *d))); // Generates a seed like value from i where 255 is the maximum. basicly 255/0.2^(-0.002*d)
int i = Convert.ToInt32(d); //then convets and get rid of the decimels
Color c = Color.FromArgb(i, i, i);// add a bit more calculation for varieng colers.
Console.WriteLine(c.Name);
Console.WriteLine("To Exit press e");
ch = Console.ReadKey()
}
Edit1: It definantly needs some tweeking, since the longer the string the ligther the color, but i think something can come from it with a little work :)
Below you can see my C# method to calculate Bollinger Bands for each point (moving average, up band, down band).
As you can see this method uses 2 for loops to calculate the moving standard deviation using the moving average. It used to contain an additional loop to calculate the moving average over the last n periods. This one I could remove by adding the new point value to total_average at the beginning of the loop and removing the i - n point value at the end of the loop.
My question now is basically: Can I remove the remaining inner loop in a similar way I managed with the moving average?
public static void AddBollingerBands(SortedList<DateTime, Dictionary<string, double>> data, int period, int factor)
{
double total_average = 0;
for (int i = 0; i < data.Count(); i++)
{
total_average += data.Values[i]["close"];
if (i >= period - 1)
{
double total_bollinger = 0;
double average = total_average / period;
for (int x = i; x > (i - period); x--)
{
total_bollinger += Math.Pow(data.Values[x]["close"] - average, 2);
}
double stdev = Math.Sqrt(total_bollinger / period);
data.Values[i]["bollinger_average"] = average;
data.Values[i]["bollinger_top"] = average + factor * stdev;
data.Values[i]["bollinger_bottom"] = average - factor * stdev;
total_average -= data.Values[i - period + 1]["close"];
}
}
}
The problem with approaches that calculate the sum of squares is that it and the square of sums can get quite large, and the calculation of their difference may introduce a very large error, so let's think of something better. For why this is needed, see the Wikipedia article on Algorithms for computing variance and John Cook on Theoretical explanation for numerical results)
First, instead of calculating the stddev let's focus on the variance. Once we have the variance, stddev is just the square root of the variance.
Suppose the data are in an array called x; rolling an n-sized window by one can be thought of as removing the value of x[0] and adding the value of x[n]. Let's denote the averages of x[0]..x[n-1] and x[1]..x[n] by µ and µ’ respectively. The difference between the variances of x[0]..x[n-1] and x[1]..x[n] is, after canceling out some terms and applying (a²-b²) = (a+b)(a-b):
Var[x[1],..,x[n]] - Var[x[0],..,x[n-1]]
= (\sum_1^n x[i]² - n µ’²)/(n-1) - (\sum_0^{n-1} x[i]² - n µ²)/(n-1)
= (x[n]² - x[0]² - n(µ’² - µ²))/(n-1)
= (x[n]-µ’ + x[0]-µ)(x[n]-x[0])/(n-1)
Therefore the variance is perturbed by something that doesn't require you to maintain the sum of squares, which is better for numerical accuracy.
You can calculate the mean and variance once in the beginning with a proper algorithm (Welford's method). After that, every time you have to replace a value in the window x[0] by another x[n] you update the average and variance like this:
new_Avg = Avg + (x[n]-x[0])/n
new_Var = Var + (x[n]-new_Avg + x[0]-Avg)(x[n] - x[0])/(n-1)
new_StdDev = sqrt(new_Var)
The answer is yes, you can. In the mid-80's I developed just such an algorithm (probably not original) in FORTRAN for a process monitoring and control application. Unfortunately, that was over 25 years ago and I do not remember the exact formulas, but the technique was an extension of the one for moving averages, with second order calculations instead of just linear ones.
After looking at your code some, I am think that I can suss out how I did it back then. Notice how your inner loop is making a Sum of Squares?:
for (int x = i; x > (i - period); x--)
{
total_bollinger += Math.Pow(data.Values[x]["close"] - average, 2);
}
in much the same way that your average must have originally had a Sum of Values? The only two differences are the order (its power 2 instead of 1) and that you are subtracting the average each value before you square it. Now that might look inseparable, but in fact they can be separated:
SUM(i=1; n){ (v[i] - k)^2 }
is
SUM(i=1..n){v[i]^2 -2*v[i]*k + k^2}
which becomes
SUM(i=1..n){v[i]^2 -2*v[i]*k} + k^2*n
which is
SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]*k} + k^2*n
which is also
SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]}*k + k^2*n
Now the first term is just a Sum of Squares, you handle that in the same way that you do the sum of Values for the average. The last term (k^2*n) is just the average squared times the period. Since you divide the result by the period anyway, you can just add the new average squared without the extra loop.
Finally, in the second term (SUM(-2*v[i]) * k), since SUM(v[i]) = total = k*n you can then change it into this:
-2 * k * k * n
or just -2*k^2*n, which is -2 times the average squared, once the period (n) is divided out again. So the final combined formula is:
SUM(i=1..n){v[i]^2} - n*k^2
or
SUM(i=1..n){values[i]^2} - period*(average^2)
(be sure to check the validity of this, since I am deriving it off the top of my head)
And incorporating into your code should look something like this:
public static void AddBollingerBands(ref SortedList<DateTime, Dictionary<string, double>> data, int period, int factor)
{
double total_average = 0;
double total_squares = 0;
for (int i = 0; i < data.Count(); i++)
{
total_average += data.Values[i]["close"];
total_squares += Math.Pow(data.Values[i]["close"], 2);
if (i >= period - 1)
{
double total_bollinger = 0;
double average = total_average / period;
double stdev = Math.Sqrt((total_squares - Math.Pow(total_average,2)/period) / period);
data.Values[i]["bollinger_average"] = average;
data.Values[i]["bollinger_top"] = average + factor * stdev;
data.Values[i]["bollinger_bottom"] = average - factor * stdev;
total_average -= data.Values[i - period + 1]["close"];
total_squares -= Math.Pow(data.Values[i - period + 1]["close"], 2);
}
}
}
I've used commons-math (and contributed to that library!) for something very similar to this. It's open-source, porting to C# should be easy as store-bought pie (have you tried making a pie from scratch!?). Check it out: http://commons.apache.org/math/api-3.1.1/index.html. They have a StandardDeviation class. Go to town!
Most important information has already been given above --- but maybe this is still of general interest.
A tiny Java library to calculate moving average and standard deviation is available here:
https://github.com/tools4j/meanvar
The implementation is based on a variant of Welford's method mentioned above. Methods to remove and replace values have been derived that can be used for moving value windows.
Disclaimer: I am the author of the said library.
I just did it with Data From Binance Future API
Hope this helps:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace Trading_Bot_1
{
public class BOLL
{
private BollingerBandData graphdata = new BollingerBandData();
private List<TickerData> data = new List<TickerData>();
public BOLL(string url)
{
string js = getJsonFromUrl(url);
//dynamic data = JObject.Parse(js);
object[][] arrays = JsonConvert.DeserializeObject<object[][]>(js);
data = new List<TickerData>();
for (int i = 1; i < 400; i++)
{
data.Add(new TickerData
{
Date = DateTime.Now,
Open = Convert.ToDouble(arrays[arrays.Length - i][1]),
High = Convert.ToDouble(arrays[arrays.Length - i][2]),
Low = Convert.ToDouble(arrays[arrays.Length - i][3]),
Close = Convert.ToDouble(arrays[arrays.Length - i][4]),
Volume = Math.Round(Convert.ToDouble(arrays[arrays.Length - i][4]), 0),
AdjClose = Convert.ToDouble(arrays[arrays.Length - i][6])
});
}
graphdata.LowerBand.Add(1);
graphdata.LowerBand.Add(2);
graphdata.LowerBand.Add(3);
graphdata.LowerBand.Add(1);
graphdata.UpperBand.Add(1);
graphdata.UpperBand.Add(2);
graphdata.UpperBand.Add(3);
graphdata.UpperBand.Add(4);
graphdata.MovingAverageWindow.Add(10);
graphdata.MovingAverageWindow.Add(20);
graphdata.MovingAverageWindow.Add(40);
graphdata.MovingAverageWindow.Add(50);
graphdata.Length.Add(10);
graphdata.Length.Add(30);
graphdata.Length.Add(50);
graphdata.Length.Add(100);
// DataContext = graphdata;
}
public static string getJsonFromUrl(string url1)
{
var uri = String.Format(url1);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
return data;
}
List<double> UpperBands = new List<double>();
List<double> LowerBands = new List<double>();
public List<List<double>> GetBOLLDATA(int decPlaces)
{
int datalength = graphdata.SelectedMovingAverage + graphdata.SelectedLength;
string bands = "";
for (int i = graphdata.SelectedLength - 1; i >= 0; i--)
{
List<double> price = new List<double>();
for (int j = 0; j < graphdata.SelectedMovingAverage; j++)
{
price.Add(data[i + j].Close);
}
double sma = CalculateAverage(price.ToArray());
double sigma = CalculateSTDV(price.ToArray());
double lower = sma - (graphdata.SelectedLowerBand * sigma);
double upper = sma + (graphdata.SelectedUpperBand * sigma);
UpperBands.Add(Math.Round( upper,decPlaces));
LowerBands.Add(Math.Round(lower, decPlaces));
bands += (Math.Round(upper, decPlaces) + " / " + Math.Round(lower, decPlaces)) + Environment.NewLine;
// graphdata.ChartData.Add(new ChartData() { SMA = sma, LowerBandData = lower, UpperBandData = upper });
}
//MessageBox.Show(bands);
return new List<List<double>> { UpperBands, LowerBands };
}
public double[] GetBOLLDATA(int decPlaces, string a)
{
List<double> price = new List<double>();
for (int j = 0; j < graphdata.SelectedMovingAverage; j++)
{
price.Add(data[j].Close);
}
double sma = CalculateAverage(price.ToArray());
double sigma = CalculateSTDV(price.ToArray());
double lower = sma - (graphdata.SelectedLowerBand * sigma);
double upper = sma + (graphdata.SelectedUpperBand * sigma);
return new double[] { Math.Round(upper, decPlaces), Math.Round(lower, decPlaces) };
}
private double CalculateAverage(double[] data)
{
int count = data.Length;
double sum = 0;
for (int i = 0; i < count; i++)
{
sum += data[i];
}
return sum / count;
}
private double CalculateVariance(double[] data)
{
int count = data.Length;
double sum = 0;
double avg = CalculateAverage(data);
for (int i = 0; i < count; i++)
{
sum += (data[i] - avg) * (data[i] - avg);
}
return sum / (count - 1);
}
private double CalculateSTDV(double[] data)
{
double var = CalculateVariance(data);
return Math.Sqrt(var);
}
}
public class ChartData
{
public double UpperBandData
{ get; set; }
public double LowerBandData
{ get; set; }
public double SMA
{ get; set; }
}
public class BollingerBandData : INotifyPropertyChanged
{
private ObservableCollection<int> _lowerBand;
private ObservableCollection<int> _upperBand;
private ObservableCollection<int> _movingAvg;
private ObservableCollection<int> _length;
private ObservableCollection<ChartData> _chartData;
private int _selectedLowerBand;
private int _selectedUpperBand;
private int _selectedMovingAvg;
private int _selectedLength;
public BollingerBandData()
{
_lowerBand = new ObservableCollection<int>();
_upperBand = new ObservableCollection<int>();
_movingAvg = new ObservableCollection<int>();
_length = new ObservableCollection<int>();
_chartData = new ObservableCollection<ChartData>();
SelectedLowerBand = 2;
SelectedUpperBand = 2;
SelectedMovingAverage = 20;
SelectedLength = 5;
}
public ObservableCollection<ChartData> ChartData
{
get
{
return _chartData;
}
set
{
_chartData = value;
RaisePropertyChanged("ChartData");
}
}
public ObservableCollection<int> LowerBand
{
get
{
return _lowerBand;
}
set
{
_lowerBand = value;
RaisePropertyChanged("LowerBand");
}
}
public ObservableCollection<int> UpperBand
{
get
{
return _upperBand;
}
set
{
_upperBand = value;
RaisePropertyChanged("UpperBand");
}
}
public ObservableCollection<int> MovingAverageWindow
{
get
{
return _movingAvg;
}
set
{
_movingAvg = value;
RaisePropertyChanged("MovingAverageWindow");
}
}
public ObservableCollection<int> Length
{
get
{
return _length;
}
set
{
_length = value;
RaisePropertyChanged("Length");
}
}
public int SelectedLowerBand
{
get
{
return _selectedLowerBand;
}
set
{
_selectedLowerBand = value;
RaisePropertyChanged("SelectedLowerBand");
}
}
public int SelectedUpperBand
{
get
{
return _selectedUpperBand;
}
set
{
_selectedUpperBand = value;
RaisePropertyChanged("SelectedUpperBand");
}
}
public int SelectedMovingAverage
{
get
{
return _selectedMovingAvg;
}
set
{
_selectedMovingAvg = value;
RaisePropertyChanged("SelectedMovingAverage");
}
}
public int SelectedLength
{
get
{
return _selectedLength;
}
set
{
_selectedLength = value;
RaisePropertyChanged("SelectedLength");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class TickerData
{
public DateTime Date
{ get; set; }
public double Open
{ get; set; }
public double High
{ get; set; }
public double Low
{ get; set; }
public double Close
{ get; set; }
public double Volume
{ get; set; }
public double AdjClose
{ get; set; }
}
}