Compare value with array and get closest value to it - c#

I'm a rookie in C# and I'm trying to learn that language.
Can you guys give me a tip how I can compare an array with a value picking the lowest from it?
like:
Double[] w = { 1000, 2000, 3000, 4000, 5000 };
double min = double.MaxValue;
double max = double.MinValue;
foreach (double value in w)
{
if (value < min)
min = value;
if (value > max)
max = value;
}
Console.WriteLine(" min:", min);
gives me the lowest value of w, how can I compare now?
If I have:
int p = 1001 + 2000; // 3001
how can I compare now with the list of the array and find out that the (3000) value is the nearest value to my "Searchvalue"?

You can do this with some simple mathematics and there are different approaches.
LINQ
Double searchValue = ...;
Double nearest = w.Select(p => new { Value = p, Difference = Math.Abs(p - searchValue) })
.OrderBy(p => p.Difference)
.First().Value;
Manually
Double[] w = { 1000, 2000, 3000, 4000, 5000 };
Double searchValue = 3001;
Double currentNearest = w[0];
Double currentDifference = Math.Abs(currentNearest - searchValue);
for (int i = 1; i < w.Length; i++)
{
Double diff = Math.Abs(w[i] - searchValue);
if (diff < currentDifference)
{
currentDifference = diff;
currentNearest = w[i];
}
}

Double[] w = { 1000, 2000, 3000, 4000, 5000 };
var minimumValueFromArray = w.Min();
produces
1000, as expected, cause we execute Enumerable.Min.
The same is for Enumerable.Max, to figure out the max value:
Double[] w = { 1000, 2000, 3000, 4000, 5000 };
var maximumValueFromArray = w.Max();
Considering that you're comparing with the double.MinValue and double.MaxValue, I would assume that you want just pick the smallest and biggest value from array.
If this is not what you're searching for, please clarify.

based on your code, you can achieve this in a very easy way
Double[] w = { 1000, 2000, 3000, 4000, 5000 }; // it has to be sorted
double search = 3001;
double lowerClosest = 0;
double upperClosest = 0;
for (int i = 1; i < w.Length; i++)
{
if (w[i] > search)
{
upperClosest = w[i];
break; // interrupts your foreach
}
}
for (int i = w.Length-1; i >=0; i--)
{
if (w[i] <= search)
{
lowerClosest = w[i];
break; // interrupts your foreach
}
}
Console.WriteLine(" lowerClosest:{0}", lowerClosest);
Console.WriteLine(" upperClosest:{0}", upperClosest);
if (upperClosest - search > search - lowerClosest)
Console.WriteLine(" Closest:{0}", lowerClosest);
else
Console.WriteLine(" Closest:{0}", upperClosest);
Console.ReadLine();
depending on the position of your search value this will be less than O(n)

Performance wise custom code will be more use full.
List<int> results;
int targetNumber = 0;
int nearestValue=0;
if (results.Any(ab => ab == targetNumber ))
{
nearestValue= results.FirstOrDefault<int>(i => i == targetNumber );
}
else
{
int greaterThanTarget = 0;
int lessThanTarget = 0;
if (results.Any(ab => ab > targetNumber ))
{
greaterThanTarget = results.Where<int>(i => i > targetNumber ).Min();
}
if (results.Any(ab => ab < targetNumber ))
{
lessThanTarget = results.Where<int>(i => i < targetNumber ).Max();
}
if (lessThanTarget == 0 )
{
nearestValue= greaterThanTarget;
}
else if (greaterThanTarget == 0)
{
nearestValue= lessThanTarget;
}
else if (targetNumber - lessThanTarget < greaterThanTarget - targetNumber )
{
nearestValue= lessThanTarget;
}
else
{
nearestValue= greaterThanTarget;
}
}

Related

Get next highest number in an ObservableCollection column [duplicate]

