I have this problem where I want to keep a list of values and normalize them so that the sum of all values is 1.
I found this question, but there is one difference. I would like to have 1 value that is fixed. Because, there are 4 GUI sliders and I drag one. The other 3 should update accordingly so that the total sum is 1.
// Let's keep 0.6 fixed and try to normalize the rest
List<float> categories = new List<float>() { 0.6f,0.3f,0.25f,0.25f };
// find the max value of the rest of the values that is not the fixed number
float maxval = categories.Where(s => s != 0.6f).Max();
if (maxval != 0) // check if not all other
{
// calculate the ratio
float ratio = (1 - 0.6f) / maxval;
foreach (float s in categories)
{
if (s != 0.6f)
{
s *= ratio;
}
}
}
else // all values are 0, so just add the rest value
{
// do something else that is correct
}
Unfortunately this example will return:
{ 0.6f, 0.4f, 0.33f, 0.33f } => sum is 1.66f
And there is something that I'm misunderstanding. Can someone give me a clue on how to get the rest of the values normalized to the rest sum (0.4f) so that the total sum of all values is 1?
You are calculating it wrongly.
If you have a 4 elements, and 1 of those is fixed, and the total sum needs to be 1, you would go like:
ratio = (1-fixedElement) / sumOfAllOtherElements;
EachNonFixedElement *= ratio;
You are calculating the ratio just with the max value, but you need to calculate it with the sum of all non-fixed elements.
Among other problems, this loop doesn't do what you expect:
foreach (float s in categories) {
if (s != 0.6f) {
s *= ratio;
}
}
s is a local copy of one of the values from categories. You modify the copy, and then it goes out of scope and your new value disappears. You never modified categories.
C# foreach cannot be used to modify a collection.
Furthermore, to avoid accumulating large rounding error, you should store to a new collection and not replace the values in categories, at least until the drag operation ends.
Related
im currently working on a game and i need to divide a changeable amount of objects in to a limited area. I made a image to explain better:
(I know its a bit difficult to understand)
So i can't find a solution and couldn't figure it out myself. I tried a few ways to multiply and divide the object count with the index and the xMaximum but it didn't work as expected.
This is what i tried:
public float xMin, xMax;
private void UpdateValues()
{
for(int i = 0; i < transform.childCount; i++)
{
float value = xMin;
float maxIndex = transform.childCount;
float index = i;
value = transform.position.x + xMax / (maxIndex * index);
transform.GetChild(i).transform.position = new Vector3(transform.position.x, transform.position.y, value);
}
}
It just gave me the wrong numbers and i can't figure the right way out.
The available variables are:
xMinimum, xMaximum
objectcount
objectindex
If you want it to look exactly how you have shown it on images then it might be a little more tricky. Objects don’t fill the area always the same, so we need to add some exceptions:
if there is only 1 item, then just set it’s position to (xMin + xMax) / 2f
if item is first, and there are more than one, set its position to xMin + (squareWidth / 2f) (or something else if pivot is not in the middle)
if item is last and there are more than one, set its position to xMax - (squareWidth / 2f)
the remaining area, which is of size xMax - 2 * squareWidth need to be divided equally between all remaining objects. Compute the amount remaining objects (which will be all objects - 2 (first and last)). Divide remaining area by number of objects plus 1. Set remaining objects position in a loop, the statement inside will look something like this: value = (i+1) * remainingArea / (transform.childCount - 2 + 1)
That’s the rough list of steps that might help. In this example I assume, that xMin is 0, otherwise you will need to take that into account and for eg. Remaining area won’t be just xMax - 2 * squareWidth, but xMax - xMin - 2 * squareWidth.
Also you might find yourself being off a half a square here and there, but that should be easy to correct.
This question already has answers here:
Generating a list of random numbers, summing to 1
(12 answers)
Closed 2 years ago.
I am making a space game with planets that are randomly generated and I need their atmospheres to be made up of random amounts of different elements. So the thing is that I want the elements to be percentages of the complete atmosphere, an example: oxygen = 30, nitrogen = 20, carbondioxide = 50, hydrogen = 0. All these values should be completely randomized and the sum of them all has to be 100.
I basically want to fill a container to the top with random amounts of set elements, but I don't know how to randomize all of the variables and end up with a fixed sum.
This is my first time submitting anything to StackOverflow so please let me know if there is anything I need to clarify, I've been stuck on this issue for so long without finding any answers so I would appreciate any help, thanks :)
(I am using c# in unity in case that makes a difference)
Well, you could think of it from the other perspective, and generate random values for each element, then use their total as 100%.
For instance...
var rnd = New Random()
ChecmicalsInAir = 9;
Hydrogen = rnd.Next();
Oxygen = rnd.Next();
...
TotalAtmos = (Hydrogen + Oxygen + ...);
Yes - as pointed out by canton7, below:
Dividing the chemical by TotalAtmos will give you the percentage of atmosphere.
Just to add a little more to this answer, you may also wish to use a Dictionary to store the information, rather than simple variables; that way you can include different gases in the atmosphere, as not all planets may have oxygen or nitrogen, this could add different bonuses or penalties, but remains a straightforward process for calculating percentages.
Questions like "how would you design this..." are better suited for Game Development. Those questions tend to get downvoted here a lot.
Here's one way you could do it.
int elementCount = 3; // set to the number of elements you have
List<float> elementPercents = new List<float>();
// Assign an initial value between 0 and 1 to each element, and set totalWeight
// to the sum of all those values. Note that this lets you have 0 for an element.
// If you don't want 0 for an element, adjust the Random.Range
float totalWeight = 0;
for (int i = 0; i < elementCount; i++)
{
elementPercents.Add(UnityEngine.Random.Range(0f, 1f));
totalWeight += elementPercents[i];
}
// Set the percent of each element to its proportion of the total weight.
// For example, if Oxygen = 0.1 and Hydrogen = 0.2 and Nitrogen = 0.3 from the above statement,
// then Oxygen's percent = 0.1 / (0.1 + 0.2 + 0.3) = 0.167
for (int i = 0; i < elementCount; i++)
{
elementPercents[i] /= totalWeight;
}
// Assign the percents to each element according to your requirements, for example
float hydrogen = elementPercents[0];
float nitrogen = elementPercents[1];
float oxygen = elementPercents[2];
The title is not really well phrased, I'm aware - can't think of a better way of writing it though.
Here's the scenario - I have two input boxes, both representing integer quantities. One is represented in our units, the other in the vendor's units. There is a multiplier defining how to convert from ours to theirs. In the below example, I'm saying that two of our units is equal to five of theirs. So, for example,
decimal multiplier = 0.4; // Two of our units equals five of theirs
int requestedQuantity = 11; // Our units
int suppliedQuantity = 37; // Their units
// Should return 12, since that is the next highest whole number that results in both of us having whole numbers (12 of ours = 30 of theirs)
int correctedFromRequestedQuantity = GetCorrectedRequestedQuantity(requestedQuantity, null, multiplier);
// Should return 16, since that is the next highest whole number that results in both of us having whole numbers (16 of ours = 40 of theirs);
int correctedFromSuppliedQuantity = GetCorrectedRequestedQuantity(suppliedQuantity, multiplier, null);
Here's the function I've written to handle this. I'm not doing a divide by zero check on the multiplier / rounder since I've already checked for that elsewhere. It seems crazy to do all that converting, but is there a better way of doing it?
public int GetCorrectedRequestedQuantity(int? input, decimal? multiplier, decimal? rounder)
{
if (multiplier == null)
{
if (rounder == null)
return input.GetValueOrDefault();
else
return (int)Math.Ceiling((decimal)((decimal)Math.Ceiling(input.GetValueOrDefault() / rounder.Value) * rounder.Value));
}
else if (input.HasValue)
{
// This is insane...
return (int)Math.Ceiling((decimal)((decimal)Math.Ceiling((int)Math.Ceiling((decimal)input * multiplier.Value) / multiplier.Value) * multiplier.Value));
}
else
return 0;
}
Represent the multiplier as a fraction in lowest terms. I don't know if .NET has a fractions class but if not you can probably find a C# implementation, or just write your own. So assume the multiplier is given by two integers a / b in lowest terms, with a ≠ 0 and b ≠ 0. That also means that conversion in the other direction is given by multiplying by b / a. In your example, a = 2 and b = 5, and a / b = 0.4.
Now suppose you want to convert an integer X. If you think about it a bit you'll see what you really want is to nudge X up until b divides X. The number you need to add to X is simply (b - (X%b)) % b. So to convert on one direction is just
return (a * (X + (b - (X % b) % b))) / b;
and to convert Y going in the other direction is just
return (b * (Y + (a - (X % a) % a))) / a;
My best idea of my head is to semi brute-force it. It does sound like it is basically Fraction Mathematics. So there might be a way easier solution for this.
First we need to find in what sort of "Batch" the multiplier becomes whole. That way, we can stop working with floats/doubles altogether. Ideally this should be supplied with the multiplier (as float math is messy).
double currentMultiple=multiplier;
int currentCount=0;
//This is the best check for "is an integer" could think off.
while(currentMultiple % 1 = 0){
//The Framework can detect Arithmetic Overflow. Let us turn that one on
//If we ever get there, likely the math is non-solveable
checked{
currentMultiple+= multiplier;
currentCount += 1;
}
}
//You get here either via exception or because you got a multiple that solves it.
//Store the value of currentCount into a variable "OurBatchSize"
//Also store the value of currentMultiple in "TheirBatchSize"
Getting the closest Multiple of OurBatchSize:
int requestedQuantity = 11; // Our units
int result = OurBatchSize;
int batchCount = 0;
while(temp < requestedQuantity){
result += OurBatchSize;
batchCount++
}
//result contains the answer here. Return it
//batchCount * TheirBatchSize will also tell you how much they get.
Edit: Credit for this goes mostly to James Reinstate Monica Polk. He had the math idea to use Modulo for this. Here is what I got with explanation:
int result;
int rest = requestedAmout % BatchSize;
if (rest != 0){
//Correct upwards to the next multiple
int DistanceToNextMultiple = BatchSize - Rest;
result = requestedAmout + DistanceToMultiple;
}
else{
//It already is right
result = requestedAmout;
}
For the BatchSize of 4, you will get:
13; 13%4=1; 4-1=3; 13+3=16;
14; 14%4=2; 4-2=2; 14+2=16;
15; 15%4=3; 4-3=1; 15+1=16;
16; 16%4=0; Else is used. 16 is already right.
I have two lists of numbers, the first list has 365 numbers and the other one has 8760.
I want to loop the first list first and get the value. Then inside the first loop, I want to loop the second list to get the 24 number each time.
For the next iterative, it will skip the first 24 and get the next 24.
The problem is the "countelev" seems to not change and stay in ''24'' for every time when I print it. It supposes to be changed to 48, 72, 96....
This is my code:
foreach (var dec in declinationangle)
{
double declination = dec;
double elev = Degreetoradian(declination);
double lati = Degreetoradian(latitude);
// Solar elevation angle, expressed as α, is the angular height of the
//sun in the sky measured from the horizontal 0° at sunrise and 90° when
//the sun is right overhead.
//α = sin−1 (sin δ sin φ + cos δ cos φ cos τ)
int countelev = 0;
foreach (var hra in Solarhourangle.Skip(countelev).Take(24))
{
double hras = Degreetoradian(hra);
double elev1 = Math.Sin(elev) * Math.Sin(lati);
double elev2 = Math.Cos(elev) * Math.Cos(lati) * Math.Cos(hras);
double slav = Math.Asin(elev1 + elev2);
double sla = Radiantodegree(slav);
solarelevationangle.Add(sla);
}
countelev += 24;
TaskDialog.Show("countelev", countelev.ToString());
}
int countelev = 0;
This statement needs to be the first statement of the block that you have pasted. Means the statement needs to be outside the nested foreach statements.
It's considerably simpler with for instead of foreach. You only need one loop, with an index that iterates over the larger array. It can select the correct element from the smaller array by dividing the index by 24 and throwing away the remainder (which, conveniently, is how integer division in c# works).
Here's another way to think of it. Instead of generating elements from two smaller lists, you are populating elements in the target list from two other lists-- since there is only one target list, you only need one loop.
for (var i = 0; i< Solarhourangle.Count; i++)
{
hra = Solarhourangle[i];
dec = declinationangle[i/24];
double elev = Degreetoradian(declination);
double lati = Degreetoradian(latitude);
double hras = Degreetoradian(hra);
double elev1 = Math.Sin(elev) * Math.Sin(lati);
double elev2 = Math.Cos(elev) * Math.Cos(lati) * Math.Cos(hras);
double slav = Math.Asin(elev1 + elev2);
double sla = Radiantodegree(slav);
solarelevationangle.Add(sla);
}
Lets say i have a list of doubles:
0.0015
0.0016
0.0017
0.0019
0.0021
0.0022
0.0029
0.0030
0.0033
0.0036
And there's obviously a large difference in 0.0022 and 0.0029 compared to the rest, but is there a way i can make my C# program to be able to notice this difference within a sorted list W/O using a static threshold value. Because these data that i receive, the difference might not always be 0.0007 difference. So i would prefer if my program is able to be 'smart' enough to identify these 'large' differences and separate this list into multiple lists.
if i have understood your question correctly here goes. you may need to fill in a few gaps but you will get the drift with the below example:
List<double> doubleList = new List<double>{
0.0015,
0.0016,
0.0017,
0.0019,
0.0021,
0.0022,
0.0029,
0.0030,
0.0033,
0.0036
};
double averageDistance = 0.0;
double totals = 0.0;
double distance = 0.0;
for (int x = 0; x < (doubleList.Count - 1); x++)
{
distance = doubleList[x] - doubleList[x + 1];
totals += Math.Abs(distance);
}
averageDistance = totals / doubleList.Count;
// check to see if any distance between numbers is more than the average in the list
for (int x = 0; x < (doubleList.Count - 1); x++)
{
distance = doubleList[x] - doubleList[x + 1];
if (distance > averageDistance)
{
// this is where you have a gap that you want to do some split (etc)
}
}
Calculate the mean average (http://en.wikipedia.org/wiki/Arithmetic_mean) and the standard deviation (http://en.wikipedia.org/wiki/Standard_deviation). Use these to determine the values that fall outside of 'n' standard deviations.
Another approach would be to calculate all the differences between consecutive values, sort these (descending) and assume the top 'm' % of these difference values represent the largest changes.