C# How to generate random high or low numbers, and have each succeeding number be based off of the last randomly generated number - random

I'm currently creating a Guess Game via a Udemy course based on C# using Unity Engine. What I'm trying to do is outside the scope of the course.
I got to the point you see below where when I press the "up arrow" or "down arrow" a random number is generated between two numbers: (avg, max) and (avg, min).
As it stand right now the numbers generated are always higher or lower than the 'avg' based on the key that's pressed, but the each number generated after that won't necessarily be higher or lower than the previously generated number.
For example. I could choose lower than 393 (the 'avg') and get 242. But if I press lower again it could be 345. While it's lower than the 'avg' I want to have each randomly generated number be lower than the last randomly generated number (or higher depending on the key press).
I thought maybe assigning a variable to the function was the right thing to do. I have an unused integer 'c' that I was thinking of using.
Not sure how to make this happen....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NumberWizard : MonoBehaviour
{
int max = 734;
int min = 52;
int avg = 393;
int a;
int b;
int c;
// Use this for initialization
void Start()
{
StartGame();
}
void StartGame()
{
max = 734;
min = 52;
avg = 393;
Debug.Log("Welcome to Number Wizard you filthy son of a bitch.");
Debug.Log("Pick a goddamn number.");
Debug.Log("The highest number is: " + max);
Debug.Log("The lowest number is: " + min);
Debug.Log("Tell me if your number is higher or lower than " + avg);
Debug.Log("Push Up = Higher, Push Down = Lower, Push Enter = Correct!");
max = max + 1;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
Debug.Log("You chose higher. What is your guess? ");
NextGuess();
}
else if (Input.GetKeyDown(KeyCode.DownArrow))
{
Debug.Log("You chose lower. What is your guess?");
NextGuess2();
}
else if (Input.GetKeyDown(KeyCode.Return))
{
Debug.Log("You are correct.");
StartGame();
}
else if (Input.GetKeyDown(KeyCode.KeypadEnter))
{
Debug.Log("You are correct.");
StartGame();
}
else if (Input.anyKeyDown)
{
Debug.Log("Input invalid. Try again.");
}
}
void NextGuess()
{
a = Random.Range(avg, max);
Debug.Log(a);
}
void NextGuess2()
{
b = Random.Range(avg, min);
Debug.Log(b);
}
}

Try updating the avg on a key down to waht the value is. So with your example
choose lower than 393 (the 'avg') and get 242
now update avg to be 242.
avg = NextGuess2();
public NextGuess2()
{
b = Random.Range(avg, min);
Debug.Log(b);
return b;
}

Related

Gin Rummy - Algorithm for determining optimal melding