Tried to googled it but with no luck.
How can I find the second maximum number in an array with the smallest complexity?
code OR idea will be much help.
I can loop through an array and look for the maximum number
after that, I have the maximum number and then loop the array again to find the second the same way.
But for sure it is not efficient.
You could sort the array and choose the item at the second index, but the following O(n) loop will be much faster.
int[] myArray = new int[] { 0, 1, 2, 3, 13, 8, 5 };
int largest = int.MinValue;
int second = int.MinValue;
foreach (int i in myArray)
{
if (i > largest)
{
second = largest;
largest = i;
}
else if (i > second)
second = i;
}
System.Console.WriteLine(second);
OR
Try this (using LINQ):
int secondHighest = (from number in test
orderby number descending
select number).Distinct().Skip(1).First()
How to get the second highest number in an array in Visual C#?
Answer in C# :
static void Main(string[] args)
{
//let us define array and vars
var arr = new int[]{ 100, -3, 95,100,95, 177,-5,-4,177,101 };
int biggest =0, secondBiggest=0;
for (int i = 0; i < arr.Length; ++i)
{
int arrItem = arr[i];
if(arrItem > biggest)
{
secondBiggest = biggest; //always store the prev value of biggest
//to second biggest...
biggest = arrItem;
}
else if (arrItem > secondBiggest && arrItem < biggest) //if in our
//iteration we will find a number that is bigger than secondBiggest and smaller than biggest
secondBiggest = arrItem;
}
Console.WriteLine($"Biggest Number:{biggest}, SecondBiggestNumber:
{secondBiggest}");
Console.ReadLine(); //make program wait
}
Output : Biggest Number:177, SecondBiggestNumber:101
public static int F(int[] array)
{
array = array.OrderByDescending(c => c).Distinct().ToArray();
switch (array.Count())
{
case 0:
return -1;
case 1:
return array[0];
}
return array[1];
}
static void Main(string[] args)
{
int[] myArray = new int[] { 0, 11, 2, 15, 16, 8, 16 ,8,15};
int Smallest = myArray.Min();
int Largest = myArray.Max();
foreach (int i in myArray)
{
if(i>Smallest && i<Largest)
{
Smallest=i;
}
}
System.Console.WriteLine(Smallest);
Console.ReadLine();
}
This will work even if you have reputation of items in an array
int[] arr = {-10, -3, -3, -6};
int h = int.MinValue, m = int.MinValue;
foreach (var t in arr)
{
if (t == h || t == m)
continue;
if (t > h)
{
m = h;
h = t;
}
else if(t > m )
{
m = t;
}
}
Console.WriteLine("High: {0} 2nd High: {1}", h, m);
//or,
m = arr.OrderByDescending(i => i).Distinct().Skip(1).First();
Console.WriteLine("High: {0} 2nd High: {1}", h, m);
/* we can use recursion */
var counter = 0;
findSecondMax = (arr)=> {
let max = Math.max(...arr);
counter++;
return counter == 1 ? findSecondMax(arr.slice(0,arr.indexOf(max)).concat(arr.slice(arr.indexOf(max)+1))) : max;
}
console.log(findSecondMax([1,5,2,3,0]))
static void Main(string[] args){
int[] arr = new int[5];
int i, j,k;
Console.WriteLine("Enter Array");
for (i = 0; i < 5; i++) {
Console.Write("element - {0} : ", i);
arr[i] = Convert.ToInt32(Console.ReadLine());
}
Console.Write("\nElements in array are: ");
j=arr[0];
k=j;
for (i = 1; i < 5; i++) {
if (j < arr[i])
{
if(j>k)
{
k=j;
}
j=arr[i];
}
}
Console.WriteLine("First Greatest element: "+ j);
Console.WriteLine("Second Greatest element: "+ k);
Console.Write("\n");
}
int max = 0;
int secondmax = 0;
int[] arr = { 2, 11, 15, 1, 7, 99, 6, 85, 4 };
for (int r = 0; r < arr.Length; r++)
{
if (max < arr[r])
{
max = arr[r];
}
}
for (int r = 0; r < arr.Length; r++)
{
if (secondmax < arr[r] && arr[r] < max)
{
secondmax = arr[r];
}
}
Console.WriteLine(max);
Console.WriteLine(secondmax);
Console.Read();
Python 36>=
def sec_max(array: list) -> int:
_max_: int = max(array)
second: int = 0
for element in array:
if second < element < _max_:
second = element
else:
continue
return second
Using below code we can find out second highest number, even array contains multiple max numbers
// int[] myArray = { 25, 25, 5, 20, 50, 23, 10 };
public static int GetSecondHighestNumberForUniqueNumbers(int[] numbers)
{
int highestNumber = 0, Seconhight = 0;
List<int> numberList = new List<int>();
for (int i = 0; i < numbers.Length; i++)
{
//For loop should move forward only for unique items
if (numberList.Contains(numbers[i]))
continue;
else
numberList.Add(numbers[i]);
//find higest number
if (highestNumber < numbers[i])
{
Seconhight = highestNumber;
highestNumber = numbers[i];
} //find second highest number
else if (Seconhight < numbers[i])
{
Seconhight = numbers[i];
}
}
It's not like that your structure is a tree...It's just a simple array, right?
The best solution is to sort the array. And depending on descending or ascending, display the second or the 2nd last element respectively.
The other alternative is to use some inbuilt methods, to get the initial max. Pop that element, and then search for the max again. Don't know C#, so can't give the direct code.
You'd want to sort the numbers, then just take the second largest. Here's a snippet without any consideration of efficiency:
var numbers = new int[] { 3, 5, 1, 5, 4 };
var result=numbers.OrderByDescending(x=>x).Distinct().Skip(1).First();
This isn't too bad:
int[] myArray = new int[] { 0, 1, 2, 3, 13, 8, 5 };
var secondMax =
myArray.Skip(2).Aggregate(
myArray.Take(2).OrderByDescending(x => x).AsEnumerable(),
(a, x) => a.Concat(new [] { x }).OrderByDescending(y => y).Take(2))
.Skip(1)
.First();
It's fairly low on complexity as it only every sorts a maximum of three elements
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int size;
Console.WriteLine("Enter the size of array");
size = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter the element of array");
int[] arr = new int[size];
for (int i = 0; i < size; i++)
{
arr[i] = Convert.ToInt32(Console.ReadLine());
}
int length = arr.Length;
Program program = new Program();
program.SeconadLargestValue(arr, length);
}
private void SeconadLargestValue(int[] arr, int length)
{
int maxValue = 0;
int secondMaxValue = 0;
for (int i = 0; i < length; i++)
{
if (arr[i] > maxValue)
{
secondMaxValue = maxValue;
maxValue = arr[i];
}
else if(arr[i] > secondMaxValue)
{
secondMaxValue = arr[i];
}
}
Console.WriteLine("First Largest number :"+maxValue);
Console.WriteLine("Second Largest number :"+secondMaxValue);
Console.ReadLine();
}
}
}
My solution below.
class Program
{
static void Main(string[] args)
{
Program pg = new Program();
Console.WriteLine("*****************************Program to Find 2nd Highest and 2nd lowest from set of values.**************************");
Console.WriteLine("Please enter the comma seperated numbers : ");
string[] val = Console.ReadLine().Split(',');
int[] inval = Array.ConvertAll(val, int.Parse); // Converts Array from one type to other in single line or Following line
// val.Select(int.Parse)
Array.Sort(inval);
Console.WriteLine("2nd Highest is : {0} \n 2nd Lowest is : {1}", pg.Return2ndHighest(inval), pg.Return2ndLowest(inval));
Console.ReadLine();
}
//Method to get the 2nd lowest and 2nd highest from list of integers ex 1000,20,-10,40,100,200,400
public int Return2ndHighest(int[] values)
{
if (values.Length >= 2)
return values[values.Length - 2];
else
return values[0];
}
public int Return2ndLowest(int[] values)
{
if (values.Length > 2)
return values[1];
else
return values[0];
}
}
I am giving solution that's in JavaScript, it takes o(n/2) complexity to find the highest and second highest number.
here is the working Fiddler Link
var num=[1020215,2000,35,2,54546,456,2,2345,24,545,132,5469,25653,0,2315648978523];
var j=num.length-1;
var firstHighest=0,seoncdHighest=0;
num[0] >num[num.length-1]?(firstHighest=num[0],seoncdHighest=num[num.length-1]):(firstHighest=num[num.length-1], seoncdHighest=num[0]);
j--;
for(var i=1;i<=num.length/2;i++,j--)
{
if(num[i] < num[j] )
{
if(firstHighest < num[j]){
seoncdHighest=firstHighest;
firstHighest= num[j];
}
else if(seoncdHighest < num[j] ) {
seoncdHighest= num[j];
}
}
else {
if(firstHighest < num[i])
{
seoncdHighest=firstHighest;
firstHighest= num[i];
}
else if(seoncdHighest < num[i] ) {
seoncdHighest= num[i];
}
}
}
Sort the array and take the second to last value?
var result = (from elements in inputElements
orderby elements descending
select elements).Distinct().Skip(1).Take(1);
return result.FirstOrDefault();
namespace FindSecondLargestNumber
{
class Program
{
static void Main(string[] args)
{
int max=0;
int smax=0;
int i;
int[] a = new int[20];
Console.WriteLine("enter the size of the array");
int n = int.Parse(Console.ReadLine());
Console.WriteLine("elements");
for (i = 0; i < n; i++)
{
a[i] = int.Parse(Console.ReadLine());
}
for (i = 0; i < n; i++)
{
if ( a[i]>max)
{
smax = max;
max= a[i];
}
else if(a[i]>smax)
{
smax=a[i];
}
}
Console.WriteLine("max:" + max);
Console.WriteLine("second max:"+smax);
Console.ReadLine();
}
}
}

