algorithm to find the correct set of numbers - c#

i will take either python of c# solution
i have about 200 numbers:
19.16
98.48
20.65
122.08
26.16
125.83
473.33
125.92
3,981.21
16.81
100.00
43.58
54.19
19.83
3,850.97
20.83
20.83
86.81
37.71
36.33
6,619.42
264.53
...
...
i know that in this set of numbers, there is a combination of numbers that will add up to a certain number let's say it is 2341.42
how do i find out which combination of numbers will add up to that?
i am helping someone in accounting track down the correct numbers

Here's a recursive function in Python that will find ALL solutions of any size with only two arguments (that you need to specify).
def find_all_sum_subsets(target_sum, numbers, offset=0):
solutions = []
for i in xrange(offset, len(numbers)):
value = numbers[i]
if target_sum == value:
solutions.append([value])
elif target_sum > value:
sub_solutions = find_all_sum_subsets(target_sum - value, numbers, i + 1)
for sub_solution in sub_solutions:
solutions.append(sub_solution + [value])
return solutions
Here it is working:
>>> find_all_sum_subsets(10, [1,2,3,4,5,6,7,8,9,10,11,12])
[[4, 3, 2, 1], [7, 2, 1], [6, 3, 1], [5, 4, 1], [9, 1], [5, 3, 2], [8, 2], [7, 3], [6, 4], [10]]
>>>

You can use backtracking to generate all the possible solutions. This way you can quickly write your solution.
EDIT:
You just implement the algoritm in C#:
public void backtrack (double sum, String solution, ArrayList numbers, int depth, double targetValue, int j)
{
for (int i = j; i < numbers.Count; i++)
{
double potentialSolution = Convert.ToDouble(arrayList[i] + "");
if (sum + potentialSolution > targetValue)
continue;
else if (sum + potentialSolution == targetValue)
{
if (depth == 0)
{
solution = potentialSolution + "";
/*Store solution*/
}
else
{
solution += "," + potentialSolution;
/*Store solution*/
}
}
else
{
if (depth == 0)
{
solution = potentialSolution + "";
}
else
{
solution += "," + potentialSolution;
}
backtrack (sum + potentialSolution, solution, numbers, depth + 1, targetValue, i + 1);
}
}
}
You will call this function this way:
backtrack (0, "", numbers, 0, 2341.42, 0);
The source code was implemented on the fly to answer your question and was not tested, but esencially you can understand what I mean from this code.

[Begin Edit]:
I misread the original question. I thought that it said that there is some combination of 4 numbers in the list of 200+ numbers that add up to some other number. That is not what was asked, so my answer does not really help much.
[End Edit]
This is pretty clunky, but it should work if all you need is to find the 4 numbers that add up to a certain value (it could find more than 4 tuples):
Just get your 200 numbers into an array (or list or some IEnumerable structure) and then you can use the code that I posted. If you have the numbers on paper, you will have to enter them into the array manually as below. If you have them in softcopy, you can cut and paste them and then add the numbers[x] = xxx code around them. Or, you could cut and paste them into a file and then read the file from disk into an array.
double [] numbers = new numbers[200];
numbers[0] = 123;
numbers[1] = 456;
//
// and so on.
//
var n0 = numbers;
var n1 = numbers.Skip(1);
var n2 = numbers.Skip(2);
var n3 = numbers.Skip(3);
var x = from a in n0
from b in n1
from c in n2
from d in n3
where a + b + c + d == 2341.42
select new { a1 = a, b1 = b, c1 = c, d1 = d };
foreach (var aa in x)
{
Console.WriteLine("{0}, {1}, {2}, {3}", aa.a1, aa.b1, aa.c1, aa.d1 );
}

Try the following approach if finding a combination of any two (2) numbers:
float targetSum = 3;
float[] numbers = new float[]{1, 2, 3, 4, 5, 6};
Sort(numbers); // Sort numbers in ascending order.
int startIndex = 0;
int endIndex = numbers.Length - 1;
while (startIndex != endIndex)
{
float firstNumber = numbers[startIndex];
float secondNumber = numbers[endIndex];
float sum = firstNumber + secondNumber;
if (sum == targetSum)
{
// Found a combination.
break;
}
else if (sum < targetSum)
{
startIndex++;
}
else
{
endIndex--;
}
}
Remember that when use floating-point or decimal numbers, rounding could be an issue.