A similar question to this has been asked here, but in my question, rather than being restricted to melds of size 3, melds can be any size.
In Gin Rummy, for any particular set of cards, cards can be grouped into either sets or runs. A set is a group of 3 or more cards that are all of the same rank (2-D, 2-C, 2-H or 7-D, 7-C, 7-H, 7-S). A run is a group of 3 or more cards with consecutive ranks and identical suits (A-D, 2-D, 3-C or 7-C, 8-C, 9-C, 10-C, J-C). Cards not belonging to a group are called "deadwood".
The goal of my algorithm is to find the optimal melding for a particular set of cards, which is one that minimizes the sum of the values of all the deadwood (The values of number cards are their associated numbers, the value of the ace is 1, and the value of face cards is 10.).
My original attempt at an algorithm worked on the assumption that for any run and group of sets that conflicted, either the run would exist or the group of sets would exist. Under this assumption, the algorithm could just calculate the sum of the values of the run and the sum of the values of all the sets, and keep whichever was greater. For example, if we had the groups
[2-D, 3-D, 4-D], [2-D, 2-C, 2-H], and [4-D, 4-H, 4-S]. The sum of the run's value would be 2 + 3 + 4 = 9, and the sum of the all the set's values would be 2 + 2 + 2 + 4 + 4 + 4 = 18. In this case, this would mean the two sets would be kept for the optimal melding and the run would not be used (3-D would be deadwood).
This assumption worked for groups of size 3, but fails with larger groups. For example, consider the following two groups:
[4-D, 5-D, 6-D, 7-D], [7-D, 7-H, 7-S]
The optimal grouping for this ends up being [4-D, 5-D, 6-D] and [7-D, 7-H, 7-S]. The conflicting set and part of the run is kept. I'm not sure how to create an algorithm, that isn't just brute force.
Any help or ideas would be appreciated.
EDIT
I'm realizing that my original algorithm doesn't even work for size 3 melds. In the case of the following groups:
[4-D, 5-D, 6-D], [4-C, 5-C, 6-C], [6-D, 6-C, 6-S]
The algorithm would look at the two runs individually, and conclude that they should be removed in favor of the set, but the optimal solution would be to keep both runs and remove the set.
Still looking for help in creating an algorithm that works in all edge cases.
My previous answer got deleted as I didn't really provide an explanation, and simply provided a link to a script with an algorithm for this problem. I realize why that isn't fit for an answer on stack overflow now. Here's my attempt at a complete answer.
The algorithm I show here is based on the approach found here: https://gist.github.com/yalue/2622575. The solution is a backtracking search as Paul Hankin suggested above.
The algorithm creates an object called MeldNode:
class MeldNode {
Cards cards;
MeldNode* parent;
double value;
MeldNode(Cards cards, MeldNode* parent) : cards(cards), parent(parent) {
value = sum of values of cards;
if (parent is not null){
value += parent->value;
}
}
}
Here value is equal to the sum of the card values of the cards provided and the value of the parent.
A function cleanMeldGroup is created which, given an array of melds and a meld, will return an array of melds with only melds that don't conflict with the given meld.
Melds cleanMeldGroup(Melds melds, Cards meldAvoid) {
Melds cleanMelds;
for (Cards meld : melds) {
bool clean = true;
for (Card cardA : meld) {
for (Card cardB : meldAvoid) {
if (cardA == cardB) {
clean = false;
}
}
}
if (clean) {
cleanMelds.push(meld);
}
}
return cleanMelds;
}
Next, a function getBestNode is created, which uses backtracking to find a meld node containing the data for the optimal melding combination.
MeldNode* getBestNode(Melds melds, MeldNode* rootNode) {
MeldNode* best = rootNode;
for (Meld meld : melds) {
MeldNode* node = new MeldNode(meld, rootNode);
MeldNode* newTree = getBestNode(cleanMeldGroup(melds, meld), node);
if (best is null || newTree->value > best->value){
best = newTree;
}
}
}
Note that as this is written now, this would result in memory leaks in c++. If necessary, take measures to free memory when the data has been used (You could free the memory of Nodes that aren't part of the best tree in this function, and then free the memory of Nodes that are part of the best tree after you use them).
Finally, the optimal melding can be determined as follows using the getOptimalMelding function.
Melds getOptimalMelding(Cards hand) {
Sort hand by rank and then by suit;
Melds possibleMelds;
// Find all possible runs
int runLength = 1;
for (int i = 0; i < hand.size(); i++) {
if (hand[i].suit == hand[i - 1].suit && hand[i].rank == hand[i - 1].rank + 1) {
runLength++;
} else {
if (runLength >= 3) {
for (int size = 3; size <= runLength; size++) {
for (int start = 0; start <= runLength - size; start++) {
Cards run;
for (int j = i - runLength + start; j < i - runLength + start + s; j++) {
run.push(hand[j]);
}
possibleMelds.push(run);
}
}
}
runLength = 1;
}
}
if (runLength >= 3) {
for (int size = 3; size <= runLength; size++) {
for (int start = 0; start <= runLength - size; start++) {
Cards run;
for (int j = i - runLength + start; j < i - runLength + start + s; j++) {
run.push(hand[j]);
}
possibleMelds.push(run);
}
}
}
// Find all possible sets
for (int i = 1; i <= 13; i++) {
Cards set;
for (Card card : hand) {
if (card.rank == i) {
set.push(card);
}
}
if (set.size() >= 3) {
possibleMelds.push(set);
}
if (set.size() == 4) {
for (Card card : set) {
Cards subset;
for (Card add : set) {
if (add != card) {
subset.push(add);
}
}
possibleMelds.push(subset);
}
}
}
// Find Optimal Melding Combination
MeldNode* bestNode = getBestNode(possibleMelds, null);
Melds optimalMelds;
while (bestNode is not null){
optimalMelds.push(bestNode.cards);
bestNode = bestNode->parent;
}
return optimalMelds;
Note that possibleMelds contains all possible melds of all sizes. For example, for the hand [2-D, 3-D, 4-D, 5-D, 5-H, 5-S, 5-C, 10-S, 9-C, 8-H], possibleMelds would contain the following groups:
[2-D, 3-D, 4-D],
[3-D, 4-D, 5-D],
[2-D, 3-D, 4-D, 5-D],
[5-D, 5-H, 5-S, 5-C],
[5-H, 5-S, 5-C],
[5-D, 5-S, 5-C],
[5-D, 5-H, 5-C],
[5-D, 5-H, 5-S]

Special numbers challenge in programming

First, sorry for my bad English.
Special numbers are numbers that the sum of the digits is divisible to the number of the digit.
Example: 135 is a special number because the sum of the digits is 1+3+5 = 9, the number of the digit is 3, and 9 is divisible to 3 because 9 % 3 == 0. 2,3,9,13,17,15,225, 14825 are also special numbers.
Requirement:
Write a program that read the number n (n <= 10^6) from a file named SNUMS.INP (SNUMS.INP can contain up to 10^6 numbers) and print the result out into the file SNUMS.OUT. Number n is the order of the special number and the result will be that special number in n order (sorry I don't know how to express it).
Example: n = 3 means you have to print out the 3rd special number which is 3, n = 10 you have to print out 10th special number which is 11, n = 13 you have to print out 13th special number which is 17, n = 15 you have to print out 15th special number which is 20.
The example bellow will demonstrate the file SNUMS.INP and SNUMS.OUT (Remember: SNUMS.INP can contain up to 10^6 numbers)
SNUMS.INP:
2
14
17
22
SNUMS.OUT:
2
19
24
35
I have my own alogrithm but the the running time exceeds 1 second (my SNUMS.INP has 10^6 numbers). So I need the optimal alogrithm so that the running time will be less than or equal 1s.
Guys I decide to post my own code which is written in Java, it always take more than 4 seconds to run. Could you guys please suggest some ideas to improve or how to make it run faster
import java.util.Scanner;
import java.io.*;
public class Test
{
public static void main(String[]args) throws IOException
{
File file = new File("SNUMS.INP");
Scanner inputFile = new Scanner(file);
int order = 1;
int i = 1;
int[] special = new int[1000000+1];
// Write all 10^6 special numbers into an array named "special"
while (order <= 1000000)
{
if (specialNumber(i) == true)
{
special[order] = i;
order++;
}
i++;
}
// Write the result to file
PrintWriter outputFile = new PrintWriter("SNUMS.OUT");
outputFile.println(special[inputFile.nextInt()]);
while (inputFile.hasNext())
outputFile.println(special[inputFile.nextInt()]);
outputFile.close();
}
public static boolean specialNumber(int i)
{
// This method check whether the number is a special number
boolean specialNumber = false;
byte count=0;
long sum=0;
while (i != 0)
{
sum = sum + (i % 10);
count++;
i = i / 10;
}
if (sum % count == 0) return true;
else return false;
}
}
This is file SNUMS.INP (sample) contains 10^6 numbers if you guys want to test.
https://drive.google.com/file/d/0BwOJpa2dAZlUNkE3YmMwZmlBOTg/view?usp=sharing
I've managed to solve it in 0.6 seconds on C# 6.0 (.Net 4.6 IA-64) at Core i7 3.2 GHz with HDD 7200 rpc; hope that precompution will be fast enough at your workstation:
// Precompute beautiful numbers
private static int[] BeautifulNumbers(int length) {
int[] result = new int[length];
int index = 0;
for (int i = 1; ; ++i) {
int sum = 0;
int count = 0;
for (int v = i; v > 0; sum += v % 10, ++count, v /= 10)
;
if (sum % count == 0) {
result[index] = i;
if (++index >= result.Length)
return result;
}
}
}
...
// Test file with 1e6 items
File.WriteAllLines(#"D:\SNUMS.INP", Enumerable
.Range(1, 1000000)
.Select(index => index.ToString()));
...
Stopwatch sw = new Stopwatch();
sw.Start();
// Precomputed numbers (about 0.3 seconds to be created)
int[] data = BeautifulNumbers(1000000);
// File (about 0.3 seconds for both reading and writing)
var result = File
.ReadLines(#"D:\SNUMS.INP")
.Select(line => data[int.Parse(line) - 1].ToString());
File.WriteAllLines(#"D:\SNUMS.OUT", result);
sw.Stop();
Console.Write("Elapsed time {0}", sw.ElapsedMilliseconds);
The output vary from
Elapsed time 516
to
Elapsed time 660
with average elapsed time at about 580 milliseconds
Now that you have the metaphor of abacus implemented below, here are some hints
instead of just incrementing with 1 inside a cycle, can we incremente more aggressively? Indeed we can, but with an extra bit of care.
first, how much aggressive we can be? Looking to 11 (first special with 2 digits), it doesn't pay to just increment by 1, we can increment it by 2. Looking to 102 (special with 3 digits), we can increment it by 3. Is it natural to think we should use increments equal with the number of digits?
now the "extra bit of care" - whenever the "increment by the number of digits" causes a "carry", the naive increment breaks. Because the carry will add 1 to the sum of digits, so that we may need to subtract that one from something to keep the sum of digits well behaved.
one of the issues in the above is that we jumped quite happily at "first special with N digits", but the computer is not us to see it at a glance. Fortunately, the "first special with N digits" is easy to compute: it is 10^(N-1)+(N-1) - 10^(N-1) brings an 1 and the rest is zero, and N-1 brings the rest to make the sum of digits be the first divisible with N. Of course, this will break down if N > 10, but fortunately the problem is limited to 10^6 special numbers, which will require at most 7 digits (the millionth specual number is 6806035 - 7 digits);
so, we can detect the "first special number with N digits" and we know we should try with care to increment it by N. Can we look now better into that "extra care"?.
The code - twice as speedy as the previous one and totally "orthodox" in obtaining the data (via getters instead of direct access to data members).
Feel free to inline:
import java.util.ArrayList;
import java.util.Arrays;
public class Abacus {
static protected int pow10[]=
{1,10,100,1000, 10000, 100000, 1000000, 10000000, 100000000}
;
// the value stored for line[i] corresponds to digit[i]*pow10[i]
protected int lineValues[];
protected int sumDigits;
protected int representedNumber;
public Abacus() {
this.lineValues=new int[0];
this.sumDigits=0;
this.representedNumber=0;
}
public int getLineValue(int line) {
return this.lineValues[line];
}
public void clearUnitLine() {
this.sumDigits-=this.lineValues[0];
this.representedNumber-=this.lineValues[0];
this.lineValues[0]=0;
}
// This is how you operate the abacus in real life being asked
// to add a number of units to the line presenting powers of 10
public boolean addWithCarry(int units, int line) {
if(line-1==pow10.length) {
// don't have enough pow10 stored
pow10=Arrays.copyOf(pow10, pow10.length+1);
pow10[line]=pow10[line-1]*10;
}
if(line>=this.lineValues.length) {
// don't have enough lines for the carry
this.lineValues=Arrays.copyOf(this.lineValues, line+1);
}
int digitOnTheLine=this.lineValues[line]/pow10[line];
int carryOnTheNextLine=0;
while(digitOnTheLine+units>=10) {
carryOnTheNextLine++;
units-=10;
}
if(carryOnTheNextLine>0) {
// we have a carry, the sumDigits will be affected
// 1. the next two statememts are equiv with "set a value of zero on the line"
this.sumDigits-=digitOnTheLine;
this.representedNumber-=this.lineValues[line];
// this is the new value of the digit to set on the line
digitOnTheLine+=units;
// 3. set that value and keep all the values synchronized
this.sumDigits+=digitOnTheLine;
this.lineValues[line]=digitOnTheLine*pow10[line];
this.representedNumber+=this.lineValues[line];
// 4. as we had a carry, the next line will be affected as well.
this.addWithCarry(carryOnTheNextLine, line+1);
}
else { // we an simply add the provided value without carry
int delta=units*pow10[line];
this.lineValues[line]+=delta;
this.representedNumber+=delta;
this.sumDigits+=units;
}
return carryOnTheNextLine>0;
}
public int getSumDigits() {
return this.sumDigits;
}
public int getRepresentedNumber() {
return this.representedNumber;
}
public int getLinesCount() {
return this.lineValues.length;
}
static public ArrayList<Integer> specials(int N) {
ArrayList<Integer> ret=new ArrayList<>(N);
Abacus abacus=new Abacus();
ret.add(1);
abacus.addWithCarry(1, 0); // to have something to add to
int increment=abacus.getLinesCount();
while(ret.size()<N) {
boolean hadCarry=abacus.addWithCarry(increment, 0);
if(hadCarry) {
// need to resynch the sum for a perfect number
int newIncrement=abacus.getLinesCount();
abacus.clearUnitLine();
if(newIncrement!=increment) {
// we switched powers of 10
abacus.addWithCarry(newIncrement-1, 0);
increment=newIncrement;
}
else { // simple carry
int digitsSum=abacus.getSumDigits();
// how much we should add to the last digit to make the sumDigits
// divisible again with the increment?
int units=increment-digitsSum % increment;
if(units<increment) {
abacus.addWithCarry(units, 0);
}
}
}
ret.add(abacus.getRepresentedNumber());
}
return ret;
}
// to understand how the addWithCarry works, try the following code
static void add13To90() {
Abacus abacus; // starts with a represented number of 0
// line==1 means units of 10^1
abacus.addWithCary(9, 1); // so this should make the abacus store 90
System.out.println(abacus.getRepresentedNumber());
// line==0 means units of 10^0
abacus.addWithCarry(13, 0);
System.out.println(abacus.getRepresentedNumber()); // 103
}
static public void main(String[] args) {
int count=1000000;
long t1=System.nanoTime();
ArrayList<Integer> s1=Abacus.specials(count);
long t2=System.nanoTime();
System.out.println("t:"+(t2-t1));
}
}
Constructing the numbers from their digits is bound to be faster.
Remember the abacus? Ever used one?
import java.util.ArrayList;
public class Specials {
static public ArrayList<Integer> computeNSpecials(int N) {
ArrayList<Integer> specials = new ArrayList<>();
int abacus[] = new int[0]; // at index i we have the digit for 10^i
// This way, when we don't have enough specials,
// we simply reallocate the array and continue
while (specials.size() < N) {
// see if a carry operation is necessary
int currDigit = 0;
for (; currDigit < abacus.length && abacus[currDigit] == 9; currDigit++) {
abacus[currDigit] = 0; // a carry occurs when adding 1
}
if (currDigit == abacus.length) {
// a carry, but we don't have enough lines on the abacus
abacus = new int[abacus.length + 1];
abacus[currDigit] = 1; // we resolved the carry, all the digits below
// are 0
} else {
abacus[currDigit]++; // we resolve the carry (if there was one),
currDigit = 0; // now it's safe to continue incrementing at 10^0
}
// let's obtain the current number and the sum of the digits
int sumDigits = 0;
for (int i = 0; i<abacus.length; i++) {
sumDigits += abacus[i];
}
// is it special?
if (sumDigits % abacus.length == 0) {
// only now compute the number and collect it as special
int number = 0;
for (int i = abacus.length - 1; i >= 0; i--) {
number = 10 * number + abacus[i];
}
specials.add(number);
}
}
return specials;
}
static public void main(String[] args) {
ArrayList<Integer> specials=Specials.computeNSpecials(100);
for(int i=0; i<specials.size(); i++) {
System.out.println(specials.get(i));
}
}
}

Score Multiplier By Time

Here is my score manager script I made:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour {
public Text scoreText;
public float scoreCount; // What the score is when the scene first loads. I set this as zero.
public float pointsPerSecond; // Every second, the points increase by THIS amount. Right now it is 100.
// Update is called once per frame
void Update () {
scoreCount += pointsPerSecond * Time.deltaTime;
scoreText.text = "" + Mathf.Round (scoreCount); // It comes out as a float with like 6 decimals; I round to an nice, clean Integer
}
}
My problem that I cannot figure out how to solve is: How do I make a multiplier that multiplies the score by times two after 30 seconds of the game, then multiplies the score by times three after 1 minute, then times four after 1 Minute and 30 seconds, then finally, times five after 2 minutes? Thanks :)
private float multiplier = 2.0f;
void Start(){
InvokeRepeating("SetScore", 30f, 30f);
}
void SetScore(){
score *= multiplier;
multiplier += 1f;
if(multiplier > 5.5f) // Added 0.5f margin to avoid issue of float inaccuracy
{
CancelInvoke()
}
}
InvokeRepeating sets the first call (second parameter) and the frequency (third parameter), in your case it is 30s and also 30s. Then once the multiplier is too big (greater than 5), you cancel the invoke.
If your multiplier is an integer, you can remove the margin and use a round number.
This is a perfect opportunity to use an IEnmurator function. These are methods you can call, which you can tell to wait for a certain period of time before resuming. So in your case, you could have an IEnumerator function the multiplies your score every thirty seconds. For example:
public Text scoreText;
public float scoreCount; // What the score is when the scene first loads. I set this as zero.
public float pointsPerSecond; // Every second, the points increase by THIS amount. Right now it is 100.
private int scoreMultiplier = 1;
void Start()
{
StartCoroutine(MultiplyScore());
}
// Update is called once per frame
void Update ()
{
scoreCount += pointsPerSecond * Time.deltaTime;
scoreText.text = "" + Mathf.Round (scoreCount); // It comes out as a float with like 6 decimals; I round to an nice, clean Integer
}
IEnumerator MultiplyScore()
{
while(true)
{
yield return new WaitForSeconds(30);
scoreMultiplier++;
scoreCount *= scoreMultiplier;
}
}
Note, if you only want to go up to 5 times multiplication you can use the score multiplier variable as the condition in your IEnumerator while-loop like so:
IEnumerator MultiplyScore()
{
while(scoreMultiplier < 5)
{
yield return new WaitForSeconds(30);
scoreMultiplier++;
scoreCount *= scoreMultiplier;
}
}

Sampling from a discrete distribution while accounting for past occurrences

I have a discrete distribution that I need to sample from. It is heavily skewed and an example is as follows
a - 1-40
b - 40-80
c - 80-85
d - 85-90
e - 90-95
f - 95-100
To currently sample from this distribution, I'm choosing a random number in the interval [1,100] and choosing the corresponding value.
However, I'd like to be certain that if I see one of [c,d,e,f] I don't see the exact same value being sampled for the next x samples. The context being that they are powerups in a game I'm building. I'd like the powerups to be random, but not hand out the same powerup to the player repeatedly.
Is there any method that incorporates past occurrences of samples into generating a value or do I have to repeatedly sample till I get a value I'd prefer?
One way to do this is to shuffle an array containing the values 1-100 and then iterate through as necessary.
For example, an implementation of this in Java:
public class PowerUpSelector
{
private int current;
private int x;
private int[] distribution;
public PowerUpSelector(int x)
{
this.x = x;
current = 0;
distribution = new int[100];
for (int i = 0; i < distribution.length; i++)
distribution[i] = i;
shuffle(distribution[i]); //implement fisher-yates shuffle here.
}
public int returnPowerUpID()
{
if (current >= x)
{
shuffle(distribution);
current = 0;
}
return distribution[current++];
}
}

What class of algorithms reduce margin of error in continuous stream of input?

A machine is taking measurements and giving me discrete numbers continuously like so:
1 2 5 7 8 10 11 12 13 14 18
Let us say these measurements can be off by 2 points and a measurement is generated every 5 seconds. I want to ignore the measurements that may potentially be same
Like continuous 2 and 3 could be same because margin of error is 2 so how do I partition the data such that I get only distinct measurements but I would also want to handle the situation in which the measurements are continuously increasing like so:
1 2 3 4 5 6 7 8 9 10
In this case if we keep ignoring the consecutive numbers with difference of less than 2 then we might lose actual measurements.
Is there a class of algorithms for this? How would you solve this?
Just drop any number that comes 'in range of' the previous (kept) one. It should simply work.
For your increasing example:
1 is kept, 2 is dropped because it is in range of 1, 3 is dropped because it is in range of 1, then 4 is kept, 5 and 6 are dropped in range of 4, then 7 is kept, etc, so you still keep the increasing trend if it's big enough (which is what you want, right?
For the original example, you'd get 1,5,8,11,14,18 as a result.
In some lines of work, the standard way to deal with problems of this nature is by using the Kalman filter.
To quote Wikipedia:
Its [Kalman filter's] purpose is to use measurements
observed over time, containing noise
(random variations) and other
inaccuracies, and produce values that
tend to be closer to the true values
of the measurements and their
associated calculated values.
The filter itself is very easy to implement, but does require calibration.
I would have two queues:
Temporary Queue
Final Queue/List
Your first value would go into the temporary queue and in the final list. As new values come in, check to see if the new value is within the deadband of the last value in the list. If it is then add it to the temporary queue. If not then add it to the final list. If your temporary queue starts to increase in size before you get a new value outside of the deadband, then once you are outside of the deadband do a check to see if the values are monotonically increasing or decreasing the whole time. If they are always increasing or decreasing then add the contents of the queue to the final list, otherwise just add the single new value to the final list. This is the general gist of it.
Here is some code I whipped up quickly that implements a class to do what I described above:
public class MeasurementsFilter
{
private Queue<int> tempQueue = new Queue<int>();
private List<int> finalList = new List<int>();
private int deadband;
public MeasurementsFilter(int deadband)
{
this.deadband = deadband;
}
public void Reset()
{
finalList.Clear();
tempQueue.Clear();
}
public int[] FinalValues()
{
return finalList.ToArray();
}
public void AddNewValue(int value)
{
// if we are just starting then the first value always goes in the list and queue
if (tempQueue.Count == 0)
{
tempQueue.Enqueue(value);
finalList.Add(value);
}
else
{
// if the new value is within the deadband of the last value added to the final list
// then enqueue the value and wait
if ((tempQueue.Peek() - deadband <= value) && (value <= tempQueue.Peek() + deadband))
{
tempQueue.Enqueue(value);
}
// else the new value is outside of the deadband of the last value added to the final list
else
{
tempQueue.Enqueue(value);
if (QueueIsAlwaysIncreasingOrAlwaysDecreasing())
{
//dequeue first item (we already added it to the list before, but we need it for comparison purposes)
int currentItem = tempQueue.Dequeue();
while (tempQueue.Count > 0)
{
// if we are not seeing two in a row of the same (i.e. they are not duplicates of each other)
// then add the newest value to the final list
if (currentItem != tempQueue.Peek())
{
currentItem = tempQueue.Dequeue();
finalList.Add(currentItem);
}
// otherwise if we are seeing two in a row (i.e. duplicates)
// then discard the value and loop to the next value
else
{
currentItem = tempQueue.Dequeue();
}
}
// add the last item from the final list back into the queue for future deadband comparisons
tempQueue.Enqueue(finalList[finalList.Count - 1]);
}
else
{
// clear the queue and add the new value to the list and as the starting point of the queue
// for future deadband comparisons
tempQueue.Clear();
tempQueue.Enqueue(value);
finalList.Add(value);
}
}
}
}
private bool QueueIsAlwaysIncreasingOrAlwaysDecreasing()
{
List<int> queueList = new List<int>(tempQueue);
bool alwaysIncreasing = true;
bool alwaysDecreasing = true;
int tempIncreasing = int.MinValue;
int tempDecreasing = int.MaxValue;
int i = 0;
while ((alwaysIncreasing || alwaysDecreasing) && (i < queueList.Count))
{
if (queueList[i] >= tempIncreasing)
tempIncreasing = queueList[i];
else
alwaysIncreasing = false;
if (queueList[i] <= tempDecreasing)
tempDecreasing = queueList[i];
else
alwaysDecreasing = false;
i++;
}
return (alwaysIncreasing || alwaysDecreasing);
}
}
Here is some test code that you can throw into a Winform Load event or button click:
int[] values = new int[] { 1, 2, 2, 1, 4, 8, 3, 2, 1, 0, 6 };
MeasurementsFilter filter = new MeasurementsFilter(2);
for (int i = 0; i < values.Length; i++)
{
filter.AddNewValue(values[i]);
}
int[] finalValues = filter.FinalValues();
StringBuilder printValues = new StringBuilder();
for (int i = 0; i < finalValues.Length; i++)
{
printValues.Append(finalValues[i]);
printValues.Append(" ");
}
MessageBox.Show("The final values are: " + printValues);

Resources