Does this minmax function has a good efficiency?

To be requested 10 numbers from the user find the min and max number without using arrays. Strings should not be count and will give an error, however program will continue to calculate the min and max if some of the value has provided.
I solved the question but what I'm wondering to know if it is a good solution.
public static void MinMax()
{
double minSayi = 0;
double maxSayi = 0;
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Sayı Giriniz: ");
bool isNumber = Double.TryParse(Console.ReadLine(), out double sayi);
if (isNumber)
{
if (i == 0)
{
minSayi = sayi;
maxSayi = sayi;
}
else
{
if (sayi < minSayi)
{
minSayi = sayi;
}
if (sayi > maxSayi)
{
maxSayi = sayi;
}
}
}
else
{
Console.WriteLine("Gecersiz sayi girdiniz.");
}
}
Console.WriteLine("*********************");
Console.WriteLine($"Min sayi: {minSayi}");
Console.WriteLine($"Max sayi: {maxSayi}");
Console.ReadLine();
}
I expect the output of (1, ... 10) => min = 1; max = 10;

Forcing one more iteration of loop?

int iterationMax = 1;
double actualMax = 0;
int A = 3;
List<double> tmp = new List<double> { 400, 0, -300, 400, 600 ,300, 400,
1200, 900, 400, 1200, 1500};
List<double> listMax = new List<double>();
for (int i = 0; i < tmp.Count; i++)
{
if (iterationMax < A) // A == 3
{
if (tmp[i] > actualMax)
{
actualMax = tmp[i];
}
iterationMax++;
}
else
{
listMax.Add(actualMax);
actualMax = 0;
iterationMax = 1;
}
}
Console.WriteLine("\nMaxs: ");
foreach (double max in listMax)
{
Console.Write(max + ", ");
}
List tmp holds = { 400,0,-300|400,600,300|400,1200,900|400,1200,1500},
Produce 400, 600, 1200, 1200,
Should be 400, 600, 1200, 1500. I don't know how to enter else statement to add 1500 to list.
I just want to get max from every 3 elements.
When one needs to perform manipulations on collection it is many times nicer to use Linq.
Use GroupBy in the index/3 and as it is an int each 3 following items will have a different key. Then for each group select the maximum value:
var items = new int[] { 400, 0, -300, 400, 600, 300, 400, 1200, 900 };
var results = items.Select((item, index) => new { item, index })
.GroupBy(item => item.index / 3)
.Select(group => group.Max(item => item.item));
//400, 600, 1200
A quick fix for your code would be:
var A = 3;
int iterationMax = 0;
double actualMax = 0;
List<double> tmp = new List<double> {400,0,-300,400,600,300,400,1200,900,400,1200,1500};
List<double> listMax = new List<double>();
for (int i = 0; i < tmp.Count; i++)
{
if (iterationMax < A) // A == 3
{
if (tmp[i] > actualMax)
{
actualMax = tmp[i];
}
iterationMax++;
}
if (iterationMax == A)
{
listMax.Add(actualMax);
actualMax = 0;
iterationMax = 0;
}
}
Console.WriteLine("\nMaxs: ");
foreach (double max in listMax)
{
Console.Write(max + ", ");
}
As others have said, start iterationMax from 0 and turn that else into an if (iterationMax == A).
Setting iterationMax to 0 in initialization and under else should solve your issue.
Currently your if structure will only check the first two out of three elements. Since 1500 is element #3, it will not be checked.
The problem is, when iterationMax reaches 3, you don't do anything with the value in temp, that loop is lost.
for (int i = 0; i < tmp.Count; i++)
{
if (tmp[i] > actualMax)
{
actualMax = tmp[i];
}
iterationMax++;
if (iterationMax > A)
{
listMax.Add(actualMax);
actualMax = 0;
iterationMax = 1;
}
}