This should be implemented as a recursive algorithm. Basically, for any given number, determine if there is a subset of the remaining numbers for which the sum is your desired value.
Iterate across the list of numbers; for each entry, subtract that from your total, and determine if there is a subset of the remaining list which sums up to the new total. If not, try with your original total and the next number in the list (and a smaller sublist, of course).
As to implementation:
You want to define a method which takes a target number, and a list, and which returns a list of numbers which sum up to that target number. That algorithm should iterate through the list; if an element of the list subtracted from the target number is zero, return that element in a list; otherwise, recurse on the method with the remainder of the list, and the new target number. If any recursion returns a non-null result, return that; otherwise, return null.
ArrayList<decimal> FindSumSubset(decimal sum, ArrayList<decimal> list)
{
for (int i = 0; i < list.Length; i++)
{
decimal value = list[i];
if (sum - value == 0.0m)
{
return new ArrayList<decimal>().Add(value);
}
else
{
var subset = FindSumSubset(sum - value, list.GetRange(i + 1, list.Length -i);
if (subset != null)
{
return subset.Add(value);
}
}
}
return null;
}
Note, however, that the order of this is pretty ugly, and for significantly larger sets of numbers, this becomes intractable relatively quickly. This should be doable in less than geologic time for 200 decimals, though.

Related

Sum range using loop, calculate sum of odd and even numbers

Hi I am sick of searching I could not find the exact code for my question.
I need to code the sum of the odd numbers from 1 to 100
and sum of the even numbers from 2 to 100.
This is what i have so far.
Thank you so much
// 1) using for statement to Sum Up a Range of values using Interactive
Console.WriteLine(" Sum Up a Range of values entered by User ");
Console.WriteLine();
// 2) Declare the Variables to be used in the Project
string strFromNumber, strToNumber;
int fromNumber, toNumber;
int sum = 0;
int i, even = 0, odd = 0;
int[] array = new int[10];
// 3) Prompt the User to Enter the From Number to Sum From
Console.Write("Enter the From Number to Sum From: ");
strFromNumber = Console.ReadLine();
fromNumber = Convert.ToInt32(strFromNumber);
// 4) Prompt the User to Enter the To Number to Sum To
Console.Write("Enter the To Number to Sum To: ");
strToNumber = Console.ReadLine();
toNumber = Convert.ToInt32(strToNumber);
// 5) Use for statement to Sum up the Range of Numbers
for (i = fromNumber; i <= toNumber; ++i)
{
sum += i;
}
if //(array[i] % 2 == 0) //here if condition to check number
{ // is divided by 2 or not
even = even + array[i]; //here sum of even numbers will be stored in even
}
else
{
odd = odd + array[i]; //here sum of odd numbers will be stored in odd.
}
Console.WriteLine("The Sum of Values from {0} till {1} = {2}",
fromNumber, toNumber, sum);
Console.ReadLine();
There is no need to write the complex code which you have written.
Problem is to calculate the sum of arithmetic progression. The formula to find the sum of an arithmetic progression is Sn = n/2[2a + (n − 1) × d] where, a = first term of arithmetic progression, n = number of terms in the arithmetic progression and d = common difference.
So in case of odd numbers its a = 1, n = 50 and d = 2
and in case of even numbers its a = 2, n = 50 and d = 2
and if you try to normalize these above formulas, it will be more easy based on your problem.
the sum of the first n odd numbers is Sn= n^2
the sum of the first n even numbers is n(n+1).
and obviously, it's very simple to loop from ( 1 to 99 with an increment of 2 ) and ( 2 to 100 with an increment of 2 )
In the simplest case, you can try looping in fromNumber .. toNumber range while adding
number either to even or to odd sum:
// long : since sum of int's can be large (beyond int.MaxValue) let's use long
long evenSum = 0;
long oddSum = 0;
for (int number = fromNumber; number <= toNumber; ++number) {
if (number % 2 == 0)
evenSum += number;
else
oddSum += number;
}
Console.WriteLine($"The Sum of Values from {fromNumber} till {toNumber}");
Console.WriteLine($"is {evenSum + oddSum}: {evenSum} (even) + {oddSum} (odd).");
Note, that you can compute both sums in one go with a help of arithmetics progression:
private static (long even, long odd) ComputeSums(long from, long to) {
if (to < from)
return (0, 0); // Or throw ArgumentOutOfRangeException
long total = (to + from) * (to - from + 1) / 2;
from = from / 2 * 2 + 1;
to = (to + 1) / 2 * 2 - 1;
long odd = (to + from) / 2 * ((to - from) / 2 + 1);
return (total - odd, odd);
}
Then
(long evenSum, long oddSum) = ComputeSums(fromNumber, toNumber);
Console.WriteLine($"The Sum of Values from {fromNumber} till {toNumber}");
Console.WriteLine($"is {evenSum + oddSum}: {evenSum} (even) + {oddSum} (odd).");
From the code snippet you shared, it seems like the user gives the range on which the sum is calculated. Adding to #vivek-nuna's answer,
Let's say the sum of the first N odd numbers is given by, f(n) = n^2 and
the sum of the first N even numbers is given by, g(n) = n(n + 1).
So the sum of odd numbers from (l, r) = f(r) - f(l - 1).
Similarly, the sum of even numbers from (l, r) = g(r) - g(l - 1).
Hope this helps.

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.

Finding the closest integer value, rounded down, from a given array of integers

I am trying to figure out the best way to find the closest value, ROUNDED DOWN, in a List of integers using any n that is between two other numbers that are stored in a List. The all integers in this situation will ALWAYS be unsigned, in case that helps.
The assumptions are as follows:
The List always starts at 0
The List is always sorted ASC
All integers in the List are unsigned (no need for Math.Abs)
The number for comparison is always unsigned
For example:
List<int> numbers = new List<int>() { 0, 2000, 4000, 8000, 8500, 9101, 10010 };
int myNumber = 9000;
int theAnswer; // should be 8500
for (int i = 0; i < numbers.Count; i++) {
if (i == numbers.Count - 1) {
theAnswer = numbers[i];
break;
} else if (myNumber < numbers[i + 1]) {
theAnswer = numbers[i];
break;
}
}
The previous code example works without any flaws.
Is there a better more succint way to do it?
You can use List<T>.BinarySearch instead of enumerating elements of list in sequence.
List<int> numbers = new List<int>() { 0, 2000, 4000, 8000, 8500, 9101, 10010 };
int myNumber = 9000;
int r=numbers.BinarySearch(myNumber);
int theAnswer=numbers[r>=0?r:~r-1];
Filter list obtaining all values less than the myNumber and return last one:
theAnswer = numbers.Where(x => x <= myNumber ).Last();
A list can be indexed.
Start at the index in the middle of the list. If you found the exact number, you are good. If the number is less than the target number, search in the middle of the range from the start of the list to one less than the middle of the list. If the number is greater than the target number, work with the opposite half of the list. Continue this binary search until you find either an exact match, or the adjacent numbers that are smaller and larger than the target number.
Select the smaller of the two.
Please try this code:
List<int> numbers = new List<int>() { 0, 2000, 4000, 8000, 8500, 9101, 10010 };
int myNumber = 9000;
int theAnswer = numbers[numbers.Count - 1];
if (theAnswer > myNumber)
{
int l = 0, h = numbers.Count - 1, m;
do
{
m = (int)((double)(myNumber - numbers[l]) / (double)(numbers[h] - numbers[l]) * (h - l) + l);
if (numbers[m] > myNumber) h = m; else l = m;
}
while ((h - l) != 1);
theAnswer = numbers[l];
}

Find the contiguous sequence with the largest product in an integer array

I have come up with the code below but that doesn't satisfy all cases, e.g.:
Array consisting all 0's
Array having negative values(it's bit tricky since it's about finding product as two negative ints give positive value)
public static int LargestProduct(int[] arr)
{
//returning arr[0] if it has only one element
if (arr.Length == 1) return arr[0];
int product = 1;
int maxProduct = Int32.MinValue;
for (int i = 0; i < arr.Length; i++)
{
//this block store the largest product so far when it finds 0
if (arr[i] == 0)
{
if (maxProduct < product)
{
maxProduct = product;
}
product = 1;
}
else
{
product *= arr[i];
}
}
if (maxProduct > product)
return maxProduct;
else
return product;
}
How can I incorporate the above cases/correct the code. Please suggest.
I am basing my answer on the assumption that if you have more than 1 element in the array, you would want to multiply at least 2 contiguous integers for checking the output, i.e. in array of {-1, 15}, the output that you want is -15 and not 15).
The problem that we need to solve is to look at all possible multiplication combinations and find out the max product out of them.
The total number of products in an array of n integers would be nC2 i.e. if there are 2 elements, then the total multiplication combinations would be 1, for 3, it would be 3, for 4, it would be 6 and so on.
For each number that we have in the incoming array, it has to multiply with all the multiplications that we did with the last element and keep the max product till now and if we do it for all the elements, at the end we would be left with the maximum product.
This should work for negatives and zeros.
public static long LargestProduct(int[] arr)
{
if (arr.Length == 1)
return arr[0];
int lastNumber = 1;
List<long> latestProducts = new List<long>();
long maxProduct = Int64.MinValue;
for (int i = 0; i < arr.Length; i++)
{
var item = arr[i];
var latest = lastNumber * item;
var temp = new long[latestProducts.Count];
latestProducts.CopyTo(temp);
latestProducts.Clear();
foreach (var p in temp)
{
var product = p * item;
if (product > maxProduct)
maxProduct = product;
latestProducts.Add(product);
}
if (i != 0)
{
if (latest > maxProduct)
maxProduct = latest;
latestProducts.Add(latest);
}
lastNumber = item;
}
return maxProduct;
}
If you want the maximum product to also incorporate the single element present in the array i.e. {-1, 15} should written 15, then you can compare the max product with the element of the array being processed and that should give you the max product if the single element is the max number.
This can be achieved by adding the following code inside the for loop at the end.
if (item > maxProduct)
maxProduct = item;
Your basic problem is 2 parts. Break them down and solving it becomes easier.
1) Find all contiguous subsets.
Since your source sequence can have negative values, you are not all that equipped to make any value judgments until you're found each subset, as a negative can later be "cancelled" by another. So let the first phase be to only find the subsets.
An example of how you might do this is the following code
// will contain all contiguous subsets
var sequences = new List<Tuple<bool, List<int>>>();
// build subsets
foreach (int item in source)
{
var deadCopies = new List<Tuple<bool, List<int>>>();
foreach (var record in sequences.Where(r => r.Item1 && !r.Item2.Contains(0)))
{
// make a copy that is "dead"
var deadCopy = new Tuple<bool, List<int>>(false, record.Item2.ToList());
deadCopies.Add(deadCopy);
record.Item2.Add(item);
}
sequences.Add(new Tuple<bool, List<int>>(true, new List<int> { item }));
sequences.AddRange(deadCopies);
}
In the above code, I'm building all my contiguous subsets, while taking the liberty of not adding anything to a given subset that already has a 0 value. You can omit that particular behavior if you wish.
2) Calculate each subset's product and compare that to a max value.
Once you have found all of your qualifying subsets, the next part is easy.
// find subset with highest product
int maxProduct = int.MinValue;
IEnumerable<int> maxSequence = Enumerable.Empty<int>();
foreach (var record in sequences)
{
int product = record.Item2.Aggregate((a, b) => a * b);
if (product > maxProduct)
{
maxProduct = product;
maxSequence = record.Item2;
}
}
Add whatever logic you wish to restrict the length of the original source or the subset candidates or product values. For example, if you wish to enforce minimum length requirements on either, or if a subset product of 0 is allowed if a non-zero product is available.
Also, I make no claims as to the performance of the code, it is merely to illustrate breaking the problem down into its parts.
I think you should have 2 products at the same time - they will differ in signs.
About case, when all values are zero - you can check at the end if maxProduct is still Int32.MinValue (if Int32.MinValue is really not possible)
My variant:
int maxProduct = Int32.MinValue;
int? productWithPositiveStart = null;
int? productWithNegativeStart = null;
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == 0)
{
productWithPositiveStart = null;
productWithNegativeStart = null;
}
else
{
if (arr[i] > 0 && productWithPositiveStart == null)
{
productWithPositiveStart = arr[i];
}
else if (productWithPositiveStart != null)
{
productWithPositiveStart *= arr[i];
maxProduct = Math.max(maxProduct, productWithPositiveStart);
}
if (arr[i] < 0 && productWithNegativeStart == null)
{
productWithNegativeStart = arr[i];
}
else if (productWithNegativeStart != null)
{
productWithNegativeStart *= arr[i];
maxProduct = Math.max(maxProduct, productWithNegativeStart);
}
maxProduct = Math.max(arr[i], maxProduct);
}
}
if (maxProduct == Int32.MinValue)
{
maxProduct = 0;
}
At a high level, your current algorithm splits the array upon a 0 and returns the largest contiguous product of these sub-arrays. Any further iterations will be on the process of finding the largest contiguous product of a sub-array where no elements are 0.
To take into account negative numbers, we obviously first need to test if the product of one of these sub-arrays is negative, and take some special action if it is.
The negative result comes from an odd number of negative values, so we need to remove one of these negative values to make the result positive again. To do this we remove all elements up the the first negative number, or the last negative number and all elements after that, whichever results in the highest product.
To take into account an array of all 0's, simply use 0 as your starting maxProduct. If the array is a single negative value, you're special case handling of a single element will mean that is returned. After that, there will always be a positive sub-sequence product, or else the whole array is 0 and it should return 0 anyway.
it can be done in O(N). it is based on the simple idea: calculate the minimum (minCurrent) and maximum (maxCurrent) till i. This can be easily changed to fit for the condition like: {0,0,-2,0} or {-2,-3, -8} or {0,0}
a[] = {6, -3, 2, 0, 3, -2, -4, -2, 4, 5}
steps of the algorithm given below for the above array a :
private static int getMaxProduct(int[] a) {
if (a.length == 0) {
throw new IllegalArgumentException();
}
int minCurrent = 1, maxCurrent = 1, max = Integer.MIN_VALUE;
for (int current : a) {
if (current > 0) {
maxCurrent = maxCurrent * current;
minCurrent = Math.min(minCurrent * current, 1);
} else if (current == 0) {
maxCurrent = 1;
minCurrent = 1;
} else {
int x = maxCurrent;
maxCurrent = Math.max(minCurrent * current, 1);
minCurrent = x * current;
}
if (max < maxCurrent) {
max = maxCurrent;
}
}
//System.out.println(minCurrent);
return max;
}

