C#: A Bounding Max and Min on Array[] - c#

Suppose I have a List. I'd like to be able to calculate semi-equi-distant max and min bounding points. I don't want to simply get the Max() and Min() its slightly more complicated.
To start, I'd like to specify a point in the list in which the list can be divided. To make it easy for now, suppose that point is 0. I'd then like to specify the number of divisions. Example:
List<int> Array = {-9,-8,-7,-2,-1,0,1,6,9,12};
int Divisions = 4;
int CutOff = 0;
So using these parameters I'd like to walk out to the extremes starting from 0 until there are 4 divisions. In this case the DivisionSize should be 6.
So the algorithm would start at 0 and walk to -6 for 1 Division then walk to -12 for the 2nd division. -12 would then become the bounding Min for the purposes of this algorithm.
The Max would then be calculated by starting at 0 and walking to 6, then 12. The bounding Max would then be 12. Its okay if the Calculate Max and Min are the actual Max and Min of the list, this is just an unlikely case.
I'm basically have some issues calculating the DivisionSize. I started with (Abs(Max)+Abs(Min))/Divisions but I can't seem to get the edge case where the Calculated size of the each division needs to be expanded to actually encompass the original Min and Max. Can somebody provided some guidance?
Edit: I don't necessarily want the BoundedMax and BoundedMin to be symmetrical about the cutoff. I want to add slack to either side of the cutoff until the BoundedMin and BoundedMax are >= and <= the range of the List.

Since your divisions are going to be "semi-equidistant" from the cutoff, your algorithm should only focus on half the divisions (one side from the cutoff). The next step would be to determine which of the "sides" of the cutoff is larger.
Next, we divide the larger side by half the division, and get the Ceiling of the value (round to next higher integer). This will give us the size of each division of the larger side which would encompass all the values on both sides of the cutoff.
The following algorithm would give you the DivisionSize of 6 when applied to the example you provided:
int NewMax = Abs(Max - CutOff);
int NewMin = Abs(Min - CutOff);
int DivisionSize = (int)Math.Ceiling(NewMax > NewMin ? NewMax/(Divisions/2) : NewMin/(Divisions/2));

L = abs(min(A)-cut)
R = abs(max(A)-cut)
size = max(L,R) # ate least two divisions
while divisions >= (1+(L-1)/size + 1+(R-1)/size)
size = size-1
size = size+1
Lets try it out:
L = 9
R = 12
size = 12
d = 1 + (9-1)/12 + 1 + (12-1)/12 = 1 + 1 = 2
size = 11
d = 1 + (9-1)/11 + 1 + (12-1)/11 = 1 + 2 = 3
size = 10
d = 1 + (9-1)/10 + 1 + (12-1)/10 = 1 + 2 = 3
size = 9
d = 1 + (9-1)/9 + 1 + (12-1) / 9 = 1 + 2 = 3
size = 8
d = 1 + (9-1)/8 + 1 + (12-1) / 8 = 2 + 2 = 4
size = 7
d = 1 + (9-1)/7 + 1 + (12-1) / 7 = 2 + 2 = 4
size = 6
d = 1 + (9-1)/6 + 1 + (12-1) / 6 = 2 + 2 = 4
size = 5
d = 1 + (9-1)/5 + 1 + (12-1) / 5 = 2 + 3 = 5
--> size = 6
Note that the integer divisions must be floored (not rounded).
For optimization, you can use a binary search between 1 and R for the size.

I think the key is to determine how many of your divisions you want either side of the CutOff point, by taking the ratio of each side's length to the total length.
In your example, the sides are 9 and 12, giving (approx) 1.7 and 2.2 divisions either side. The actual numbers must be integers, so try (1,3) and (2,2). 1 division on the left means the size must be 9, 2 divisions on either side allow you to use division size 6.
Wrote some C# to illustrate this. Not particularly elegant, but it seems to work.
public class RangeDivider
{
public int Min;
public int CutOff;
public int Max;
public int NumDivisions;
public RangeDivider(int min, int cutOff, int max, int numDivisions)
{
Min = min;
CutOff = cutOff;
Max = max;
NumDivisions = numDivisions;
System.Diagnostics.Debug.Assert(Min < CutOff && CutOff < Max && numDivisions >= 2);
}
public int LeftSize { get { return CutOff - Min; } }
public int RightSize { get { return Max - CutOff; } }
public int WholeSize { get { return Max - Min; } }
private static int divCeil(int dividend, int divisor) { return 1 + (dividend - 1)/divisor; }
private int ReturnSize(int leftDivisions)
{
int rightDivisions = NumDivisions - leftDivisions;
if (leftDivisions > 0 && rightDivisions > 0)
{
return Math.Max(divCeil(LeftSize, leftDivisions), divCeil(RightSize, rightDivisions));
}
else
{ //Must have at least 1 division each side of cutoff
return int.MaxValue;
}
}
public int GetSize()
{
var leftDivisions = NumDivisions * LeftSize / WholeSize;
var size = Math.Min(ReturnSize(leftDivisions), ReturnSize(leftDivisions + 1));
Console.WriteLine("Min {0}, CutOff {1}, Max {2}, NumDivisions {3} gives a Division Size of {4}", Min, CutOff, Max, NumDivisions, size);
return size;
}
public static int Get(int min, int cutOff, int max, int numDivisions)
{
return new RangeDivider(min, cutOff, max, numDivisions).GetSize();
}
public static void Test()
{
Get(-7,0,57,4);
Get(-9, 0, 12, 4);
Get(-1, 0, 7, 6);
}
}
Min -7, CutOff 0, Max 57, NumDivisions 4 gives a Division Size of 19
Min -9, CutOff 0, Max 12, NumDivisions 4 gives a Division Size of 6
Min -1, CutOff 0, Max 7, NumDivisions 6 gives a Division Size of 2