How to reverse a number and compare it with its original value [duplicate]

This question already has answers here:
How do I check if a number is a palindrome?
(53 answers)
Closed 8 years ago.
I'm trying to multiply two numbers and then reverse the answer and then compare the original value with the reversed value to see if they are the same numbers. After I reverse the value how do I set it back to an int or a string so I can compare them?
int i = 0;
var x = Enumerable.Range(100,999);
var y = Enumerable.Range(100,999);
foreach (var xValue in x)
{
foreach (var yValue in y)
{
i = (xValue * yValue);
var t = i.ToString();
var tempt = t.Reverse();
var temp = new string(tempt);
if (i.ToString() == temp)
{
}
}
}
Like this perhaps? (One shouldn't really give away answers to projecteuler problems, but this is only problem 4, so what the heck)
public int SolveProblem004()
{
int result = 0;
for (int a = 999; a >= 100; --a) {
for (int b = 999; b >= a; --b) {
int product = a * b;
if (product <= result) break;
if (IsPalindromic(product)) { result = product; break; }
}
}
return result;
}
public static bool IsPalindromic(int i)
{
return i == Reverse(i);
}
public static int Reverse(int number)
{
if (number < 0) return -Reverse(-number);
if (number < 10) return number;
int reverse = 0;
while (number > 0) {
reverse = reverse * 10 + number % 10;
number /= 10;
}
return reverse;
}
int n; //given number
int left = n;
int rev = 0;
while(left>0)
{
r = left % 10; //take the last number
rev = rev * 10 + r; //add it to a new number to flip it
left = left / 10; //left = Math.floor(left / 10);
}
You can do it this way:
int i = 0;
var x = Enumerable.Range(100, 999);
var y = Enumerable.Range(100, 999);
foreach (var xValue in x)
{
foreach (var yValue in y)
{
i = (xValue * yValue);
char[] number = i.ToString().ToCharArray();
char[] reversedNumber = i.ToString().ToCharArray();
Array.Reverse(reversedNumber);
if (new string(number).Equals(new string(reversedNumber)))
{
}
}
}
It will enter inside if loop if number is the same as reversed number and will just pass by in oposite case.

Modified Knapsack/Subset-Sum with same weight/values

I was working on a problem that has to deal with a special case of the Knapsack/Subset-Sum Problem. The problem is as follows:
You have a set of bundle sizes in decreasing sizes that are random like: {47, 35, 22, ...}. You have value that is the quantity of widgets like: #widgets = 33.
Find the least number of bundles that can make up the number of widgets for the bundle. If there is no way to return set that equals quantities then return null.
Example 1:
Bundle Sizes: 46, 25, 12, 4, 3
quantity: 30
Returned Value: {46:0, 25:0, 12:2, 4:0, 3:2} (bundle size:# of bundles)
Example 2:
Bundle Sizes: 46, 25, 12, 4, 3
quantity: 17
Returned Value: {46:0, 25:0, 12:0, 4:2, 3:3}
Example 3:
Bundle Sizes: 46, 25, 12, 4, 3
quantity: 47
Returned Value: {46:0, 25:1, 12:1, 4:1, 3:2}
Example 4:
Bundle Sizes: 46, 25, 12, 4, 3
quantity: 5
Returned Value: null
How would go about this problem?
I have written a program in C# that does close enough job but resets an index in a for loop to dump bundle sizes that will not work with the rest of the set. Will post source if requested for how far it goes.
Requested code:
public List<int> BreakDown(List<int> bunSize, int buySize)
{
List<int> tempBunSize = bunSize.ToList();
tempBunSize.RemoveAll(e => e > buySize);
List<int> ammountNeeded = new List<int>();
int result = buySize;
for (int index = 0; index < tempBunSize.Count; index++)
{
int size = tempBunSize[index];
int tempResult = 0;
double calcResult = 0;
int addAmmount = 0;
if (index == tempBunSize.Count - 2)
{
tempResult = result % size;
if ((tempBunSize.Count > 2 || result % tempBunSize[tempBunSize.Count - 1] == 0))
{
if (result % size != 0)
{
ammountNeeded.Add(0);
tempResult = result % tempBunSize[tempBunSize.Count - 1];
if (tempResult != 0)
{
tempResult = result;
int sizeInc = 0;
while (tempResult != 0)
{
tempResult = tempResult - size;
sizeInc++;
if (tempResult < 0)
{
int decreaseOne = ammountNeeded.First();
ammountNeeded[0] = --decreaseOne;
result = result + tempBunSize.First();
if (ammountNeeded[0] <= 0)
{
tempBunSize.RemoveAt(0);
index = 0;
ammountNeeded = new List<int>();
}
ammountNeeded.Remove(0);
--index;
break;
}
if (tempResult % tempBunSize[tempBunSize.Count - 1] == 0)
{
ammountNeeded.Remove(0);
ammountNeeded.Add(sizeInc);
ammountNeeded.Add(tempResult / tempBunSize[tempBunSize.Count - 1]);
break;
}
}
if (tempResult < 0) continue;
break;
}
else
{
calcResult = result / tempBunSize[tempBunSize.Count - 1];
addAmmount = (int)Math.Floor(calcResult);
ammountNeeded.Add(addAmmount);
break;
}
}
else if (result % size == 0)
{
tempResult = result % size;
if (tempResult != 0 && result % tempBunSize[tempBunSize.Count - 1] != 0)
{
int decreaseOne = ammountNeeded.First();
ammountNeeded.Insert(0, --decreaseOne);
result = result + tempBunSize.First();
continue;
}
else
{
calcResult = result / size;
addAmmount = (int)Math.Floor(calcResult);
ammountNeeded.Add(addAmmount);
if (result % size == 0)
{
ammountNeeded.Add(0);
break;
}
calcResult = result / tempBunSize[tempBunSize.Count - 1];
addAmmount = (int)Math.Floor(calcResult);
ammountNeeded.Add(addAmmount);
break;
}
}
}
else if (tempResult % tempBunSize[tempBunSize.Count - 1] != 0)
{
tempResult = result;
int sizeInc = 0;
while (tempResult != 0)
{
tempResult = tempResult - size;
sizeInc++;
if (tempResult % tempBunSize[tempBunSize.Count - 1] == 0)
{
ammountNeeded.Add(sizeInc);
ammountNeeded.Add(tempResult / tempBunSize[tempBunSize.Count - 1]);
break;
}
}
break;
}
else if (result == 0)
{
while (ammountNeeded.Count < bunSize.Count)
{
ammountNeeded.Add(0);
}
break;
}
else
{
calcResult = result / size;
ammountNeeded.Add((int)Math.Floor(calcResult));
calcResult = tempResult / tempBunSize[tempBunSize.Count - 1];
ammountNeeded.Add((int)Math.Floor(calcResult));
break;
}
}
ammountNeeded.Add((int)Math.Floor((decimal)result / size));
result = result % size;
if (result == 0) continue;
}
if (ammountNeeded.Count < bunSize.Count)
{
ammountNeeded.Reverse(0, ammountNeeded.Count);
while (ammountNeeded.Count < bunSize.Count)
{
ammountNeeded.Add(0);
}
ammountNeeded.Reverse(0, ammountNeeded.Count);
}
if (ammountNeeded.FindLast(e => e < 0) < 0 || ammountNeeded.Sum() == 0)
return null;
return ammountNeeded;
}
}
This was a FUN problem to solve. Geek points all around.
Your main problem I believe is in trying to loop through a single list. Really what you want here is to test all variations of the list then find the one with the highest values.
Also, as is stated in the comments, recursion is your friend here. I recursed through each permutation of the bundle amounts.
One problem that your data has is that of your 17 example. The math used in this example is greedy. Meaning, 4 is going to try to get as many as it can before it hands off the remainder to 3. 4 therefore gets 4 bundles and with 1 remainder a null is returned. For this reason I think the correct answer to 17 should be null. You might be able to figure out how to balance between numbers, but that'll be a whole lot more logic IMO.
Here is the code:
public class test
{
public static void Main()
{
var bundleSizes = new List<int> { 46, 25, 12, 4, 3 };
var quantity = 30;
var bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 17;
bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 47;
bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 5;
bundleResults = Begin(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
Console.Read();
}
static void Output(List<int> bundleSizes, int quantity, Dictionary<int, int> bundleResults)
{
var fullResults = new Dictionary<int, int>();
bundleSizes.ForEach(
delegate(int e)
{
var result = 0;
if(bundleResults != null)
bundleResults.TryGetValue(e, out result);
fullResults.Add(e, result);
});
Console.WriteLine("Bundle Sizes: {0}", string.Join(",", bundleSizes));
Console.WriteLine("Quantity: {0}", quantity);
Console.WriteLine("Returned Value: {0}", bundleResults == null ? "null" : fullResults.Aggregate("", (keyString, pair) => keyString + pair.Key + ":" + pair.Value + ","));
}
static Dictionary<int, int> Begin(List<int> bundleSizes, int quantity)
{
var bundleCombinations = GetCombination(bundleSizes);
var tempBundleSizes = new List<Dictionary<int, int>>();
foreach (var bundleCombination in bundleCombinations)
{
var tempBundleSize = new Dictionary<int, int>();
bundleCombination.ForEach(e => tempBundleSize.Add(e, 0));
var results = Bundle(bundleCombination, quantity);
if (results == null)
continue;
foreach (var result in results)
{
tempBundleSize[result.Key] = result.Value;
}
tempBundleSizes.Add(tempBundleSize);
}
var longest = tempBundleSizes.OrderByDescending(x => x.Count).FirstOrDefault();
return longest;
}
static List<List<int>> GetCombination(List<int> list)
{
var retValue = new List<List<int>>();
var count = Math.Pow(2, list.Count);
for (var i = 1; i <= count - 1; i++)
{
var retList = new List<int>();
var str = Convert.ToString(i, 2).PadLeft(list.Count, '0');
for (var j = 0; j < str.Length; j++)
{
if (str[j] == '1')
{
retList.Add(list[j]);
}
}
retValue.Add(retList);
}
return retValue;
}
static Dictionary<int, int> Bundle(List<int> bundleSizes, int quantity)
{
var bundleQuantities = new Dictionary<int, int>();
bundleSizes.ForEach(delegate(int k)
{
if (k <= quantity)
bundleQuantities.Add(k, 0);
});
return bundleQuantities.Count == 0 ? null : RecurseBundles(quantity, bundleQuantities.Keys.ToList(), bundleQuantities);
}
static Dictionary<int, int> RecurseBundles(int quantity, List<int> bundleSizes, Dictionary<int, int> bundleQuantities)
{
var bundleSize = bundleSizes.First();
var bundles = (int)Math.Floor((double)quantity / bundleSize);
var remainingQuantity = quantity % bundleSize;
bundleQuantities[bundleSize] = bundles;
var remainingBundles = bundleSizes.Skip(1).ToList();
if (!remainingBundles.Any() && remainingQuantity > 0)
bundleQuantities = null;
if (remainingBundles.Any())
bundleQuantities = RecurseBundles(remainingQuantity, remainingBundles, bundleQuantities);
return bundleQuantities;
}
}
Here is the output:
Bundle Sizes: 46,25,12,4,3
Quantity: 30
Returned Value: 46:0,25:0,12:2,4:0,3:2,
Bundle Sizes: 46,25,12,4,3
Quantity: 17
Returned Value: null
Bundle Sizes: 46,25,12,4,3
Quantity: 47
Returned Value: 46:0,25:0,12:3,4:2,3:1,
Bundle Sizes: 46,25,12,4,3
Quantity: 5
Returned Value: null
Special thanks to these fantastic SO answers:
All Possible Combinations of a list of Values
How do I combine the keys and values of a Dictionary into one List using LINQ?
Find max count of a list of custom types
EDIT: Made a small change for a better formatted output in the Output method
Worked more on the solution and thanks to a co-worker and paqogomez's answer we got the correct code that works for all my examples and more! My co-worker showed me that you can use a stack in place of recursion and use the stack to keep track of the index that looks to the left and right of the bundle list. This code uses a Greedy Brute-force solution, if there is a faster way I will be interested!
C# Code:
class Program
{
public static void Main()
{
List<int> bundleSizes = new List<int> { 46, 25, 12, 4, 3 };
int quantity = 30;
Dictionary<int, int> bundleResults = StackThis(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 17;
bundleResults = StackThis(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 47;
bundleResults = StackThis(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
quantity = 5;
bundleResults = StackThis(bundleSizes, quantity);
Output(bundleSizes, quantity, bundleResults);
Console.Read();
}
// Reused paqogomez output
static void Output(List<int> bundleSizes, int quantity, Dictionary<int, int> bundleResults)
{
var fullResults = new Dictionary<int, int>();
bundleSizes.ForEach(
delegate(int e)
{
var result = 0;
if (bundleResults != null)
bundleResults.TryGetValue(e, out result);
fullResults.Add(e, result);
});
Console.WriteLine("Bundle Sizes: {0}", string.Join(",", bundleSizes));
Console.WriteLine("Quantity: {0}", quantity);
Console.WriteLine("Returned Value: {0}", bundleResults == null ? "null" : fullResults.Aggregate("", (keyString, pair) => keyString + pair.Key + ":" + pair.Value + ","));
}
static private Dictionary<int, int> StackThis(List<int> usableBundle, int currentQuantity)
{
// Order the list from largest bundle size to smallest size
List<int> arrUsableBundles = usableBundle.OrderByDescending(x => x).ToList();
// Key: Bundles | Value: Quantity
Dictionary<int, int> hmBundleToQuantity = new Dictionary<int, int>();
// Create the hashmap table structure
foreach (int Bundle in arrUsableBundles)
{
hmBundleToQuantity.Add(Bundle, 0);
}
// Keep track of our index of the bundles we need to figure out
Stack<int> stBundleIndex = new Stack<int>();
// Used to calculate the left and right of bundle list
int ixCursor;
// Our remaining quantity after calculations
int iRemaining;
/*
This will act as the midpoint of the bundle list
Will update the right of the cursor, decrement the
cursor, don’t touch the left, and since the loop
hasn’t started we’ll consider everything updatable
and on the right of the cursor
*/
stBundleIndex.Push(-1);
// Keep working till there is no more ways to solve the solution
while (stBundleIndex.Count > 0)
{
// The current cursor is always removed and needs to
// be added back if we want to check it again
ixCursor = stBundleIndex.Pop();
iRemaining = currentQuantity;
for (int ixBundle = 0; ixBundle < usableBundle.Count; ++ixBundle)
{
//Left of current scope, values remain the same
if (ixBundle < ixCursor)
{
iRemaining -= (hmBundleToQuantity[usableBundle[ixBundle]] * usableBundle[ixBundle]);
}
//At the cursor stack scope – decrement current quantity
if (ixBundle == ixCursor)
{
--hmBundleToQuantity[usableBundle[ixBundle]];
iRemaining -= (hmBundleToQuantity[usableBundle[ixBundle]] * usableBundle[ixBundle]);
}
//Right of current scope gets calculated
if (ixBundle > ixCursor)
{
hmBundleToQuantity[usableBundle[ixBundle]] += iRemaining / usableBundle[ixBundle];
iRemaining = iRemaining % usableBundle[ixBundle];
}
}
if (iRemaining == 0) return hmBundleToQuantity;
// Otherwise… We have nothing left on the stack we’ll check
// back to the beginning for non-zero values
int iNonEmptyStart = 0;
//Keep the current scope on the stack if the quantity is still bigger than zero
if (ixCursor >= 0 && hmBundleToQuantity[usableBundle[ixCursor]] > 0)
{
stBundleIndex.Push(ixCursor);
// Maximum cursor on the stack
// (this way we don’t decrement further back than we want)
iNonEmptyStart = stBundleIndex.Max();
}
//Add last non-empty value to the stack to decrement and recalculate from there
for (int ixNonEmpty = usableBundle.Count - 1; ixNonEmpty >= iNonEmptyStart; ixNonEmpty--)
{
if (hmBundleToQuantity[usableBundle[ixNonEmpty]] > 0)
{
stBundleIndex.Push(ixNonEmpty);
break;
}
}
}
return null;
}
}
Once again thanks for all the help on this and special thanks to my co-worker and paqogomez on some help to the solution!

Categories