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!
Related
I'm trying to write knapsack c# algorithm with given conditions but there's always two problems i encountering. I'm getting "Index was outside the bounds of the array" error or my result is just 0.
I found couple code examples of Knapsack implementation and just can't figure out what I'm doing wrong.
Code examples:
https://www.programmingalgorithms.com/algorithm/knapsack-problem
http://www.csharpstar.com/csharp-knapsack-problem/
My code:
static int Knapsack(int n, int w, int[] s, int[] v)
{
int[,] G = new int[n+1,w+1];
for (int k = 0; k <= n; k++)
{
for (int r = 0; r < w; r++)
{
if (r == 0 || k == 0)
G[k, r] = 0;
else if (s[k] <= r)
G[k, r] = Math.Max(G[k- 1, r], v[k] + G[k - 1, r - s[k]]);
else
G[k, r] = G[k - 1, r];
}
}
return G[n, w];
}
static void Main(string[] args)
{
int[] s = { 60, 100, 120};
int[] v = { 10, 20, 30 };
int w = 50;
int n = s.Length;
Console.WriteLine(Knapsack(n, w, s, v));
}
In this case my result is 0.
The issue with your code is that s is the weights and v is the values and your weights of 60, 100, and 120 will obvious not fit into a capacity of 50, that's why you get a result of 0. The example you pull those values from set's the 60, 100, and 120 as the values and 10, 20, and 30 as the weights which is why it gets a result of 220.
I think this works better if you create a class to handle the related weight and value for the items.
public class Item
{
public int Weight { get; set; }
public int Value { get; set; }
}
Then the method only needs an array of items and the desired capacity. Also using meaningful names can make understanding what's going on easier than a bunch of single letter names.
public static int KnapSack(Item[] items, int capacity)
{
int[,] matrix = new int[items.Length + 1, capacity + 1];
for (int itemIndex = 0; itemIndex <= items.Length; itemIndex++)
{
// This adjusts the itemIndex to be 1 based instead of 0 based
// and in this case 0 is the initial state before an item is
// considered for the knapsack.
var currentItem = itemIndex == 0 ? null : items[itemIndex - 1];
for (int currentCapacity = 0; currentCapacity <= capacity; currentCapacity++)
{
// Set the first row and column of the matrix to all zeros
// This is the state before any items are added and when the
// potential capacity is zero the value would also be zero.
if (currentItem == null || currentCapacity == 0)
{
matrix[itemIndex, currentCapacity] = 0;
}
// If the current items weight is less than the current capacity
// then we should see if adding this item to the knapsack
// results in a greater value than what was determined for
// the previous item at this potential capacity.
else if (currentItem.Weight <= currentCapacity)
{
matrix[itemIndex, currentCapacity] = Math.Max(
currentItem.Value
+ matrix[itemIndex - 1, currentCapacity - currentItem.Weight],
matrix[itemIndex - 1, currentCapacity]);
}
// current item will not fit so just set the value to the
// what it was after handling the previous item.
else
{
matrix[itemIndex, currentCapacity] =
matrix[itemIndex - 1, currentCapacity];
}
}
}
// The solution should be the value determined after considering all
// items at all the intermediate potential capacities.
return matrix[items.Length, capacity];
}
Then running this code
var items = new[]
{
new Item {Value = 60, Weight = 10},
new Item {Value = 100, Weight = 20},
new Item {Value = 120, Weight = 30},
};
Console.WriteLine(KnapSack(items, 50));
results in 220.
Here's a solution that uses recursion.
public static int KnapSackRecursive(Item[] items, int capacity)
{
// If there are no items or capacity is 0 then return 0
if (items.Length == 0 || capacity == 0) return 0;
// If there is one item and it fits then return it's value
// otherwise return 0
if (items.Length == 1)
{
return items[0].Weight < capacity ? items[0].Value : 0;
}
// keep track of the best value seen.
int best = 0;
for (int i = 0; i < items.Length; i++)
{
// This is an array of the other items.
var otherItems = items.Take(i).Concat(items.Skip(i + 1)).ToArray();
// Calculate the best value without using the current item.
int without = KnapSackRecursive(otherItems, capacity);
int with = 0;
// If the current item fits then calculate the best value for
// a capacity less it's weight and with it removed from contention
// and add the current items value to that.
if (items[i].Weight <= capacity)
{
with = KnapSackRecursive(otherItems, capacity - items[i].Weight)
+ items[i].Value;
}
// The current best is the max of the with or without.
int currentBest = Math.Max(without, with);
// determine if the current best is the overall best.
if (currentBest > best)
best = currentBest;
}
return best;
}
I am struggling to get my head around this but basically trying to create a list of batches of for example 1-200, 201-400, 401-53 from a number value of 453.
So far I am able to split the number 453 into a list of 200,200,53 which basically tells me I need to create 3 batches.
However, am not sure on how to create the Batches?
Here is my code so far of creating the stacks
public List<int> CreateStacks(Int32 number, Int32 stackSize)
{
List<int> stacks = new List<int>();
int d = number / stackSize;
if (d == 0)
{
return stacks;
}
int r = number % stackSize;
string str = string.Join(",", Enumerable.Repeat("200", d));
if (number % 100 > 0)
{
str = str + ", " + r;
foreach(var s in str.Split(',').ToList())
{
stacks.Add(Convert.ToInt32(s));
}
}
return stacks;
}
Any suggestions guys?
I created a second method called CreateRanges() which produces the output you provided, with the exception of all ranges starting from one. Each range should start with zero or one; in your example the first batch would actually have 201 items.
// Stacks: 200, 200, 53
// Ranges: <1,200>, <201,400>, <401,453>
void Main()
{
var stacks = CreateStacks(453, 200);
Console.WriteLine("Stacks: " + string.Join(", ", stacks.Select(n => n.ToString()).ToArray()));
var ranges = CreateRanges(453, stacks);
Console.WriteLine("Ranges: " + string.Join(", ", ranges.Select(t => $"<{t.Item1},{t.Item2}>").ToArray()));
Console.WriteLine();
}
public static List<Tuple<int, int>> CreateRanges(Int32 number, List<int> stacks)
{
var ranges = new List<Tuple<int, int>>();
var index = 0;
foreach (var stack in stacks){
ranges.Add(new Tuple<int, int>(index + 1, stack + index));
index += stack;
}
return ranges;
}
public static List<int> CreateStacks(Int32 number, Int32 stackSize)
{
List<int> stacks = new List<int>();
int d = number / stackSize;
if (d == 0)
{
return stacks;
}
int r = number % stackSize;
string str = string.Join(",", Enumerable.Repeat("200", d));
if (number % 100 > 0)
{
str = str + ", " + r;
foreach (var s in str.Split(',').ToList())
{
stacks.Add(Convert.ToInt32(s));
}
}
return stacks;
}
You can use a for-loop and determine if the count has reached 200 and then create a new List.
int total = 453;
int batchCount = 200;
List<List<int>> batches = new List<List<int>>();
for (int i = 0; i < total; i++)
{
if (i % batchCount == 0)
{
batches.Add(new List<int>());
}
batches[batches.Count -1].Add(i);
}
The List batches will contain 3 Lists of int with all the numbers.
My shot, taking "create a list of batches" literally:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var batches = Batchify( 453, 200);
Console.WriteLine(batches.Count);
foreach( Batch batch in batches )
{
Console.WriteLine("{0}-{1}", batch.Start, batch.End);
}
}
public static List<Batch> Batchify( int number, int size)
{
List<Batch> result = new List<Batch>();
int running = number;
int startIndex = 0;
do
{
int endIndex = running < size ? startIndex+running : startIndex+size-1;
result.Add(new Batch{ Start = startIndex, End = endIndex });
startIndex = endIndex+1;
running -= size;
}while(running > 0);
return result;
}
}
public class Batch
{
public int Start{get;set;}
public int End{get;set;}
}
In action: https://dotnetfiddle.net/oqFmv0
I have a SortedList with long datatype elements where the elements
may repeat ( has some duplicate records too).
Eg: ValueCov = { 1,2,2,2,5,5,5,5,6,6,7,7,7,...........}
I have to search a value "x" each time in this list with the option
BinarySearch(x) in C# and when the value "x" is not there in the
ValueCov list, I need to find the next closer value's index. (least
diff from value "x" ).
The issue I have here is, as duplicate elements available if I search
for value x=3, This should return value=2's index from valueCov (
Index=1) . But the second time I search for value=3 this shouldn't
return the previously returned index because I'm fixing that
position for some other work. So I need the index as = 2 when I search
the second time.
Please provide a solution for this search while duplicates available in a list.
for (int i = 0; i < valueWat.Count; i++)
{
var x=valueWat[i];
int index = valueCov.BinarySearch(x);
//Console.Write(index + ",");
if (index >= 0)
{
//Console.Write(sortedCov[index].Key + ",");
Console.WriteLine("addr of w/m" + sortedWat[i].Key);
//adding addr of w/m and coveraddr
blockedWaterCoverMap.Add(sortedCov[index].Key, sortedWat[i].Key);
//valueCov.Remove(x);
//valueCov[index] = 10000000000;
valueCov[index] = -100000000;
// valueWat[i] = 20000000000;
//foreach (var z in blockedWaterCoverMap)
//{
// Console.WriteLine("cov,wat indexpair" + z.Key+z.Value);
//}
}
else
{
int ind = findClosest(x);
Console.WriteLine("index,value#index\t" + ind + "\t" + valueCov[ind]);
blockedWaterCoverMap.Add(sortedCov[ind].Key, sortedWat[i].Key);
valueCov[ind] = 00000; valueCov[index] = 00000;
}
}
/////////
private int findClosest(long data)
{
int i = 0; // index of currently checked element from valueCov
int ind =i; // returned index of the closest element
// current lowest distance to searched value:
long min = long.MaxValue;
// currently counted difference between input value
// and the next element of the list valueCov
long diff = 0;
var valueCov = new List<long>();
foreach (var y in sortedCov)
{
valueCov.Add(y.Value);
}
for ( i = 0; i < valueCov.Count; i++)
{
var x=valueCov[i];
if ((diff = Math.Abs(x - data)) < min)
{
min = diff;
ind = i; // the searched index is updated
//Console.WriteLine("findclosest i\t" + i);
}
}
// random selection of the index from the closest
// found values
List<int> indices = new List<int>();
for (int n = 0; n < valueCov.Count; n++)
{
if (valueCov[n] == valueCov[ind])
indices.Add(n);
}
Random r = new Random();
ind = indices[r.Next(indices.Count)];
return ind;
}
I would just use linq to get the value, and you can later get the index by BinarySearch
public static void Main()
{
var valueCov = new long[]{ 1,2,2,2,5,5,5,5,6,6,7,7,7};
var result = 0L;
var x = 3;
result = FindNextCloseset(valueCov, x, result);
Console.WriteLine(result);
result = FindNextCloseset(valueCov, x, result);
Console.WriteLine(result);
}
public static long FindNextCloseset(long[] values, int occurrence, long searchAfterValue)
{
return values
.Where(i => i > searchAfterValue)
.GroupBy(i => i)
.Where(i => i.Count() == occurrence)
.Select(i => i.Key)
.FirstOrDefault();
}
Fiddle
Here is a solution that I came up with that you can start with. It is long, and you should create unit tests and refactor it since it contains a lot of logic:
public static int FindIndexOfClosest(List<long> list, long value_to_search_for, int time)
{
int index = list.BinarySearch(value_to_search_for);
if (index >= 0) //We found the value
{
while (index > 0 && list[index - 1] == value_to_search_for)
//BinarySearch might not return the index of the first occurance
{
index--;
}
index += time;
if (index >= list.Count || list[index] != value_to_search_for)
return -1;
return index;
}
else
{
int location_for_next_larger_number = ~index; //This could be equal to Count
int? larger_index = location_for_next_larger_number == list.Count
? (int?) null
: location_for_next_larger_number;
int? smaller_index = null;
if (!larger_index.HasValue)
{
if (list.Count > 0)
smaller_index = list.Count - 1;
}
else
{
int i = location_for_next_larger_number;
while (i > 0 && list[i - 1] == larger_index.Value)
i--;
if (i > 0)
smaller_index = i - 1;
}
int? closer_number_index = null;
if (larger_index.HasValue)
closer_number_index = larger_index.Value;
if (smaller_index.HasValue)
{
if (!closer_number_index.HasValue)
closer_number_index = smaller_index.Value;
else
{
if (Math.Abs(list[smaller_index.Value] - value_to_search_for) < Math.Abs(list[closer_number_index.Value] - value_to_search_for))
closer_number_index = smaller_index.Value;
}
}
if (closer_number_index.HasValue)
{
while (closer_number_index > 0 && list[closer_number_index.Value - 1] == list[closer_number_index.Value])
closer_number_index = closer_number_index.Value - 1;
long closer_number_value = list[closer_number_index.Value];
closer_number_index += time;
if (closer_number_index.Value >= list.Count || list[closer_number_index.Value] != closer_number_value)
return -1;
return closer_number_index.Value;
}
return -1;
}
}
And you can test it like this:
static void Main(string[] args)
{
List<long> list = new List<long> {1, 2, 2, 2, 5, 5, 5, 5, 6, 6, 7, 7, 7};
var test1 = FindIndexOfClosest(list, 3, 0); // returns 1
var test2 = FindIndexOfClosest(list, 3, 1); // returns 2
var test3 = FindIndexOfClosest(list, 3, 2); // returns 3
var test4 = FindIndexOfClosest(list, 3, 3); // returns -1 (not found)
}
Make sure that the list that you give to the FindIndexOfClosest method is always sorted.
Take a look at the reference for the BinarySearch method as it will help you understand the code I provided.
So here is what I'm trying to do. I have a list of integers:
List<int> myList = new List<int>() {5,7,12,8,7};
And I also got a target:
int target = 20;
What I'm trying to do is find a way to create a new list of integer that when added together equal my target. So if my target is 20, I need a list like this:
{ 12, 8 }
If my target is 26, then I'll have this:
{ 7, 12, 7 }
Each number can only be used one time (7 is used twice because its in the list twice). If there is no solution, it should return an empty list. Anyone have any idea how I can do something like this?
That's a statistical problem. You want to find all possible combinations with a matching sum. I can recommend this project which is also worth reading:
http://www.codeproject.com/Articles/26050/Permutations-Combinations-and-Variations-using-C-G
Then it's easy and efficient:
List<int> myList = new List<int>() { 5, 7, 12, 8, 7 };
var allMatchingCombos = new List<IList<int>>();
for (int lowerIndex = 1; lowerIndex < myList.Count; lowerIndex++)
{
IEnumerable<IList<int>> matchingCombos = new Combinations<int>(myList, lowerIndex, GenerateOption.WithoutRepetition)
.Where(c => c.Sum() == 20);
allMatchingCombos.AddRange(matchingCombos);
}
foreach(var matchingCombo in allMatchingCombos)
Console.WriteLine(string.Join(",", matchingCombo));
Output:
12,8
5,7,8
5,8,7
Using recursion. Note that this solution will favor solutions that can be acquired by using the first items from the list. So for a target of 20, you won’t get 12+8 as the solution but 5+7+8.
List<int> findSum(List<int> numbers, int target, int index = 0)
{
// loop through all numbers starting from the index
for (var i = index; i < numbers.Count; i++)
{
int remainder = target - numbers[i];
// if the current number is too big for the target, skip
if (remainder < 0)
continue;
// if the current number is a solution, return a list with it
else if (remainder == 0)
return new List<int>() { numbers[i] };
else
{
// otherwise try to find a sum for the remainder later in the list
var s = findSum(numbers, remainder, i + 1);
// if no number was returned, we could’t find a solution, so skip
if (s.Count == 0)
continue;
// otherwise we found a solution, so add our current number to it
// and return the result
s.Insert(0, numbers[i]);
return s;
}
}
// if we end up here, we checked all the numbers in the list and
// found no solution
return new List<int>();
}
void Main()
{
List<int> myList = new List<int>() { 5, 7, 12, 8, 7 };
Console.WriteLine(findSum(myList, 12)); // 5, 7
Console.WriteLine(findSum(myList, 20)); // 5, 7, 8
Console.WriteLine(findSum(myList, 31)); // 5, 7, 12, 7
}
You can find all the solutions given below here: https://github.com/Mr-Zoidberg/Find-Possible-Combinations
1. Using recursion
static void Main(string[] args)
{
const int target = 20;
var numbers = new List<int> { 1, 2, 5, 8, 12, 14, 9 };
Console.WriteLine($"Number of Combinations: {SumCounter(numbers, target)}");
Console.ReadKey();
}
private static int SumCounter(IReadOnlyList<int> numbers, int target)
{
var result = 0;
RecursiveCounter(numbers, target, new List<int>(), ref result);
return result;
}
private static void RecursiveCounter(IReadOnlyList<int> numbers, int target, List<int> partial, ref int result)
{
var sum = partial.Sum();
if (sum == target)
{
result++;
Console.WriteLine(string.Join(" + ", partial.ToArray()) + " = " + target);
}
if (sum >= target) return;
for (var i = 0; i < numbers.Count; i++)
{
var remaining = new List<int>();
for (var j = i + 1; j < numbers.Count; j++) remaining.Add(numbers[j]);
var partRec = new List<int>(partial) { numbers[i] };
RecursiveCounter(remaining, target, partRec, ref result);
}
}
2. Get subsets using Linq
static void Main(string[] args)
{
const int target = 20;
var numbers = new int[] { 1, 2, 5, 8, 12, 14, 9 };
var matches = numbers.GetSubsets().Where(s => s.Sum() == target).ToArray();
foreach (var match in matches) Console.WriteLine(match.Select(m => m.ToString()).Aggregate((a, n) => $"{a} + {n}") + $" = {target}");
Console.WriteLine($"Number of Combinations: {matches.Length}");
Console.ReadKey();
}
}
public static class Extension
{
public static IEnumerable<IEnumerable<T>> GetSubsets<T>(this IEnumerable<T> collection)
{
if (!collection.Any()) return Enumerable.Repeat(Enumerable.Empty<T>(), 1);
var element = collection.Take(1);
var ignoreFirstSubsets = GetSubsets(collection.Skip(1));
var subsets = ignoreFirstSubsets.Select(set => element.Concat(set));
return subsets.Concat(ignoreFirstSubsets);
}
3. Another recursive method...
static void Main(string[] args)
{
const int target = 20;
var numbers = new [] { 1, 2, 5, 8, 12, 14, 9 };
var result = GetSubsets(numbers, target, "");
foreach (var subset in result) Console.WriteLine($"{subset} = {target}");
Console.WriteLine($"Number of Combinations: {result.Count()}");
Console.ReadLine();
}
public static IEnumerable<string> GetSubsets(int[] set, int sum, string values)
{
for (var i = 0; i < set.Length; i++)
{
var left = sum - set[i];
var vals = values != "" ? $"{set[i]} + {values}" : $"{set[i]}";
if (left == 0) yield return vals;
else
{
int[] possible = set.Take(i).Where(n => n <= sum).ToArray();
if (possible.Length <= 0) continue;
foreach (string s in GetSubsets(possible, left, vals)) yield return s;
}
}
}
4. Using binary search. Linear time.
static void Main(string[] args)
{
const int target = 20;
var numbers = new [] { 1, 2, 5, 8, 12, 14, 9 };
var subsets = GetSubsets(numbers, target);
foreach (var s in subsets) Console.WriteLine($"{s} = {target}");
Console.WriteLine($"Number of Combinations: {subsets.Count()}");
Console.ReadKey();
}
private static IEnumerable<string> GetSubsets(int[] array, int target)
{
Array.Sort((Array)array);
List<string> result = new List<string>();
for (int i = array.Length-1; i >= 0; i--)
{
var eq = $"{array[i]}";
var sum = array[i];
var toAdd = 0;
while (sum != target)
{
var mid = Array.BinarySearch(array, 0, sum <= target / 2 && sum != array[i] ? array.Length - 1 : i, target - sum);
mid = mid < 0 ? ~mid - 1 : mid;
if (mid == i || mid < 0 || toAdd == array[mid] ) break;
toAdd = array[mid];
sum += array[mid];
eq += $" + {array[mid]}";
if (sum == target) result.Add(eq);
}
}
return result;
}
This implementation of mine in C# will return all combinations(including repetitive use of same number) that equals a given number.
string r;
List<int> l = new List<int>();
void u(int w, int s, int K, int[] A) {
// If a unique combination is found
if (s == K) {
r += "(" + String.Join(" ", l) + ")";
return;
}
// For all other combinations
for (int i=w; i<A.Length; i++){
// Check if the sum exceeds K
int x = A[i];
if (s + x <= K){
l.Add(x);
u(i, s + x, K,A);
l.Remove(x);
}
}
}
string combinationSum(int[] a, int s) {
r = "";
u(0, 0, s, a.Distinct().OrderBy(x=>x).ToArray());
return r.Any() ? r : "Empty";
}
for a: [2, 3, 5, 9] and s = 9
the result will be :
"(2 2 2 3)(2 2 5)(3 3 3)(9)"
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;
}
}