Related

Get min and max integer value containing X number of digits

What would be the best way to generate the lowest and highest integer values which contain x number of digits?
For example:
x = 1: Min = 0, Max = 9
x = 2: Min = 10, Max = 99
x = 3: Min = 100, Max = 999
x = 4: Min = 1000, Max = 9999
I feel like this should be a really easy thing to accomplish but I'm struggling to get my head around the math behind it.
Here's what I've got so far for generating the max value (based on this answer):
public static int MaxIntWithXDigits(this int x)
{
if (x == 0) throw new ArgumentException(nameof(x), "An integer cannot contain zero digits.");
try
{
return Convert.ToInt32(Math.Pow(10, x) -1);
}
catch
{
throw new InvalidOperationException($"A number with {x} digits cannot be represented as an integer.");
}
}
If anyone can help with generating the min value or suggest improvements on the code above I'd appreciate it.
Edit
I'm almost there - this seems to work for everything except when x = 1 (and min value expected as 0)
public static int MinIntWithXDigits(this int x)
{
if (x == 0) throw new ArgumentException(nameof(x), "An integer cannot contain zero digits.");
x -= 1;
try
{
return Convert.ToInt32(Math.Pow(10, x));
}
catch
{
throw new InvalidOperationException($"A number with {x} digits cannot be represented as an integer.");
}
}
Fox any x>1, the min value would be 10x-1. E.g.:
If x = 2, min=102-1 = 101 = 10
If x = 3, min=103-1 = 102 = 100
If x = 4, min=104-1 = 103 = 1000
For x=1, since it's the only value that is not a one followed by a series of zeroes, you'll need special treatment.
public static int MinIntWithXDigits(this int x)
{
if (x == 0)
{
throw new ArgumentException(nameof(x), "An integer cannot contain zero digits.");
}
if (x == 1) {
return 0;
}
try
{
return Convert.ToInt32(Math.Pow(10, x - 1));
}
catch
{
throw new InvalidOperationException($"A number with {x} digits cannot be represented as an integer.");
}
}
Side note:
If the result were restricted to positive integers instead of non-negative integers, the smallest integer with one digit would be 1, and the general formula would have applied for this case too.
You can try something like this:
// let's return min and max in one go as a value tuple
public static (int min, int max) MaxIntWithXDigits(this int x) {
if (x <= 0 || x > 9)
throw new ArgumentOutOfRangeException(nameof(x));
int min = (int)Math.Pow(10, x - 1);
return (min == 1 ? 0 : min, min * 10 - 1);
}
Demo:
var result = Enumerable
.Range(1, 9)
.Select(x => $"{x} : {MaxIntWithXDigits(x).min} .. {MaxIntWithXDigits(x).max}");
Console.Write(string.Join(Environment.NewLine, result));
Outcome:
1 : 0 .. 9
2 : 10 .. 99
3 : 100 .. 999
4 : 1000 .. 9999
5 : 10000 .. 99999
6 : 100000 .. 999999
7 : 1000000 .. 9999999
8 : 10000000 .. 99999999
9 : 100000000 .. 999999999

Pairs of amicable numbers