perfect side combination of right triangle

I want to get a list of: Sides of Right Triangle
which are perfectly whole numbers.(where each sides less than 100)
Example:
//I want these combination to be printed
3, 4, 5
6, 8, 10 |'.
5, 12, 13 12 | '. 13 (Figure is just Example)
. | '.
. |______'.
. 5
// I don't want these
1, 1, 1.414.... |'.
. 1 | '. √ˉ2 = 1.414.... (Figure is just Example)
. | '.
|______'.
1
Update:
I do like this: But this is very heavy code(regarding optimization)
for(int i=1;i<100;i++)
{
for(int j=1;j<100;j++)
{
for(int k=1;k<100;k++)
{
if(i*i + j*j == k*k)
{
//print i, j, k
}
}
}
}
What you're looking for are the Pythagorean triples.
// Obvious min is 1, obvious max is 99.
for(int i = 1; i != 100; ++i)
{
// There's no point going beyond the lowest number that gives an answer higher than 100
int max = 100 * 100 - i * i;
// There's no point starting lower than our current first side, or we'll repeat results we already found.
for(int j = i; j * j <= max; ++j)
{
// Find the square of the hypotenuse
int sqr = i * i + j * j;
// We could have a double and do hyp == Math.Round(hyp), but lets avoid rounding error-based false positives.
int hyp = (int)Math.Sqrt(sqr);
if(hyp * hyp == sqr)
{
Console.WriteLine(i + ", " + j + ", " + hyp);
// If we want to e.g. have not just "3, 4, 5" but also "4, 3, 5", then
// we can also here do
// Console.WriteLine(j + ", " + i + ", " + hyp);
}
}
}
I've used this formula in C# for generating Pythagorean triples in the past. But there are many other options on that page.
You can improve your code by removing the innermost loop if you take advantage of the fact that for each pair of catheti, there is only one possible value for the hypotenuse. Instead of looping around to find that value, you can compute it using the Pythagorean theorem and test if it is an whole number.
Something like:
// compute the hypotenuse
var hypotenuse = Math.Sqrt(i*i + j*j);
// test if the hypotenuse is a whole number < 100
if(hypotenuse < 100 && hypotenuse == (int)hypotenuse)
{
// here's one!
}
Some other improvements you can do include:
Once you've checked a pair of catheti (x,y), don't check for (y,x) again;
Once you find a triangle (x,y,z), you can include all triangles with the same sides multiplied by a constant factor (k*x, k*y, k*z), i.e, if you find (3,4,5) you can include (6,8,10), (9,12,15), (12,16,20), etc (this one might be a too much effort for little gains);
A fairly good exhaustive search:
for(i=1;i<100;i++) {
k=i;
for(j=1;k<100;j++) {
while(i*i+j*j<k*k) {
k++;
}
if(i*i+j*j==k*k) {
printf("%d %d %d", i, j, k);
}
}
}
In a declarative language (Mathematica):
FindInstance[x^2 + y^2==z^2 &&1<=z<=100 && 1<=y<=x<=100, {x, y, z}, Integers,100]

Categories