I'd like to take a range of four random integers between 1-64, and generate a random value somewhere within the range but leaning towards a more weighted average.
The practical application is that you take a pixel, grab the 4 surrounding pixels and use those plus the current pixel to generate a value that can then be used as the base weight for a Gaussian random number generator. So you have a pixel of 10 brightness, surrounded by 8,8,9,9. Add them all up, average out to 8.8. 8.8 is then the weight for the Gaussian random number generator. So you have a random result within a range, but close to the average brightness which is 8.8, and still with some element of randomness.
The issue comes when you have wide variations because of random noise.
To give a pseudo example of how I would like it to work..
Input = [1,16,19,21]
The average of this is 14.25, but that has too much movement because of the "1" bringing the average way down. The average of this should be more around the 18 mark, because more of the numbers are clustered around that area.
I would like to see a random result coming out that is between 1 and 64, but heavily weighted between 15 and 22, with a lower possibility of it being towards the 1(Because it is still within the range as a whole) and a much lower possibility of it being over 22(Because that is completely outside of the range).
Additional The purpose of this is to generate a galactic map. I have got to the point where I have a good set of galaxy shaped data, giving me the rough density of each sector on the map. Now I need to generate specific sets of data and generate exact numbers of stars in each sector. Taking the average of the 4 surrounding sectors and using that to work out how "dense" this sector should be is the main purpose. The main thing I want to avoid is that sectors bordering an empty region of space do not also end up mostly empty, as this does not fit with general observations of galaxies.
You could imagine that the four numbers are points in a line, the x axis. Around that points there is a sphere of probability with a radius of 64, with the probability more concentrated in the proximity of the points rather than on the edges. Pick randomly one of the four points, calculate a random point inside the sphere of that number and take its x coordinate. Repeat if it is out of the range 1..64.
using System;
using System.Collections.Generic;
namespace ProbabilityDistribution1
{
class Program
{
// This derived class converts the uniformly distributed random
// numbers generated by base.Sample( ) to another distribution.
class RandomProportional : Random
{
// The Sample method generates a distribution more concentrated around the 0, in the range [0.0, 1.0].
protected override double Sample()
{
double BSample = base.Sample();
const double concentrationAroundInputs = 5;//more concentrated when greater
double result = Math.Pow(BSample, concentrationAroundInputs);
return result;
}
}
static double XCoordinateOfRandomUnitInsideSphere(Random aRandom)
{
//Even with uniform distribution the probability of exiting is greater than 0.5 on each iteration
while (true)
{
double x = aRandom.NextDouble();
double y = aRandom.NextDouble();
double z = aRandom.NextDouble();
if ((x * x + y * y + z * z) < 1) //inside the sphere
{
return x;
}
}
}
static void TestDistribution()
{
double[] Input = { 1, 16, 19, 21 };
List<int> sampleValues = new List<int>();
Random aRandom = new Random();
RandomProportional aRandomProportinal = new RandomProportional();
for (int i = 0; i < 100; i++)
{
int value = 0;
do
{
int indexChosen = aRandom.Next(4);
double xCoordinate = XCoordinateOfRandomUnitInsideSphere(aRandomProportinal);
if (aRandom.Next(2)==0)
{
xCoordinate = -xCoordinate;
}
double xRandomResult = xCoordinate * 64;
value = (int)(Input[indexChosen] + xRandomResult);
} while (value < 1 || value > 64);
sampleValues.Add((int)value);
}
sampleValues.Sort();
Console.WriteLine();
foreach (int i in sampleValues)
{
Console.Write(" {0:00} ", i);
}
Console.WriteLine();
}
static void Main(string[] args)
{
TestDistribution();
Console.WriteLine("end");
Console.ReadLine();
}
}
}
Related
So I'm making a map generator that makes random islands. It uses Perlin Noise at the heart of the generator and then a method using circles with gradients to make the islands.
The circle method creates a number of circles in the centerish of the map with a gradient from a colour starting at 64 down to 0. The issue is that this method is creating a un-natrual look at parts of the map with circular edges. When the perlin noise is generated for a pixel it will get that pixel on the gradient map and then mutliply it by the blue value.
So if the perlin noise gives a one on pixel 1, 5 and the blue value on the gradient map is 54 it will output a noise value of 54. If the perlin noise on pixel 130, 560 is 0.5 and the gradient colour value is 64 then the noise value of 32.
Here is what I am getting:
There is two key points to the code, the perlin bit:
noise = NoiseGenerator.Noise(x, y);
double gradColour = getGradColour(x, y).B;
double addedNoise = noise * gradColour;
double gradNoise = addedNoise;// - gradColour;
And then the gradient map generator:
public static void DrawGrad(float X, float Y, float R, Color C1, Color C2)
{
Graphics g = Graphics.FromImage(imgGrad);
GraphicsPath path = new GraphicsPath();
path.AddEllipse(X, Y, R, R);
PathGradientBrush pathGrBrush = new PathGradientBrush(path);
pathGrBrush.CenterColor = C1;
Color[] colours = { C2 };
pathGrBrush.SurroundColors = colours;
g.FillEllipse(pathGrBrush, X, Y, R, R);
//g.FillEllipse(Brushes.Red, X, Y, R, R);
g.Flush();
}
int amount = rnd.Next(25, 30);
for (int i = 0; i < amount / 4; i++)
{
float X = rnd.Next(-800, 1748);
float Y = rnd.Next(-800, 1748);
float R = rnd.Next(1000, 1200);
DrawGrad(X, Y, R, Color.FromArgb(255, 0, 0, rnd.Next(15, 20)), Color.FromArgb(0, 0, 0, 0));
}
for (int i = 0; i < amount; i++)
{
double positionDiv = 1.98;
double X1 = rnd.Next(0, 450) / positionDiv;
double Y1 = rnd.Next(0, 450) / positionDiv;
double R1 = rnd.Next(300, 650) / 4;
float R = (float)R1;
float X = (float)X1;
float Y = (float)Y1;
while (X + R > 1004)
{
X = 924 - R;
}
while (Y + R > 1004)
{
Y = 924 - R;
}
if(X < 30)
{
X = 30;
}
if(Y < 30)
{
Y = 30;
}
DrawGrad(X, Y, R, Color.FromArgb(255, 0, 0, rnd.Next(40, 64)), Color.FromArgb(0, 0, 0, rnd.Next(13, 17)));
}
I was just wondering if anyone else knows any other methods in C# that could create an island using perlin noise? Any advice would be greatly appreciated.
As I mentioned in the comment diamond and square is much easier with good enough results. So the algorithm:
configure generation properties
Here you need to have set of parameters like min,max elevation, sea level, elevation ranges for vegetation, sand/rock/dirt, etc, slope parameters etc.
create terrain height map I call it zed[][]
For this you need slightly modified Diamond&Square algorithm. The problem is this algorithm produces "inland" like terrain.
To adjust it so it produces island like terrains you need to initialize it with lowest possible elevation in corners. Also you need to ignore the first diamond step and initialize the mid point with some random value instead (not average of corners). And last after each square iteration correct the border points to the minimal (underwater) elevation (or some random value near it).
To achieve the good output I use approximately range <-2^15 , 2^16> while generation. After this I find min and max elevation in the generated terrain and rescale to configured elevation ranges.
Do not forget that Diamond and square need map of resolution (2^n)+1 !!!
create surface map I call it typ[][]
When terrain map is finished you can add elevation based features like these in ascending order:
watter,sand,vegetation type,mountine rocks,snow
Then add parameters based on slope of terrain
rocks
Then you can add additional things like (based on some rules):
rivers,streams,watter-falls,building,roads,...
I do it in C++ like this:
void map_random(int _xs,int _ys)
{
// config
int h0=-1000,h1=3000; // [m] terrain elevation range
int h_water= 0; // [m] sea level
int h_sand=15; // [m] sand level
int h_evergreen=1500; // [m] evergreen level
int h_snow=2000; // [m] snow level
int h_rock=1800; // [m] mountine rock level
float a_rock=60.0; // [deg] mountine rock slope
float d_pixel=15.0; // [m] pixel size
bool _island=true;
// types
enum _cover_enum
{
_cover_none=0,
_cover_water,
_cover_snow,
_covers,
_cover_shift=0,
_cover_mask=15,
};
DWORD _cover[_covers]=
{
// RRGGBB
0x00000000, // none
0x00004080, // water
0x008F8F8F, // snow
};
enum _terrain_enum
{
_terrain_enum_beg=-1,
_terrain_dirt,
_terrain_sand,
_terrain_rock,
_terrains,
_terrain_shift=4,
_terrain_mask=15,
};
DWORD _terrain[_terrains]=
{
// RRGGBB
0x00301510, // dirt
0x00EEC49A, // sand
0x00777777, // rock
};
enum _flora_enum
{
_flora_enum_beg=-1,
_flora_none,
_flora_grass,
_flora_hardwood,
_flora_evergreen,
_flora_deadwood,
_floras,
_flora_shift=8,
_flora_mask=15,
};
DWORD _flora[_floras]=
{
// RRGGBB
0x00000000, // none
0x007F7F3F, // grass
0x001FFF1F, // hardwood
0x00007F00, // evergreen
0x007F3F1F, // deadwood
};
// variables
float a,b; int c,t,f;
int x,y,z,xx,yy,mxs,mys,dx,dy,dx2,dy2,r,r2;
int **ter=NULL,**typ=NULL;
Randomize();
// align resolution to power of 2
for (mxs=1;mxs+1<_xs;mxs<<=1); if (mxs<3) mxs=3;
for (mys=1;mys+1<_ys;mys<<=1); if (mys<3) mys=3;
ter=new int*[mys+1]; for (y=0;y<=mys;y++) ter[y]=new int[mxs+1];
typ=new int*[mys+1]; for (y=0;y<=mys;y++) typ[y]=new int[mxs+1];
// [Terrain]
// diamond & square random height map -> ter[][]
dx=mxs; dx2=dx>>1; r=1<<16; // init step,half step and randomness
dy=mys; dy2=dy>>1; r2=r>>1;
// set corners values
if (_island)
{
t=-r2;
ter[ 0][ 0]=t;
ter[ 0][mxs]=t;
ter[mys][ 0]=t;
ter[mys][mxs]=t;
ter[dy2][dx2]=r2;
}
else{
ter[ 0][ 0]=Random(r);
ter[ 0][mxs]=Random(r);
ter[mys][ 0]=Random(r);
ter[mys][mxs]=Random(r);
}
for (;dx2|dy2;dx=dx2,dx2>>=1,dy=dy2,dy2>>=1) // subdivide step until full image is filled
{
if (!dx) dx=1;
if (!dy) dy=1;
// diamond (skip first one for islands)
if ((!_island)||(dx!=mxs))
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y-dy2][x-dx2]+ter[y-dy2][x+dx2]+ter[y+dy2][x-dx2]+ter[y+dy2][x+dx2])>>2)+Random(r)-r2;
// square
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx ,xx=mxs-dx ;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (y=dy ,yy=mys-dy ;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
{
y= 0; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y+dy2][x])/3)+Random(r)-r2;
y=mys; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x])/3)+Random(r)-r2;
}
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
{
x= 0; ter[y][x]=((ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
x=mxs; ter[y][x]=((ter[y][x-dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
}
// adjust border
if (_island)
{
for (y=0;y<=mys;y+=dy2) { ter[y][0]=t; ter[y][mxs]=t; }
for (x=0;x<=mxs;x+=dx2) { ter[0][x]=t; ter[mys][x]=t; }
}
// adjust randomness
// r=(r*100)>>8; if (r<2) r=2; r2=r>>1;
r>>=1; if (r<2) r=2; r2=r>>1;
}
// rescale to <h0,h1>
xx=ter[0][0]; yy=xx;
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
{
z=ter[y][x];
if (xx>z) xx=z;
if (yy<z) yy=z;
}
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
ter[y][x]=h0+(((ter[y][x]-xx)*(h1-h0))/(yy-xx));
// [Surface]
for (y=0;y<mys;y++)
for (x=0;x<mxs;x++)
{
z=ter[y][x];
// max slope [deg]
a=atan2(ter[y][x+1]-z,d_pixel);
b=atan2(ter[y+1][x]-z,d_pixel);
if (a<b) a=b; a*=180.0/M_PI;
c=_cover_none;
if (z<=h_water) c=_cover_water;
if (z>=h_snow ) c=_cover_snow;
t=_terrain_dirt;
if (z<=h_sand) t=_terrain_sand;
if (z>=h_rock) t=_terrain_rock;
if (a>=a_rock) t=_terrain_rock;
f=_flora_none;
if (t==_terrain_dirt)
{
r=Random(100);
if (r>10) f=_flora_grass;
if (r>50)
{
if (z>h_evergreen) f=_flora_evergreen;
else{
r=Random(h_evergreen);
if (r<=z) f=_flora_evergreen;
else f=_flora_hardwood;
}
}
if (r<5) f=_flora_deadwood;
}
typ[y][x]=(c<<_cover_shift)|(t<<_terrain_shift)|(f<<_flora_shift);
}
// [copy data] rewrite this part to suite your needs it just compute color based on type of terrain and height
// ter[][] is elevation in meters
// typ[][] is surface type
/*
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
pic.p[y][x].dd=(((ter[y][x]-h0)*255)/(h1-h0))*0x00010101;
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
{
r=typ[y][x];
c=(r>> _cover_shift)& _cover_mask;
t=(r>>_terrain_shift)&_terrain_mask;
f=(r>> _flora_shift)& _flora_mask;
r=_terrain[t];
if (c) r= _cover[c];
if (c==_cover_water)
{
xx=256-((ter[y][x]<<7)/h0);
yy=int(r>>16)&255; yy=(yy*xx)>>8; r=(r&0x0000FFFF)|(yy<<16);
yy=int(r>> 8)&255; yy=(yy*xx)>>8; r=(r&0x00FF00FF)|(yy<< 8);
yy=int(r )&255; yy=(yy*xx)>>8; r=(r&0x00FFFF00)|(yy );
}
if (f){ if (c) r|=_flora[f]; else r=_flora[f]; };
pic.p[y][x+_xs].dd=r;
}
*/
// free ter[][],typ[][]
for (y=0;y<=mys;y++) delete[] ter[y]; delete[] ter; ter=NULL;
for (y=0;y<=mys;y++) delete[] typ[y]; delete[] typ; typ=NULL;
}
The output with current settings is like this:
[Notes]
This approach usually produce only single big hill on the island. (Inland is generated OK) If you want more of them you can create more terrain maps and average them together.
I do following instead: I set the middle point to max height and ignore first diamond pass. After the first square pass I set the middle point back to some random value. This adds the possibility of more central hills then just one. Using this approach and adding lighting (ambient + normal shading) to preview and slight tweaking of pixel size (35m) I got this result:
On rare occasion this can generate inland like map (if the central area is too small. To handle it you can scan corners for watter. if there is land generate again or add some bias for central points randomness in first pass.
You can play with the code for example add rivers:
find topest hill
get random location close/around it
set it to river type
find smallest height neighbor pixel not set to river type
if it is on edge of map or set to sea/water type stop otherwise loop #3
If you want more then one rivers then do not forget to use some temp type for already done rivers so the algorithm can work properly. You can also increase the river volume with distance from start... Here is the result:
After this you should also equalize the formed lakes water level.
I'm stuck on one final piece of a calculation puzzle below. I know how to generate a percentage score of correct parts from total correct possible parts ((correctNumPartsOnBoard / totalPossibleCorrectParts)*100) but I want to the final percentage score to factor in the number the incorrect parts on the board as well. (even if all the right parts are on the board you still won't get 100% if there are also incorrect parts). Right now my current formula percentCorrectParts = ((correctNumPartsOnBoard / totalPossibleCorrectParts) / totalNumPartsOnBoard) * 100); is wrong and I'm having trouble pinpointing the correct calculation.
So, the way the calc would need to work is: a user needs to match one of the six possible animals, each animal has around 15 correct parts, but users can also drag incorrect parts onto the board (parts from the other animals are still visible so they could drag a different set of legs or horns on a lizard head, they could make frankenstein type creatures as well this way). So the total number of parts available would be 6*15. But seeing as how they're not all correct they would influence the score as well by bringing the overall score average of pieces on the board down.
What's the correct formula for this?
// Scoring System
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
public class ScoreManager : MonoBehaviour
{
public List<string> totalBuildBoardParts; // Running list of all parts on board (by Tag)
public int numCorrectPartsOnBoard;
public int numIncorrectPartsOnBoard;
public int totalPossibleCorrectParts;
public float percentCorrectParts;
void Start()
{
GameObject gameController = GameObject.FindGameObjectWithTag("gc");
GameSetup gameSetup = gameController.GetComponent<GameSetup>();
totalPossibleCorrectParts = gameSetup.totalPossibleCorrectParts;
Debug.Log("TOTAL POSSIBLE CORRECT PARTS ARE: " + totalPossibleCorrectParts);
}
public void AddAnimalPartByTag(string tag)
{
// Add object tag to List
totalBuildBoardParts.Add(tag);
Debug.Log ("Added an object tagged as: " + tag);
GameObject gameController = GameObject.FindGameObjectWithTag("gc");
GameSetup gameSetup = gameController.GetComponent<GameSetup>();
if (tag == gameSetup.activeTag)
{
numCorrectPartsOnBoard ++;
Debug.Log ("There are " + numCorrectPartsOnBoard + " correct parts on the board");
} else {
numIncorrectPartsOnBoard ++;
}
CalculateScore();
}
public void RemoveAnimalPartByTag(string tag)
{
// Add object tag to List
totalBuildBoardParts.Remove(tag);
Debug.Log ("Removed an object tagged as: " + tag);
GameObject gameController = GameObject.FindGameObjectWithTag("gc");
GameSetup gameSetup = gameController.GetComponent<GameSetup>();
if (tag == gameSetup.activeTag)
{
numCorrectPartsOnBoard --;
Debug.Log ("There are " + numCorrectPartsOnBoard + " correct parts on the board");
} else {
numIncorrectPartsOnBoard --;
}
CalculateScore();
}
public void CalculateScore()
{
float totalNumPartsOnBoard = totalBuildBoardParts.Count();
float correctNumPartsOnBoard = numCorrectPartsOnBoard;
percentCorrectParts = ((correctNumPartsOnBoard / totalPossibleCorrectParts) / totalNumPartsOnBoard) * 100);
Debug.Log ("Your current score is: " + percentCorrectParts);
}
}
Your formula is probably correct. However, your datatypes are not.
You are currently doing an integer division, which results in an int too. So let's say that correctNumPartsOnBoard is 3 and totalPossibleCorrectParts is 5, 3/5 gives 0 because an int does not have any decimals.
You need to cast one of the two operands in the division as a datatype with decimals ( float, double or decimal for example):
percentCorrectParts = ((correctNumPartsOnBoard / (float)totalPossibleCorrectParts) / totalNumPartsOnBoard) * 100);
By setting denominator totalPossibleCorrectParts as a float, the first division will return a float. That float is then used in the second division, also returning correctly a float.
I think your formula should look like this:
int correctParts;
int possibleCorrect;
int incorrectParts;
int parts;
float percentFinished =
Mathf.Max((((float)correctParts/possibleCorrect) // Correct percent
- ((float)incorrectParts/parts)) // Minus incorrect percent
* 100f, // Normalized to 100
0f); // Always a minimum of 0
Also with this formula unlike other answers, you don't have to use all of the parts to get 100%, just get the total possible correct parts which doesn't necessarily have to use up all of your parts ;)
Scenario
Lets say you have 100 parts, with 3 right and 3 wrong. Total right we are aiming for here is 20.
int correctParts = 3;
int possibleCorrect = 20;
int incorrectParts = 3;
int parts = 100;
float percentFinished =
Mathf.Max((((float)correctParts/possibleCorrect) // Correct percent is 0.15 or 15%
- ((float)incorrectParts/parts)) // Minus incorrect percent which is .03 or 3%
* 100f, // Normalized to 100 which gives us 15% - 3% = 12%
0f); // Always a minimum of 0
I think your final (%age) score should be:
correctNumPartsOnBoard / totalNumPartsOnBoard * 100
If you have 80 correctparts and 20 incorrect then the total parts is 100 and you've got 80 of them correct so you should score 80% like this:
80 / (80+20) * 100
I am drawing on canvas based on device movement, I want to draw different characters in canvas based on mobile movement.
Currently its working, but I want to find time difference and i want to detect pause, pause means when user is not trying to draw and user is not moving mobile phone, so that Application ca assume that now user want to draw next character.
How to find pause in accelerometer values. Any logic? Also tell me how i can smooth accelerometer values, so that user can draw lines without noise.
I cannot help with the accelerator part, but for the noise in the data, here is one approach using Weighted Moving Average.
The basics are simple:
Find out how many points before current you want to use for smoothing
Calculate a weight based on length, f.ex. if length is 5 then the weight = 1+2+3+4+5 = 15
Iterate each data point starting from length of weight (you can start at 1 and cut the weighting short - below I'll demo the latter approach)
For point current - 5 multiply with 1/15, for current - 4 multiply with 2/15 and so forth. The sum is stored as value for this point, repeat for the next value points
Live demo
Below is a demo (enter full page to see all graphics). I wrote it in JavaScript so it could be shown live here in the answer. I think you should have little problem converting it into the language you're using (which is not stated).
Move the slider to increase number of points to weight. You can run the data through several passes to smooth even more. The original data is a sinus curve with noise jitter. With many points you can see the curve smooths to replicate this. Just using 9-10 points length over 2 passes will give a good result with very little time delay:
var ctx = document.querySelector("canvas").getContext("2d"),
rng = document.querySelector("input"),
val = document.querySelector("span"),
data = [], scale = 30;
// generate sinus wave with noise jitters
for(var i = 0; i < ctx.canvas.width; i += 2)
data.push(Math.sin(i*0.1) * Math.random() + Math.random())
// draw initial smoothed curve (length=1, no smoothing)
drawWMA();
// calculate moving average
function drawWMA() {
var len = +rng.value, // get smoothing length (number of previous points)
dataa = [], datab = [], // pass A and B arrays
weight = 0; // calc weight based on length
val.innerHTML = len;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.beginPath();
// calc weight
for(var i = 1; i <= len; i++) weight += i; // add range together [1, length]
// plot original data at top of canvas
plot(data, 30);
// PASS 1: Calc new smoothed array
dataa = calcWMA(data, len, weight);
// plot smoothed curve
ctx.fillText("FIRST PASS:", 0, 100);
plot(dataa, 120);
// PASS 2 (optional)
datab = calcWMA(dataa, len, weight);
ctx.fillText("SECOND PASS:", 0, 190);
plot(datab, 210);
ctx.stroke(); // render plots
}
function calcWMA(data, len, weight) {
var i, t, datao = [];
// calc new smoothed array
for(i = 0; i < data.length; i++) { // iterate from length to end of data
var v = 0; // calc average value for this position
for(t = 0; t < len; t++) { // [1, len]
if (i-t >= 0)
v += data[i-t] * ((t+1) / weight); // weight previous values based on -delta
}
datao.push(v); // store new value
}
return datao
}
function plot(data, y) {
ctx.moveTo(0, y + data[0]*scale);
for(i = 1; i < data.length; i++) ctx.lineTo(i * 2, y + data[i]*scale);
}
rng.onchange = rng.oninput = drawWMA;
<label>Points to consider: <input type="range" min=1 max=50 value=1></label><span>1</span><br>
<canvas width=600 height=300></canvas>
A different approach would be to use a Savitzky–Golay filter which gives a similar result, but not "sacrifice" any points at the end (moving average will push forward or crop at the end).
I'm graphing some statistics which can be percentages, currency values or plain numbers.
I need to set the maximum value of the graph control's axis to a nice, round number just a bit above the maximum value in the data set. (The graph control's default value is not what I want).
Two things to note:
The value I set for the axis maximum should be minimum 5% above the dataset's maximum value (the less above this the better).
I have 4 horizontal lines above the 0 Y-axis; so ideally the Y-axis maximum should divide nicely by 4.
Sample data might be:
200%, 100%, 100%, 100%, 75%, 50%, 9%
In this case, 220% would be acceptable as the maximum value.
$3500161, $1825223, $1671232, $110112
In this case, $3680000 might be ok. Or $3700000 I suppose.
Can anyone suggest a nice formula for doing this? I might need to adjust settings, like the 5% margin might be changed to 10%, or I might need to change the 4 horizontal lines to 5.
Here is the code I use to create graph axes.
/// <summary>
/// Axis scales a min/max value appropriately for the purpose of graphs
/// <remarks>Code taken and modified from http://peltiertech.com/WordPress/calculate-nice-axis-scales-in-excel-vba/</remarks>
/// </summary>
public struct Axis
{
public readonly float min_value;
public readonly float max_value;
public readonly float major_step;
public readonly float minor_step;
public readonly int major_count;
public readonly int minor_count;
/// <summary>
/// Initialize Axis from range of values.
/// </summary>
/// <param name="x_min">Low end of range to be included</param>
/// <param name="x_max">High end of range to be included</param>
public Axis(float x_min, float x_max)
{
//Check if the max and min are the same
if(x_min==x_max)
{
x_max*=1.01f;
x_min/=1.01f;
}
//Check if dMax is bigger than dMin - swap them if not
if(x_max<x_min)
{
float temp = x_min;
x_min = x_max;
x_max = temp;
}
//Make dMax a little bigger and dMin a little smaller (by 1% of their difference)
float delta=(x_max-x_min)/2;
float x_mid=(x_max+x_min)/2;
x_max=x_mid+1.01f*delta;
x_min=x_mid-1.01f*delta;
//What if they are both 0?
if(x_max==0&&x_min==0)
{
x_max=1;
}
//This bit rounds the maximum and minimum values to reasonable values
//to chart. If not done, the axis numbers will look very silly
//Find the range of values covered
double pwr=Math.Log(x_max-x_min)/Math.Log(10);
double scl=Math.Pow(10, pwr-Math.Floor(pwr));
//Find the scaling factor
if(scl>0&&scl<=2.5)
{
major_step=0.2f;
minor_step=0.05f;
}
else if(scl>2.5&&scl<5)
{
major_step=0.5f;
minor_step=0.1f;
}
else if(scl>5&&scl<7.5)
{
major_step=1f;
minor_step=0.2f;
}
else
{
major_step=2f;
minor_step=0.5f;
}
this.major_step=(float)(Math.Pow(10, Math.Floor(pwr))*major_step);
this.minor_step=(float)(Math.Pow(10, Math.Floor(pwr))*minor_step);
this.major_count=(int)Math.Ceiling((x_max-x_min)/major_step);
this.minor_count=(int)Math.Ceiling((x_max-x_min)/minor_step);
int i_1=(int)Math.Floor(x_min/major_step);
int i_2=(int)Math.Ceiling(x_max/major_step);
this.min_value=i_1*major_step;
this.max_value=i_2*major_step;
}
public float[] MajorRange
{
get
{
float[] res=new float[major_count+1];
for(int i=0; i<res.Length; i++)
{
res[i]=min_value+major_step*i;
}
return res;
}
}
public float[] MinorRange
{
get
{
float[] res=new float[minor_count+1];
for(int i=0; i<res.Length; i++)
{
res[i]=min_value+minor_step*i;
}
return res;
}
}
}
You can the nice max_value and min_value as calculated from the initialized for Axis given the mathematical min. max. values in x_min and x_max.
Example:
new Axis(0,3500161) calculates max_value = 4000000.0
new Axis(0,1825223) calculates max_value = 2000000.0
new Axis(0,1671232) calculates max_value = 1800000.0
new Axis(0, 110112) calculates max_value = 120000.0
For your 1st query use:
DataView data = new DataView(dt);
string strTarget = dt.Compute("MAX(target)", string.Empty).ToString();// target is your column name.
int tTarget = int.Parse(strTarget.Equals("") ? "0" : strTarget); // Just in case if your string is empty.
myChart.ChartAreas[0].AxisY.Maximum = myChart.ChartAreas[0].AxisY2.Maximum = Math.Ceiling(tTarget * 1.1); // This will give a 10% plus to max value.
For the 2nd point, i guess you can figure this out with minor/major axis interlaced and offset properties.
First, you'll need to decide on a range for (top of graph)/(max data point). You have this bounded on the lower end as 1.05; reasonable upper bounds might be 1.1 or 1.15. The wider the range, the more empty space may appear at the top of the graph, but the "nicer" the numbers may be. Alternatively, you can pick a "niceness" criterion first and then pick the smallest sufficiently nice number where the above ratio is at least 1.05.
You can also improve the "niceness" of the intervals by loosening that lower bound, for instance lowering it to 1.02 or even 1.0.
EDIT: In response to comment.
What you'll have to do to find a good max size is take your max value plus margin, divide it by the number of intervals, round it upward to the nearest "nice" value, and multiply by the number of intervals. A reasonable definition of "nice" might be "multiple of 10^(floor(log_10(max value)) - 2)" A looser definition of niceness will give you (on average) less extra margin at the top.
I have an angle say 60deg and want to generate random angle within interval say [-120,120] where the interval centred around the 60deg which be now [-60,180]
I have this code below:
http://www.cs.princeton.edu/introcs/22library/StdRandom.java.html
I'm confused because it's say that the gaussian distribution is within [0,1].
How could I pass the range [-120,120]?
The 60 angle is the relative rotation of an object the generated random angle is a predication of it's next postion
When testing the code I have angles ,say 65 ,55 if i use this angle directly it performs stranges so I take the difference 65-60 ,55-60.
Is this idea correct?
If you have a random number with a range 0 to 1, you can convert it to -120 to 120 by using:
rand_num*240 - 120
More generally, transforming any number within range [A,B] to range [C,D] involves:
num * (D-C)/(B-A) + C
I'm not sure what you mean by keeping your mean, however.
If you want a range that extends 120 in each direction, from 60, you could either do the above and add 60, or use a range [60-120,60+120] = [-60,180]
In that sense, you'd have
rand_num * 240 - 60
following from the formula given above
static void Main(string[] args)
{
Random rand = new Random();
double a = 0;
for (int i = 0; i < 1000; i++)
{
double r = rand.NextDouble() * 240 - 60;
a += r;
Console.WriteLine(string.Format("ang:{0,6:0.0} avg:{1,5:0.0}", r, a / (i + 1)));
}
Console.ReadKey();
}
If you have something that generates random numbers in a range such as [0, 1] it's easy to transform that to another range, such as [-120, 120]: you just have to multiply by the size of the target range (240 in this case) and add the start of the target range (-120 in this case).
So, for example:
java.util.Random random = new java.util.Random();
// Generate a random number in the range [-120, 120]
double value = random.nextDouble() * 240.0 - 120.0;
Is there a special reason why you are using that StdRandom class? Does the distribution of the random numbers have to be Gaussian? (That doesn't matter, the above will still work).
If it has to be centered around 60, then just add 60.
Try this:
import java.lang.Math;
public static void main(String[] args)
{
System.out.println((int)(Math.random()*(-240))+120);
}
You have C# and Java marked as tags. Kind of confusing to figure out which one you want.
I prefer this over the Random class in java.utils because you don't have to instantiate a class. Everything you need is in the static methods of the Math class.
Breakdown:
return Math.random(); // returns a double value [0, 1]
return Math.random()*-240; // returns a double value from [-240, 0]
return (int)(Math.random()*-240); // returns an integer value from [-240, 0]
return (int)(Math.random()*-240) + 120; // returns an integer value from [-120, 120]