I have a task to find pairs of amicable numbers and I've already solved it. My solution is not efficient, so please help me to make my algorithm faster.
Amicable numbers are two different numbers so related that the sum of the proper divisors of each is equal to the other number. The smallest pair of amicable numbers is (220, 284). They are amicable because the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110, of which the sum is 284; and the proper divisors of 284 are 1, 2, 4, 71 and 142, of which the sum is 220.
Task: two long numbers and find the first amicable numbers between them. Let s(n) be the sum of the proper divisors of n:
For example:
s(10) = 1 + 2 + 5 = 8
s(11) = 1
s(12) = 1 + 2 + 3 + 4 + 6 = 16
If s(firstlong) == s(secondLong) they are amicable numbers
My code:
public static IEnumerable<long> Ranger(long length) {
for (long i = 1; i <= length; i++) {
yield return i;
}
}
public static IEnumerable<long> GetDivisors(long num) {
return from a in Ranger(num/2)
where num % a == 0
select a;
}
public static string FindAmicable(long start, long limit) {
long numberN = 0;
long numberM = 0;
for (long n = start; n <= limit; n++) {
long sumN = GetDivisors(n).Sum();
long m = sumN;
long sumM = GetDivisors(m).Sum();
if (n == sumM ) {
numberN = n;
numberM = m;
break;
}
}
return $"First amicable numbers: {numberN} and {numberM}";
}
I generally don't write C#, so rather than stumble through some incoherent C# spaghetti, I'll describe an improvement in C#-madeup-psuedo-code.
The problem seems to be in your GetDivisors function. This is linear O(n) time with respect to each divisor n, when it could be O(sqrt(n)). The trick is to only divide up to the square root, and infer the rest of the factors from that.
GetDivisors(num) {
// same as before, but up to sqrt(num), plus a bit for floating point error
yield return a in Ranger((long)sqrt(num + 0.5)) where num % a == 0
if ((long)sqrt(num + 0.5) ** 2 == num) { // perfect square, exists
num -= 1 // don't count it twice
}
// repeat, but return the "other half"- num / a instead of a
yield return num/a in Ranger((long)sqrt(num + 0.5)) where num % a == 0
}
This will reduce your complexity of that portion from O(n) to O(sqrt(n)), which should provide a noticeable speedup.
There is a simple formula giving the sum of divisors of a number knowing its prime decomposition:
let x = p1^a1 * ... * pn^an, where pi is a prime for all i
sum of divisors = (1+p1+...+p1^a1) * ... (1+pn+...+pn^an)
= (1-p1^(a1+1))/(1-p1) * ... ((1-pn^(an+1))/(1-pn)
In order to do a prime decomposition you must compute all prime numbers up to the square root of the maximal value in your search range. This is easily done using the sieve of Erathostenes.

Get range in multiplication of 5

I have a number. For instance, my number is 19 . Then I want to populate my drop down with range in multiplication of 5. So my dropdownlist will consist of items of:
1-5
6-10
11-15
16-19
I tried modulus and division, however, I can't seems to get the range. Is there a fixed method?
Sample code
List<string> range = new List<string>();
int number = 19;
int numOfOccur = (19/5);
for (int i = 1; i < numOfOccur ; i++)
{
range.Add(i + " - " + (i * 5))
}
Sometime I think that old school code, without fancy linq is a bit more clear
int maximum = 19;
int multiple = 5;
int init = 1;
while (init + multiple <= maximum )
{
string addToDDL = init.ToString() + "-" + (init + multiple - 1).ToString();
Console.WriteLine(addToDDL);
init += multiple;
}
if(init <= maximum)
{
string last = init.ToString() + "-" + maximum.ToString();
Console.WriteLine(last);
}
Linq solution (modern techs allow us to put it consize):
int number = 19;
int div = 5;
List<string> range = Enumerable
.Range(0, number / div + (number % div == 0 ? 0 : 1))
.Select(i => $"{i * div + 1} - {Math.Min((i + 1) * div, number)}")
.ToList();
Test
Console.Write(string.Join(Environment.NewLine, range));
Returns
1 - 5
6 - 10
11 - 15
16 - 19
When using modulo arithmetics, do not forget about remainders: you have an error in int numOfOccur = (19/5); line. It should be
int numOfOccur = 19 / 5 + (19 % 5 == 0 ? 0 : 1);
for the last incomplete 16 - 19 range to be proceeded.
Add this package to your project : https://www.nuget.org/packages/System.Interactive/
Then you can do this:
IEnumerable<IList<int>> buffers2 = Enumerable.Range(1, 19).Buffer(5);
IList<int>[] result2 = buffers2.ToArray();
// { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, ...
Don't forget to add System.Interactive namespace to your using block.

Find all the addition and subtraction combinations from an array of numbers

I need to make a function to take in an array of numbers and a target number and return how many different ways you can add or subtract those numbers to get the target number.
ie.
Values = 2, 4, 6, 8 Target = 12
2 + 4 + 6 = 12,
4 + 8 = 12,
6 + 8 - 2 = 12,
2 - 4 + 6 + 8 = 12,
Return 4
Here is what I have so far, but it only counts addition problems.
private void RecursiveSolve(int goal, int currentSum, List<int> included, List<int> notIncluded, int startIndex)
{
for (int index = startIndex; index < notIncluded.Count; index++)
{
int nextValue = notIncluded[index];
if (currentSum + nextValue == goal)
{
List<int> newResult = new List<int>(included);
newResult.Add(nextValue);
mResults.Add(newResult);
}
else if (currentSum - nextValue == goal)
{
List<int> newResult = new List<int>(included);
newResult.Add(nextValue);
mResults.Add(newResult);
}
if (currentSum - nextValue < goal && currentSum - nextValue > 0 )
{
List<int> nextIncluded = new List<int>(included);
nextIncluded.Add(nextValue);
List<int> nextNotIncluded = new List<int>(notIncluded);
nextNotIncluded.Remove(nextValue);
RecursiveSolve(goal, currentSum - nextValue, nextIncluded, nextNotIncluded, startIndex++);
}
if (currentSum + nextValue < goal)
{
List<int> nextIncluded = new List<int>(included);
nextIncluded.Add(nextValue);
List<int> nextNotIncluded = new List<int>(notIncluded);
nextNotIncluded.Remove(nextValue);
RecursiveSolve(goal, currentSum + nextValue, nextIncluded, nextNotIncluded, startIndex++);
}
}
}
Well, the simple way would be to try all of the combinations. If you have N numbers, you have 3^N combinations. The reasoning is this: You sum the numbers but put a coefficient in front of each of them. If your numbers are A1..AN, you add N coefficients (C1..CN) and sum:
Sum (Ai*Ci)
Your Cis can be 1 (meaning you add the number), -1 (meaning you subtract the number) or 0 (meaning you ignore the number).
So, go over all 3^N possible coefficient assignments, calculate the sum and compare to your target.
I am assuming all the numbers are different (as in your example). If a number can appear twice, you need to take that into account.

C# Formula to distribute numbers

I'm looking for a formula that can spread out numbers in a linear format based on a minimum number, max number and amount of numbers (or dots) between. The catch is, the closer you get to the max, the more numbers should be there.
An example (number will vary and will be about 100 times larger)
Min = 0
Max = 16
AmountOfNumbersToSpread = 6
0 1 2 3 4 5 6 7 8 9 A B C D E F
1 2 3 4 5 6
Thanks for the help in advance.
Based on the answer of Tal Pressman, you can write a distribution function like this:
IEnumerable<double> Spread(int min, int max, int count, Func<double, double> distribution)
{
double start = min;
double scale = max - min;
foreach (double offset in Redistribute(count, distribution))
yield return start + offset * scale;
}
IEnumerable<double> Redistribute(int count, Func<double, double> distribution)
{
double step = 1.0 / (count - 1);
for (int i = 0; i < count; i++)
yield return distribution(i * step);
}
You can use any kind of distribution function which maps [0;1] to [0;1] this way. Examples:
quadratic
Spread(0, 16, 6, x => 1-(1-x)*(1-x))
Output: 0 5.76 10.24 13.44 15.36 16
sine
Spread(0, 16, 6, x => Math.Sin(x * Math.PI / 2))
Output: 0 4.94427190999916 9.40456403667957 12.9442719099992 15.2169042607225 16
Basically, you should have something that looks like:
Generate a random number between 0 and 1.
Implement your desired distribution function (a 1:1 function from [0,1]->[0,1]).
Scale the result of the distribution function to match your desired range.
The exact function used for the second point is determined according to how exactly you want the numbers to be distributed, but according to your requirement, you'll want a function that has more values close to 1 than 0. For example, a sin or cos function.
Tried this on paper and it worked:
given MIN, MAX, AMOUNT:
Length = MAX - MIN
"mark" MIN and MAX
Length--, AMOUNT--
Current = MIN
While AMOUNT > 1
Space = Ceil(Length * Amount / (MAX - MIN))
Current += Space
"mark" Current
By "mark" I mean select that number, or whatever you need to do with it.
Close answer, not quite though, needs to work for larger numbers.
List<int> lstMin = new List<int>();
int Min = 1;
int Max = 1500;
int Length = Max - Min;
int Current = Min;
int ConnectedClient = 7;
double Space;
while(ConnectedClient > 0)
{
Space = Math.Ceiling((double)(Length * ConnectedClient / (Max - Min)));
Current += (int)Space;
ConnectedClient--;
Length--;
lstMin.Add(Current);
}

Categories